How to use angular content projection .?

Angular content projection is one of the important and useful concepts of Angular. Because Angular is a tree of components, we can have nesting components inside one another. The content projection allows us to create a reusable and configurable component depending on the needs of its user using ng-content.

Where the user can pass dynamic data from the template of a parent component template to the nested child component template. The HTML content passed within the opening and closing tags of the child component is the content to be projected. This is what we call Content Projection.

<h3>Parent component heading ..<h3>
<app-child>
  <h3>Add dynamic heading</h3>
  <p>Dynamic content ...</p>
</app-child>
<div>
 Parent component content ..
</div>
<ng-content>

The ng-content is (a built-in directive in Angular) for projecting content.

When to use angular content projection ?

Sometimes we have dynamic and complex HTML code which we want to pass into a child component template from outside or parent component. We can accomplish this using content projection, which allows us to declare the place to insert external content into our component. This will allow us to create the reusable component with dynamic content for each component instance then the content projection is a key capability that we’ll need to use.

Angular content projection example basic.

In the example below, we will simply create a quote component and we will pass dynamic quote data from the parent component template to the quote component using ng-content. The ng-content is a special directive you can add in the child component template place where we want to render the content is ng-content with opening and closing. This serves as a hook you can place in a custom child component to mark the place for Angular where it should add any content it finds between the opening and closing tag of the child component in a parent.

The ngAfterContentInit method is called once after Angular puts external content into the component’s view. A placeholder for any external content is marked with the ngContent directive (the ng-content tag). Let’s first create a project to learn the ng-content and add a quote component to display dynamic data pass from the parent component.

$ng new contentProjectApp
$ng generate component quote
angular content projection example

Let’s update the app.component.ts file, we have the ngFor directive which iterates three quote component tags. In each tag, we are passing unique and dynamic quote data in the opening and closing of the quote tag in the parent template.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
  <div>
  <h3>Parent Component header</h3>
    <div class="child-component">
      <h3>Child component list of quote component passing dynamic quote data from parent template.</h3>
      <app-quote *ngFor="let quoteData of quotes" [quote]="quoteData" #quoteRef>
        {{ quoteData.data }}
      </app-quote>
    </div>
    <div>
      Parent Component content ...
    </div>
  </div>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  quotes = [
    { data: 'All philosophies are mental fabrications. There has never been a single doctrine by which one could enter the true essence of things.', by: 'Acharya Nagarjuna'},
    { data: 'Believe nothing, no matter where you read it, or who said it, no matter if I have said it, unless it agrees with your own reason and your own common sense.', by: 'Buddha'},
    { data: 'Remember that sometimes not getting what you want is a wonderful stroke of luck.', by: 'Dalai Lama XIV'}
  ];
}

Let’s now update the custom child quote component, quote.component.ts file as

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-quote',
  template: `
    <ng-content></ng-content>
    <p style="text-align:right;"><b>By: {{ quote.by }}</b></p>
  `,
  styleUrls: ['./quote.component.scss']
})
export class QuoteComponent {
  @Input() quote: { data: string, by: string };
}

Note: The ng-content in our child quote component template area gets replaced by dynamic quote data passed from the inner content of the quote tag in the parent template. Without the ng-content, we will not get an error but content pass to the child have no place to project in the child quote component.

How to use ng-content with multiple projection ?

We can use ng-content alone when we have only one projection in the child component. When we have multiple content projections in our child component then we have to know which content to project into particular ng-content?

The child component with ng-content directive has an optional select attribute, which allows us to sort of name our slot, to be more specific, it allows us to define the selector of our slot. The select attribute will look for a placeholder on your component while rendering to determine what content to insert where. In the example below, we have three ng-content elements in the card component expecting content project for

1. card-header : <ng-content select="[card-header]"></ng-content>
2. card-body   : <ng-content select="[card-body]"></ng-content>
3. card-footer : <ng-content select="[card-footer]"></ng-content>

In the app.component.html or parent component, we have to give corresponding content value to populate those placeholders in the card component. we need to declare the corresponding selector name in the parent component template. Selector names can be declared as attribute or CSS class names and here we declare select as an attribute.

<app-card>
  <span card-header>Rendered in header selection of ng-content</span>
  <div card-body>Rendered in body selection of ng-content</div>
  <span card-footer>Rendered in footer selection of ng-content</span>
</app-card>

To check the whole project for multiple content projects. We have declared two tags of card components in the parent component and each has a different header, body, and footer value for the card component. First, create a project and create a card component.

$ng new multipleProject
$ng generate component card
ng-content directive for Angular content projection
ng-directive used on Angular content projection example

Step 1: Update card.component.ts file and add three ng-content elements each will render and place holder for header, body, and footer of card component.

import { Component } from '@angular/core';

@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <div class="header">
          <ng-content select="[card-header]"></ng-content>
      </div>
      <div class="body">
          <p>Card Body: <ng-content select="[card-body]"></ng-content></p>
      </div>
      <div class="footer">
          <ng-content select="[card-footer]"></ng-content>
      </div>
    </div>`,
  styleUrls: ['./card.component.scss']
})
export class CardComponent {
  constructor() { }
}

Step 2: Edit app.component.ts file to add two card component tags as

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h3>Parent Component header</h3>
      <div class="child-component">
        <app-card>
          <span card-header >This should be rendered in header 1 selection of ng-content</span>
          <div card-body >This should be rendered in body 1 selection of ng-content</div>
          <span card-footer >This should be rendered in footer 1 selection of ng-content</span>
        </app-card>
        <app-card>
          <span card-header >This should be rendered in header 2 selection of ng-content</span>
          <div card-body >This should be rendered in body 2 selection of ng-content</div>
          <span card-footer >This should be rendered in footer 3 selection of ng-content</span>
        </app-card>
      </div>
      <div>
        Parent Component content ...
      </div>
    </div>`,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
}

Conclusion
We had learned how and when to use Angular content projection directive ng-content and have a few examples of content projection. I hope you got some ideas.

Related posts

Spread the love

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top