Angular content projection

Angular content projection

Angular content projection is one of the key features of Angular [1], which is also known as transclusion, and if you are from the world of React, you can think of it as children. Content projection allows developers to create reusable components that can be used to display dynamic content in different parts of an application.

In this guide, we’ll explore the concept of content projection in Angular, its benefits, and how to use it to create more flexible and reusable components.

What is the Angular Content Projection?

Content projection in Angular is the ability to project content from a parent component into a child component. It allows developers to create reusable components that can be used to display different types of content depending on the context in which they are used.

Content projection is achieved using the ng-content directive, which is used to mark a placeholder for projected content in a child component. The ng-content directive can be used with a selector to filter the projected content based on its type.

Content projection works like children in React, so you pass the content that you want to render as children between the tags of your component’s template:

<component-with-projected-content>
    <div>
        <span>This will be projected in component-with-projected-content</span>
        <!-- You can also use another components here -->
        <some-component></some-component>
    </div>
</component-with-projected-content>

Then in the template of the component that should render projected content you use the <ng-content> directive to render passed content in component’s desired position [2]:

<div>
   <span>Normal content of your component</span>
   <!-- Here will be rendered projected content -->
   <ng-content></ng-content>
</div>

How to Use Content Projection in Angular

The above example showed you how to use simple content projection in Angular, however, there are 2 more options for that:

Named Content Projection

Named content projection allows developers to project specific pieces of content into specific locations in the child component’s template. This is done using the ng-content directive with a select attribute.

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

@Component({
  selector: 'app-child',
  template: '<ng-content select=".header"></ng-content><ng-content select=".body"></ng-content>',
})
export class ChildComponent {}
import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <app-child>
      <h1 class="header">Header Content</h1>
      <div class="body">Body Content</div>
    </app-child>
  `,
})
export class ParentComponent {}

In this example, the parent component passes two pieces of content to the child component: a header and a body. The header is displayed in the location marked by the ng-content directive with the class header, and the body is displayed in the location marked by the ng-content directive with the class body.

Conditional content projection

f your component needs to conditionally render content, or render content multiple times, you should configure that component to accept an <ng-template> element that contains the content you want to conditionally render.

    We are not using the <ng-content> directive here intentionally, because with this directive content is always initialized, even if the child component doesn’t use the <ng-content> at all, or use it with a structural directive such as *ngIf

    Here’s how you would typically do that:

    import { Component, ContentChild, TemplateRef, Input } from '@angular/core';
    
    @Component({
      selector: 'app-tab',
      template: `
        <ng-container *ngIf="selected && contentTemplate">
          <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
        </ng-container>
      `,
    })
    export class TabComponent {
      @ContentChild('tabContent') contentTemplate: TemplateRef<any>;
      @Input() selected: boolean;
    }

    We use ContentChild and TemplateRef to get a reference to the content template provided by the parent component. We conditionally display the content template based on the selected property.

    import { Component } from '@angular/core';
    
    interface Tab {
      title: string;
      content: string;
      selected: boolean;
    }
    
    @Component({
      selector: 'app-tabs',
      template: `
        <div class="tabs">
          <div
            class="tab"
            *ngFor="let tab of tabs"
            [class.active]="tab.selected"
            (click)="selectTab(tab)"
          >
            {{ tab.title }}
          </div>
        </div>
        <app-tab *ngFor="let tab of tabs" [selected]="tab.selected">
          <ng-template #tabContent>
            {{ tab.content }}
          </ng-template>
        </app-tab>
      `,
    })
    export class TabsComponent {
      tabs: Tab[] = [
        {
          title: 'Tab 1',
          content: 'This is the content of Tab 1',
          selected: true,
        },
        {
          title: 'Tab 2',
          content: 'This is the content of Tab 2',
          selected: false,
        },
        {
          title: 'Tab 3',
          content: 'This is the content of Tab 3',
          selected: false,
        },
      ];
      selectTab(tab: Tab) {
        this.tabs.forEach((t) => (t.selected = false));
        tab.selected = true;
      }
    }

    In the parent component, we define the tabContent template using ng-template, and pass it into the app-tab component using the ContentChild decorator. We also pass the selected property into the app-tab component using an input property.

    This approach allows us to define the content of each tab using a template, which can be conditionally displayed based on the selected property. It also avoids the use of ng-content and allows for more flexibility in how the content is displayed.

    Benefits of Content Projection

    Content projection provides a number of benefits for Angular developers, including:

    1. Reusability: With content projection, developers can create reusable components that can be used to display different types of content in different contexts. This reduces the amount of code that needs to be written and makes the codebase more maintainable.
    2. Flexibility: Content projection provides a high level of flexibility, allowing developers to customize the content displayed in a component based on the context in which it is used.
    3. Simplified Development: Content projection simplifies the development process by reducing the number of components needed to display different types of content. This makes it easier to create complex user interfaces.

    Conclusion

    Content projection is a powerful feature of Angular that provides developers with a high level of flexibility and reusability when building complex web applications. By using content projection, developers can create more flexible and maintainable components that can be used in a variety of different contexts. If you’re familiar with React, you might compare it to React’s Children API which enables the same kind of functionality.

    If you want to learn more about React, I have written a few posts about that library, such as useState hook

    List of references:

    Leave a Comment

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