Angular 2 ContentChild & ContentChildren

Time:2020-3-25

In the previous article, we have introduced the viewchild & viewchildren property decorator of angular 2. Now we will introduce their brothers, contentchild and contentchildren property decorator. I want to introduce our topic today through a simple requirement. The specific requirements are as follows:

Angular 2 ContentChild & ContentChildren

Users familiar with angular 1. X should knowng-transcludeInstruction, through which we can easily achieve the above functions. And in angular 2, newng-contentOrder, let’s try it right away.

greet.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 { }

app.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 { }

After the above code is run, the output of the browser is:

Angular 2 ContentChild & ContentChildren

Is it too simple? Let’s upgrade the requirement that our greetcomponent component supports user-defined greeting content in card style, as shown in the following figure:

Angular 2 ContentChild & ContentChildren

This function is also very simple. Let’s take a look at the code immediately:

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 { }

After the above code is run, the output of the browser is:

Angular 2 ContentChild & ContentChildren

The function is realized, but is there any problem? Assuming that you also need to use the green component component on another page, you need to set the default style again. Can we define the default style of each part of the card style in the greetcomponent component? When using the component, we only need to care about the style of the custom content area. The answer is yes. The adjusted code is as follows:

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{ }

The greetcomponent component has been adjusted. Now, the remaining problem is how to dynamically extract the contents of each part from the parent component. Fortunately,ng-contentCommand supportselectProperty, which allows us to set the extracted content. What’s more, it supports our common selector types, such as tag selector, class selector, ID selector, property selector, etc.

greet.component.ts – template

<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>

app.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 { }

After the above code runs, we can see the expected results on the browsing. Next, our protagonist, contentchild, appears.

ContentChild

Before we formally introduce the contentchild property decorator, let’s take a look at content projection.

Angular 2 ContentChild & ContentChildren

In the previous example, we haveAppComponentContent defined in components, viang-contentThe selector provided by the instruction was successfully projected into the greetcomponent component. After learning about content projection, let’s introduce contentchild.

Contentchild is an attribute decorator, which is used to get matching elements from views set through content projection.

@Contentchild example

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 { }

After the above code is run, the output of the console is as follows:

Angular 2 ContentChild & ContentChildren

ContentChildren

The contentchildren property decorator is used to get multiple matching elements from the view set by content projection. The returned result is a querylist collection.

@Contentchildren example

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 { }

After the above code is run, the output of the console is as follows:

Angular 2 ContentChild & ContentChildren

Contentchild interface and decorator

Contentchilddecorator interface

export interface ContentChildDecorator {
  //Type: @ contentchild (childcomponent)
  (selector: Type<any>|Function|string, {read}?: {read?: any}): any;

  new (selector: Type<any>|Function|string, {read}?: {read?: any}): ContentChild;
}

Contentchilddecorator decorator

export const ContentChild: ContentChildDecorator = makePropDecorator(
    'ContentChild',
    [
      ['selector', undefined], {
        first: true,
        Isviewquery: false, // true when viewchild or viewchildren decorator
        descendants: true,
        read: undefined,
      }
    ],
Query);

I have something to say.

The definition of contentchildren and viewchildren

  • In the host element<opening>and</closing>Called content children in Tags

  • The content defined in the component’s template, which is part of the component, is called view children

Similarities and differences between contentchild and viewchild

Same points

  • All attribute decorators

  • There are corresponding plural decorators: contentchildren, viewchildren

  • All support selector of type < any > | function| string type

Difference

  • Contentchild is used to get matching elements from views set through content projection (ng content)

  • Viewchild is used to get matching elements from the template view

  • Only in the ngaftercontentinit lifecycle hook of the parent component can the elements queried by contentchild be successfully obtained

  • Only in the ngafterviewinit lifecycle hook of the parent component can the elements queried by viewchild be successfully obtained

Cannot use ‘ng content’ in root component

Angular 2 ContentChild & ContentChildren

The main reasons are as follows:

  • <my-app></my-app>The information between tags is used to indicate that angular’s application is starting

  • The angular 2 compiler does not handleindex.htmlThe binding information set in the file. In addition, for security reasons, to avoidindex.htmlMedium{{}}Interpolation is processed by the template engine used by the server.

summary

In this paper, through a simple requirement, we introduce theng-contentInstruction, and then introduced the concept of angular content projection, and finally formally introduced contentchild and contentchildren property decorator. At the end of this paper, we also introduce the similarities and differences between contentchild and viewchild, hoping that this paper can help readers better understand the relevant knowledge points.