Angular 2 ContentChild & ContentChildren

前端之家收集整理的这篇文章主要介绍了Angular 2 ContentChild & ContentChildren前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前面的文章我们已经介绍过了 Angular 2 的 ViewChild & ViewChildren 属性装饰器,现在我们就来介绍一下它们的兄弟 ContentChild 和 ContentChildren 属性装饰器。我想通过一个简单的需求,来引入我们今天的主题。具体需求如下:

熟悉 Angular 1.x 的用户,应该都知道 ng-transclude 指令,通过该指令我们可以非常容易实现上述的功能。而在 Angular 2 中引入新的 ng-content 指令,我们马上动手试一下。

greet.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'exe-greet',template: `
  5. <div class="border">
  6. <p>Greet Component</p>
  7. <ng-content></ng-content>
  8. </div>
  9. `,styles: [` .border { border: 2px solid #eee; } `]
  10. })
  11. export class GreetComponent { }

app.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'my-app',template: `
  5. <h4>Welcome to Angular World</h4>
  6. <exe-greet>
  7. <p>Hello Semlinker</p>
  8. </exe-greet>
  9. `,})
  10. export class AppComponent { }

以上代码运行后,浏览器的输出结果:

是不是感觉太简单了,那我们来升级一下需求,即我们的 GreetComponent 组件要支持用户自定义卡片风格的问候内容,具体如下图所示:

这个功能也很简单啊,我们马上看一下代码

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'my-app',template: `
  5. <h4>Welcome to Angular World</h4>
  6. <exe-greet>
  7. <div style="border: 1px solid #666; margin: 4px;">
  8. <div style="border: 1px solid red; margin: 5px;">Card Header</div>
  9. <div style="border: 1px solid green; margin: 5px;">Card Body</div>
  10. <div style="border: 1px solid blue; margin: 5px;">Card Footer</div>
  11. </div>
  12. </exe-greet>
  13. `,})
  14. export class AppComponent { }

以上代码运行后,浏览器的输出结果:

功能是实现了,但有没有什么问题 ?假设在另一个页面,也需要使用 GreetComponent 组件 ,那么还需要再次设置预设的样式。我们能不能把卡片风格的各个部分默认样式定义在 GreetComponent 组件中,使用组件时我们只需关心自定内容区域的样式,答案是可以的,调整后的代码如下:

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'exe-greet',template: `
  5. <div class="border">
  6. <p>Greet Component</p>
  7. <div style="border: 1px solid #666;margin: 4px;">
  8. <div style="border: 1px solid red;margin: 5px;">
  9. <ng-content></ng-content>
  10. </div>
  11. <div style="border: 1px solid green;margin: 5px;">
  12. <ng-content></ng-content>
  13. </div>
  14. <div style="border: 1px solid blue;margin: 5px;">
  15. <ng-content></ng-content>
  16. </div>
  17. </div>
  18. </div>
  19. `,styles: [` .border { border: 2px solid #eee; } `]
  20. })
  21. export class GreetComponent{ }

GreetComponent 组件已经调整好了,现在剩下的问题就是如何从父级组件动态的抽取各个部分的内容。幸运的是,ng-content 指令支持 select 属性,它允许我们设定抽取的内容,更强大的是它支持我们常用的选择器类型,如标签选择器、类选择器、ID选择器、属性选择器等。

greet.component.ts - template

  1. <div style="border: 1px solid #666;margin: 4px;">
  2. <div style="border: 1px solid red;margin: 5px;">
  3. <ng-content select="header"></ng-content>
  4. </div>
  5. <div style="border: 1px solid green;margin: 5px;">
  6. <ng-content select=".card_body"></ng-content>
  7. </div>
  8. <div style="border: 1px solid blue;margin: 5px;">
  9. <ng-content select="footer"></ng-content>
  10. </div>
  11. </div>

app.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'my-app',template: `
  5. <h4>Welcome to Angular World</h4>
  6. <exe-greet>
  7. <header>Card Header</header>
  8. <div class="card_body">Card Body</div>
  9. <footer>Card Footer</footer>
  10. </exe-greet>
  11. `,})
  12. export class AppComponent { }

以上代码运行后,在浏览上我们可以看到预期的结果,接下来该我们的主角 - ContentChild 出场了。

ContentChild

在正式介绍 ContentChild 属性装饰器前,我们要先了解一下 Content Projection (内容投影)。

