Angular Content Projection the complete guide

Islam Muhammad
3 min readMar 11, 2019

--

Photo by Maksym Kaharlytskyi on Unsplash

Content Projection

In this post i am going to answer 3 basic questions ( What, Why and How ).

What is content projection ?

In Angular, content projection is used to project content in a component (angular.io).

Why we use it?

  1. Many Components in your app using same structure and style but the content are different, in another word Reusability.
  2. You build a component for display only and the other component built for handling user actions, in another word Separation of concern.

How we use it ?

Single-Slot

Basically you just add <ng-content></ng-content> in your html and this will replaced with content from outside the component

<!-- inside container component -->
<ng-content></ng-content>
<!-- inside another component -->
<container-component> <p>Content Here</p> </container-component>

Multi-slot ( Targeted projection )

ng-content accepts a select attribute, which allow us to set specific css selector name for that slot.

  • Using element(s) name
<!-- inside container component -->
<ng-content select="slot-one"></ng-content>
<!-- inside another component using container component -->
<container-component>
<slot-one>Content For Slot one</slot-one>
</container-component>

If you using it in a normal angular cli setup, you will hit an error if you use the <slot-one> tag now.

Unhandled Promise rejection: Template parse errors: ‘slot-one’ is not a known element, Angular does not recognize the slot-one tag. slot-one is neither a directive nor a component.

A quick way to get around this error is to add schema metadata property in your module, set value to NO_ERRORS_SCHEMA in your module file.

// app.module.tsimport { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; //
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ContainerComponent } from './container-component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, ContainerComponent],
bootstrap: [AppComponent],
schemas: [NO_ERRORS_SCHEMA] // add this line
})
export class AppModule {}
  • Using Attribute(s) [name] | [name][another-name]
<!-- inside container component -->
<!-- Using Single Attribute -->
<ng-content select="[slot-one]"></ng-content>
<!-- Using Multiple Attributes -->
<ng-content select="[slot][two]"></ng-content>
<!-- inside another component using container component -->
<container-component>
<p slot-one>Content For Slot one</p>
<p slot two>Content For Slot two</p>
</container-component>
  • Using Attribute with Value [name="vlue"]
<!-- inside container component -->
<ng-content select="[slot='one']"></ng-content>
<ng-content select="[slot='two']"></ng-content>
<!-- inside another component using container component -->
<container-component>
<p slot="one">Content For Slot one</p>
<p slot="two">Content For Slot two</p>
</container-component>
  • Using class(s) .name | .name.another-name
<!-- inside container component -->
<!-- Using Single Class -->
<ng-content select=".slot-one"></ng-content>
<!-- Using Multi Class -->
<ng-content select=".slot.two"></ng-content>
<!-- inside another component using container component -->
<container-component>
<p class="slot-one">Content For Slot one</p>
</container-component>
  • Without using wrapping div

as you can see in the previous example you can use the target slot by wrapping your content with div or element and attach the selector with it, but in some cass you just want to put it there.

Using ngProjectAs angular attribute on ng-container tag or and tag you want.

<!-- inside container component -->
<!-- Element -->
<ng-content select="slot-one"></ng-content>
<!-- Attributes -->
<ng-content select="[slot-one]"></ng-content>
<ng-content select="[slot][two]"></ng-content>
<!-- Attributes with Value -->
<ng-content select="[slot='one']"></ng-content>
<ng-content select="[slot='two']"></ng-content>
<!-- Inside ngProjectAs use projected name-->
<ng-container ngProjectAs="slot-one">
Very <strong>important</strong> text with tags.
</ng-container>
<ng-container ngProjectAs="[slot][two]">
Very <strong>important</strong> text with tags.
</ng-container>

Inside *ngFor

// Container Component
@Component({
...
template: `
<li *ngFor="let item of items">
<ng-template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{$implicit: item}"></ng-template>
</li>
`
})
class TabsComponent {
@ContentChild(TemplateRef) templateRef:TemplateRef;
@Input() items;
}
<!-- data here is the array input for container-component -->
<container-component [items]="data">
<ng-template let-item>
<!-- here we can use item -->
{{ item }}
</ng-template>
</container-component>

--

--

Islam Muhammad
Islam Muhammad

Responses (3)