Utilizing components as standalone tags that are self-contained and wholly manage their contents is a clean pattern, but you will frequently find that your component tags demand that they enclose content.
The code, links, and a live example of this are available at .
Suppose you had the following application:
[app/ad-section.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'ad-section', template: ` <a href="#">{{adText}}</a> ` }) export class AdSectionComponent { adText:string = 'Selfie sticks 40% off!'; } [app/article.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'article', template: ` <h1>{{title}}</h1> <p>U.S. senators are up in arms following the recent ruling stripping them of their beloved selfie sticks.</p> <p>A bipartisan committee drafted a resolution to smuggle selfie sticks onto the floor by any means necessary.</p> ` }) export class ArticleComponent { title:string = 'Selfie Sticks Banned from Senate Floor'; }
Your objective here is to modify this so that the AdSection
component can be incorporated into the Article
component without interfering with its content.
The AdSection
component wants to incorporate an extra element around the existing Article
content. This is easy to accomplish:
[app/article.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'article', template: ` <h1>{{title}}</h1> <ad-section> <p>U.S. senators are up in arms following the recent ruling stripping them of their beloved selfie sticks.</p> <p>A bipartisan committee drafted a resolution to smuggle selfie sticks onto the floor by any means necessary.</p> </ad-section> ` }) export class ArticleComponent { title:string = 'Selfie Sticks Banned from Senate Floor'; }
You will notice though that this is a destructive operation. When rendering AdSectionComponent
, Angular is not concerned about any content that is inside it. It sees that AdSectionComponent
has a template associated with it, and it dutifully supplants the element's contents with it; this template is defined in the @Component
decorator. In this case, that wipes out the <p>
tags that you want to retain.
To preserve them, you must instruct Angular how it should manage wrapped content. This can be accomplished with an <ng-content>
tag:
[app/ad-section.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'ad-section', template: ` <a href="#">{{adText}}</a> <ng-content select="p"></ng-content> ` }) export class AdSectionComponent { adText:string = 'Selfie sticks 40% off!'; }
With this, the ad anchor element is inserted before the wrapped content.
Similar to how ng-transclude
worked in Angular 1, ng-content
serves to interpolate the component tag's wrapped content into its template. The difference here is that ng-content
uses a select
attribute to target the wrapped elements. This is simply a CSS selector, operating in the same way in which @Component
decorators handle the selector
property in ComponentMetadata
.
The select
attribute in this example was superfluous, as it ended up selecting the entirety of the wrapped content. Of course, if the select value only matched some of the wrapped content, it would tease out only those elements and interpolate them. <ng-content>
will by default insert the entirety of the wrapped content if you decline to provide it with a select value.
Also note that the select attribute is a limited CSS selector. It is not capable of performing complex selections such as :nth-child
, and it is only able to target top-level elements inside the wrapping tags. For example, in this application, the paragraph tag inside <div><p>Blah</p></div>
would not be included with a select="p"
attribute value.
ViewChild
to reference child component object instances