以前面的例子为例,我们在 AppComponent 组件中定义的内容,通过 ng-content 指令提供的选择器,成功投射到 GreetComponent 组件中。了解完 Content Projection,接下来我们来介绍 ContentChild。

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

@ContentChild 示例

child.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'exe-child',template: `
  5. <p>Child Component</p>
  6. `
  7. })
  8. export class ChildComponent {
  9. name: string = 'child-component';
  10. }

parent.component.ts

  1. import { Component,ContentChild,AfterContentInit } from '@angular/core';
  2. import { ChildComponent } from './child.component';
  3.  
  4. @Component({
  5. selector: 'exe-parent',template: `
  6. <p>Parent Component</p>
  7. <ng-content></ng-content>
  8. `
  9. })
  10. export class ParentComponent implements AfterContentInit {
  11. @ContentChild(ChildComponent)
  12. childCmp: ChildComponent;
  13.  
  14. ngAfterContentInit() {
  15. console.dir(this.childCmp);
  16. }
  17. }

app.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'my-app',template: `
  5. <h4>Welcome to Angular World</h4>
  6. <exe-parent>
  7. <exe-child></exe-child>
  8. </exe-parent>
  9. `,})
  10. export class AppComponent { }

以上代码运行后,控制台的输出结果:

ContentChildren

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

@ContentChildren 示例

parent.component.ts

  1. import { Component,ContentChildren,QueryList,template: `
  2. <p>Parent Component</p>
  3. <ng-content></ng-content>
  4. `
  5. })
  6. export class ParentComponent implements AfterContentInit {
  7. @ContentChildren(ChildComponent)
  8. childCmps: QueryList<ChildComponent>;
  9.  
  10. ngAfterContentInit() {
  11. console.dir(this.childCmps);
  12. }
  13. }

app.component.ts

  1. import { Component } from '@angular/core';
  2.  
  3. @Component({
  4. selector: 'my-app',template: `
  5. <h4>Welcome to Angular World</h4>
  6. <exe-parent>
  7. <exe-child></exe-child>
  8. <exe-child></exe-child>
  9. </exe-parent>
  10. `,})
  11. export class AppComponent { }

以上代码运行后,控制台的输出结果:

ContentChild 接口及装饰器

ContentChildDecorator 接口

  1. export interface ContentChildDecorator {
  2. // Type类型:@ContentChild(ChildComponent)
  3. (selector: Type<any>|Function|string,{read}?: {read?: any}): any;
  4.  
  5. new (selector: Type<any>|Function|string,{read}?: {read?: any}): ContentChild;
  6. }

ContentChildDecorator 装饰器

  1. export const ContentChild: ContentChildDecorator = makePropDecorator(
  2. 'ContentChild',[
  3. ['selector',undefined],{
  4. first: true,isViewQuery: false,// ViewChild或ViewChildren装饰器时为true
  5. descendants: true,read: undefined,}
  6. ],Query);

我有话说

ContentChildren 与 ViewChildren 的定义

  • 在 host 元素 <opening></closing> 标签中被称为 Content Children

  • 在组件的模板中定义的内容,它是组件的一部分,被称为 View Children

ContentChild 与 ViewChild 的异同点

相同点

  • 都是属性装饰器

  • 都有对应的复数形式装饰器:ContentChildren、ViewChildren

  • 支持 Type<any>|Function|string 类型的选择器

不同点

  • ContentChild 用来从通过 Content Projection 方式 (ng-content) 设置的视图中获取匹配的元素

  • ViewChild 用来从模板视图中获取匹配的元素

  • 在父组件的 ngAfterContentInit 生命周期钩子中才能成功获取通过 ContentChild 查询的元素

  • 在父组件的 ngAfterViewInit 生命周期钩子中才能成功获取通过 ViewChild 查询的元素

在 Root Component 中无法使用 `ng-content

原因主要有以下几点:

  • <my-app></my-app> 标签之间的信息是用来表明 Angular 的应用程序正在启动中

  • Angular 2 编译器不会处理 index.html 文件中设置的绑定信息,另外出于安全因素考虑,为了避免 index.html 中的 {{}} 插值,被服务端使用的模板引擎处理。

总结

本文先通过一个简单的需求,引出了 Angular 2 中的 ng-content 指令,进而介绍了 Angular Content Projection (内容投影) 的概念,最后才正式介绍 ContentChild 和 ContentChildren 属性装饰器。另外在文末我们也介绍了 ContentChild 与 ViewChild 的异同点,希望本文能帮助读者更好地理解相关的知识点。

猜你在找的Angularjs相关文章