ng-content @ContentChild @ContentChildren学习

参考博客:https://segmentfault.com/a/1190000008707828

我们在写HTML时经常使用到标签嵌套,比如:

<div>
    <p>嵌套</p>
</div>

在angular自定义标签里面能否实现标签的嵌套呢?答案是肯定的.

/* parent.component.ts */
import { Component } from '@angular/core';

@Component({
    selector: 'exe-greet',
    template: `
    <div class="border">
        <p>Greet Component</p>  
        <ng-content></ng-content>
    </div>
    `,
    styles: [` .border { border: 2px solid #eee; } `]
})
export class GreetComponent { }
/* parent.component.ts */
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Welcome to Angular World</h4>
    <exe-greet>
      <p>Hello Semlinker</p>
    </exe-greet>
  `,
})
export class AppComponent { }

效果如下:
image.png
在页面里即有child组件的内容,也有parent组件child子组件嵌套的内容,也就是说parent组件child子组件嵌套的内容
映射到了 ng-content 标签上了,将代码稍微改一下:

<exe-greet>
    <p>Hello Semlinker</p>
    <p>Hello Bob</p>
</exe-greet>

页面上的效果就变成这样了
image.png

如果需要下面的效果
image.png
可以写成这样

/*parent.component.ts*/
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Welcome to Angular World</h4>
    <exe-greet>
      <div style="border: 1px solid #666; margin: 4px;">
         <div style="border: 1px solid red; margin: 5px;">Card Header</div>
         <div style="border: 1px solid green; margin: 5px;">Card Body</div>
         <div style="border: 1px solid blue; margin: 5px;">Card Footer</div>
      </div>
    </exe-greet>
  `,
})
export class AppComponent { }

虽然效果实现了,假设在另一个页面,也需要使用GreetComponent组件,那么还需要再次设置预设的样式.我们能不能把卡片风格的各个部分的默认样式定义在GreetComponent组件中,使用组件时我们只需关心自定内容区域的样式,当然也可以,调整后的代码

/*greet.component.ts*/
import { Component } from '@angular/core';

@Component({
    selector: 'exe-greet',
    template: `
    <div class="border">
        <p>Greet Component</p>  
        <div style="border: 1px solid #666;margin: 4px;">
            <div style="border: 1px solid red;margin: 5px;">
                <ng-content></ng-content>
            </div>
            <div style="border: 1px solid green;margin: 5px;">
                <ng-content></ng-content>
            </div>
            <div style="border: 1px solid blue;margin: 5px;">
                 <ng-content></ng-content>
            </div>
        </div>
    </div>
    `,
    styles: [` .border { border: 2px solid #eee; } `]
})
export class GreetComponent{ }

现在遇到的问题是怎样将父组件映射的内容与 ng-content 进行一一对应,好在 ng-content 支持 select 属性,它支持我们常用的选择器类型,如:

  • 标签选择器、
  • 类选择器、
  • ID选择器、
/*greet.component.ts*/
<div style="border: 1px solid #666;margin: 4px;">
     <div style="border: 1px solid red;margin: 5px;">
         <ng-content select="header"></ng-content>
     </div>
     <div style="border: 1px solid green;margin: 5px;">
         <ng-content select=".card_body"></ng-content>
     </div>
     <div style="border: 1px solid blue;margin: 5px;">
         <ng-content select="footer"></ng-content>
     </div>
</div>
/*parent.component.ts*/
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Welcome to Angular World</h4>
    <exe-greet>
      <header>Card Header</header>
          <div class="card_body">Card Body</div>
      <footer>Card Footer</footer>
    </exe-greet>
  `,
})
export class AppComponent { }

ContentChild
以前面的例子为例,我们在 AppComponent 组件中定义的内容,通过 ng-content 指令提供的选择器,成功投射到 GreetComponent 组件中。

但是我们只能让greet的元素显示,而不能获取到class中的属性,我想要class中的属性该怎么办呢?

了解完 Content Projection,接下来我们来介绍 ContentChild。

ContentChild 是属性装饰器,用来从通过 Content Projection 方式设置的视图中获取匹配的元素。

我们需要借助 @ContentChild 装饰器来处理

/*child.component.ts*/
import { Component } from '@angular/core';

@Component({
    selector: 'exe-child',
    template: `
      <p>Child Component</p>  
    `
})
export class ChildComponent {
    name: string = 'child-component';
}
/*parent.component.ts*/
import { Component, ContentChild, AfterContentInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
    selector: 'exe-parent',
    template: `
      <p>Parent Component</p>  
      <ng-content></ng-content>
    `
})
export class ParentComponent implements AfterContentInit {
    @ContentChild(ChildComponent)
    childCmp: ChildComponent;

    ngAfterContentInit() {
        console.dir(this.childCmp);
    }
}
/*app.component.ts*/
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Welcome to Angular World</h4>
    <exe-parent>
      <exe-child></exe-child>
    </exe-parent>
  `,
})
export class AppComponent { }

ContentChildren 属性装饰器用来从通过 Content Projection 方式设置的视图中获取匹配的多个元素,返回的结果是一个 QueryList 集合。

/*parent.component.ts*/
import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
    selector: 'exe-parent',
    template: `
      <p>Parent Component</p>  
      <ng-content></ng-content>
    `
})
export class ParentComponent implements AfterContentInit {

    @ContentChildren(ChildComponent)
    childCmps: QueryList<ChildComponent>;

    ngAfterContentInit() {
        console.dir(this.childCmps);
    }
}
/*app.component.ts*/
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Welcome to Angular World</h4>
    <exe-parent>
      <exe-child></exe-child>
      <exe-child></exe-child>
    </exe-parent>
  `,
})
export class AppComponent { }

猜你喜欢

转载自blog.csdn.net/coding_1/article/details/79405223