Content projection in Angular

Gurinderpal Singh Narang
4 min readNov 24, 2023

--

Content projection allows you to insert and project content into a component’s template from outside the component. This is a powerful technique for creating reusable and flexible components.

In Angular, content projection is achieved using the <ng-content> element.

Single Slot Projection:

In a single-slot projection, you have a single <ng-content> element in your component's template. This means that all content passed into the component will be projected into the same location in the component's template. Here's a simple example:

Parent component:

<div class="courses" *ngIf="courses[0] as course">
<course-card (courseSelected)="onCourseSelected($event)" [course]="course">
<img
width="300"
alt="Angular Logo"
src="{{ course.iconUrl }}"
*ngIf="course?.iconUrl"
else
noImageFound
/>
<p #noImageFound>
{{ course?.description }}
</p>
<div class="course-description">
{{ course?.longDescription }}
</div>
</course-card>
</div>

Child component:

<div class="course-card" [ngClass]="cardClasses()" *ngIf="showCard">
<div class="course-title" *ngIf="course?.description">
{{ index + ". " + course?.description }}
</div>
<ng-content></ng-content>

<button (click)="onCourseViewed(course)">View course</button>
</div>

So here in the above example, The child component tag (course-card ) in the parent, contains some content within its template, including an image (<img>), a paragraph (<p>), and a <div>. All this content is intended to be projected into the child component.

Multiple Slot Projection:

In multi-slot projection, you can have multiple slots with different names. This allows you to project content into specific locations within the component’s template based on the slot name. Here’s an example:

Parent Component:


<div class="courses" *ngIf="courses[0] as course">
<course-card (courseSelected)="onCourseSelected($event)" [course]="course">
<img select="[img]"
width="300"
alt="Angular Logo"
src="{{ course.iconUrl }}"
*ngIf="course?.iconUrl"
else
noImageFound
/>
<p #noImageFound>
{{ course?.description }}
</p>
<div class="course-description" select="[description]">
{{ course?.longDescription }}
</div>
</course-card>
</div>

Child Component:

<div class="course-card" [ngClass]="cardClasses()" *ngIf="showCard">
<div class="course-title" *ngIf="course?.description">
{{ index + ". " + course?.description }}
</div>

<ng-content slot="img"></ng-content>
<ng-content slot="description"></ng-content>

<button (click)="onCourseViewed(course)">View course</button>
</div>

So here in the above example, In the parent component we use select attribute to filter and select specific content for projection and in the parent we used Angular’s slot-based content projection with the slog attribute along with the <ng-content> .

We can also use selectors as well to project the content, like className, id and CSS selectors to find the the content to be projects. Below is a sample code:

Parent Component:

<div class="courses" *ngIf="courses[0] as course">
<ccourse-card (courseSelected)="onCourseSelected($event)" [course]="course">
<img select="[img]"
width="300"
alt="Angular Logo"
src="{{ course.iconUrl }}"
*ngIf="course?.iconUrl"
else
noImageFound
/>
<p #noImageFound>
{{ course?.description }}
</p>
<div class="course-description" select="[description]">
{{ course?.longDescription }}
</div>
<h5>Remaining content</h5>
</course-card>
</div>

Child Component:

<div class="course-card" [ngClass]="cardClasses()" *ngIf="showCard">
<div class="course-title" *ngIf="course?.description">
{{ index + ". " + course?.description }}
</div>

<ng-content select="img"></ng-content>
<ng-content select=".course-description"></ng-content>
<ng-content></ng-content>

<button (click)="onCourseViewed(course)">View course</button>
</div>

Here the parent component uses the course-card component and tries to project content into different slots:

  • The img tag is intended to be projected into the first <ng-content select="img"> slot.
  • The div with the class course-description is intended to be projected into a named slot <ng-content select=".course-description">
  • The h5 tag with the text "Remaining content" is intended to be projected into the default slot which is <ng-content></ng-content>

Notes:

  • The select attribute is typically used with ng-content to filter and select specific content based on the provided CSS selector.
  • Make sure that the CSS classes or selectors used in select match the structure of the content you want to project.

In summary, the ng-content directive allows you to create a reusable child component that can accept and display content provided by its parent component. It provides a way to customize the appearance and behavior of the child component by projecting different content into it while maintaining a consistent structure.

How to Querying the template projected by content projection?

In Angular, @ViewChild is used to get a reference to a child component or directive in the template. However, it does not work directly with projected content via ng-content. This is because ng-content is a placeholder for content provided by the parent component, and the actual rendering of the content happens outside the component's view.

When you use @ViewChild, it looks for a component or directive in the current component's view, and since the content projected through ng-content is not part of the current component's view, @ViewChild cannot directly reference it.

If you need to interact with projected content in the child component, you can use a combination of @ContentChild and ngAfterContentInit lifecycle hook.

Here is link of @ContentChild in details: https://gurindernarang.medium.com/accessing-child-component-instances-using-contentchild-and-contentchildren-4af6ebfe7b47

--

--

No responses yet