Angular 2 is inspired by modern web standards, especially Web Components, which led to the adoption of some of the methods of content projection used there. In this article, we’ll look at them in the context of Angular 2 using the ng-content
directive
Content projection is an important concept when developing user interfaces. It allows us to project pieces of content into different places of the user interface of our application. Web Components solve this problem with the content
element. In AngularJS 1.x, it is implemented with the infamous transclusion.
Basic content projection in Angular 2
Let’s suppose we’re building a component called fancy-button
. This component will use the standard HTML button element and add some extra behavior to it. Here is the definition of the fancy-button
component:
@Component({ selector: 'fancy-button', template: '<button>Click me</button>' }) class FancyButton { … }
Inside of the @Component
decorator, we set the inline template of the component together with its selector. Now, we can use the component with the following markup:
<fancy-button></fancy-button>
On the screen, we are going to see a standard HTML button that has a label with the content Click me. This is not a very flexible way to define reusable UI components. Most likely, the users of the fancy button will need to change the content of the label to something, depending on their application.
In AngularJS 1.x, we were able to achieve this result with ng-transclude
:
// AngularJS 1.x example app.directive('fancyButton', function () { return { restrict: 'E', transclude: true, template: '<button><ng-transclude></ng-transclude></button>' }; });
In Angular 2, we have the ng-content
element:
// dunebook4/ts/ng-content/app.ts @Component({ selector: 'fancy-button', template: '<button><ng-content></ng-content></button>' }) class FancyButton { /* Extra behavior */ }
Now, we can pass custom content to the fancy button by executing this:
<fancy-button>Click <i>me</i> now!</fancy-button>
As a result, the content between the opening and the closing fancy-button
tags will be placed where the ng-content
directive resides.
Projecting multiple content chunks
Another typical use case of content projection is when we pass content to a custom Angular 2 component or AngularJS 1.x directive and we want different parts of this content to be projected to different locations in the template.
For instance, let’s suppose we have a panel
component that has a title and a body:
<panel> <panel-title>Sample title</panel-title> <panel-content>Content</panel-content> </panel>
And we have the following template of our component:
<div class="panel"> <div class="panel-title"> <!-- Project the content of panel-title here --> </div> <div class="panel-content"> <!-- Project the content of panel-content here --> </div> </div>`
In AngularJS 1.5, we are able to do this using multi-slot transclusion, which was implemented in order to allow us to have a smoother transition to Angular 2. Let’s take a look at how we can proceed in Angular 2 in order to define such a panel
component:
// dunebook4/ts/ng-content/app.ts @Component({ selector: 'panel', styles: [ … ], template: ` <div class="panel"> <div class="panel-title"> <ng-content select="panel-title"></ng-content> </div> <div class="panel-content"> <ng-content select="panel-content"></ng-content> </div> </div>` }) class Panel { }
We have already described the selector
and styles
properties, so let’s take a look at the component’s template. We have a div
element with the panel
class, which wraps the two nested div
elements, respectively: one for the title of panel
and one for the content of panel
. In order to grab the content from the panel-title
element and project it where the title of the panel
is supposed to be in the rendered panel, we need to use the ng-content
element with the selector
attribute, which has the panel-title
value. The value of the selector
attribute is a CSS selector, which in this case is going to match all the panel-title
elements that reside inside the target panel
element. After this, ng-content
will grab their content and set them as its own content.
Nesting components
We’ve already built a few simple applications as a composition of components and directives. We saw that components are basically directives with views, so we can implement them by nesting/composing other directives and components. The following figure illustrates this with a structural diagram:

The composition could be achieved by nesting directives and components within the components’ templates, taking advantage of the nested nature of the used markup. For instance, let’s say we have a component with the
sample-component
selector, which has the following definition:
@Component({ selector: 'sample-component', template: '<view-child></view-child>' }) class Sample {}
The template of the sample-component
selector has a single child element with the tag name view-child
.
On the other hand, we can use the sample-component
selector inside the template of another component, and since it can be used as an element, we can nest other components or directives inside it:
<sample-component> <content-child1></content-child1> <content-child2></content-child2> </sample-component>
This way, the sample-component
component has two different types of successors:
- The successor defined within its template.
- The successor that is passed as nested elements between its opening and closing tags.
In the context of Angular 2, the direct children elements defined within the component’s template are called view children and the ones nested between its opening and closing tags are called content children.
Using ViewChildren and ContentChildren
Let’s take a look at the implementation of the Tabs
component, which uses the following structure:
<tabs (changed)="tabChanged($event)"> <tab-title>Tab 1</tab-title> <tab-content>Content 1</tab-content> <tab-title>Tab 2</tab-title> <tab-content>Content 2</tab-content> </tabs>
The preceding structure is composed of three components:
- The
Tab
component. - The
TabTitle
component. - The
TabContent
component.
Let’s look at the implementation of the TabTitle
component:
@Component({ selector: 'tab-title', styles: […], template: ` <div class="tab-title" (click)="handleClick()"> <ng-content></ng-content> </div> ` }) class TabTitle { tabSelected: EventEmitter<TabTitle> = new EventEmitter<TabTitle>(); handleClick() { this.tabSelected.emit(this); } }
There’s nothing new in this implementation. We define a TabTitle
component, which has a single property called tabSelected
. It is of the type EventEmitter
and will be triggered once the user clicks on the tab title.
Now, let’s take a look at the TabContent
component:
@Component({ selector: 'tab-content', styles: […], template: ` <div class="tab-content" [hidden]="!isActive"> <ng-content></ng-content> </div> ` }) class TabContent { isActive: boolean = false; }
This has an even simpler implementation—all we do is project the DOM passed to the tab-content
element inside ng-content
and hide it once the value of the isActive
property becomes false
.
The interesting part of the implementation is the Tabs
component itself:
// dunebook4/ts/basic-tab-content-children/app.ts @Component({ selector: 'tabs', styles: […], template: ` <div class="tab"> <div class="tab-nav"> <ng-content select="tab-title"></ng-content> </div> <ng-content select="tab-content"></ng-content> </div> ` }) class Tabs { @Output('changed') tabChanged: EventEmitter<number> = new EventEmitter<number>(); @ContentChildren(TabTitle) tabTitles: QueryList<TabTitle>; @ContentChildren(TabContent) tabContents: QueryList<TabContent>; active: number; select(index: number) {…} ngAfterViewInit() {…} }
In this implementation, we have a decorator that we haven’t used yet—the @ContentChildren
decorator. The @ContentChildren
property decorator fetches the content children of the given component. This means that we can get references to all TabTitle
and TabContent
instances from within the instance of the Tabs
component and get them in the order in which they are declared in the markup. There’s an alternative decorator called @ViewChildren
, which fetches all the view children of the given element. Let’s take a look at the difference between them before we explain the implementation further
In next chapter you learn about .. ViewChild versus ContentChild