Ng-magical directives series (ng-content)

"Magic is just science that we do not understand yet"
...Arthur C. Clarke

This article is part of what I call the magical directives series. In this series, we will unravel the mystery behind some interesting Angular directives. Afterwards, we can add this little magic to our tool box. I call them magical directives because they play a very important role in building reusable components across our Angular applications.

Below are the directives that we will be looking at in this series.

  • ng-template
  • ng-container
  • ng-content
  • *ngTemplateOutlet
links to other articles in this series are below

The ng-content

The ng-content is used to create configurable components. If we use the ng-content tag in our template, the inner content of the tags that define our component are then projected into this space.

//app-modal.html

<div>
  <div class="header"> Amazing Header </div>
  <div class="body">
     <p> Hi Friends! How are you today </p>
  </div>
  <div class="footer">This is our footer</div>
<div>

The sample code above represents a modal. If there is a need to have this modal in multiple places in our application with different styles/content in the body, it means that we will have to create more modals for each of the use cases. This is not a problem for small apps. But imagine when you have to use similar modal in over 5 different places or more with dynamic content in the body. This is where the magical effects of ng-content comes to our aid.

//app-modal.html

<div>
  <div class="header"> Amazing Header </div>
  <div class="body">
    <ng-content> </ng-content>
  </div>
  <div class="footer">This is our footer</div>
<div>

In the sample code above, instead of hardcoding the content of the body, we used the <ng-content> </ng-content> tag.
If other components wants to use the app-modal component with a custom body, the components will project the content inside the app-modal and the content projected will be positioned in the place where we have specified the ng-content tag in the app-modal as seen below.

//app-parent.html

<app-modal> 
   <div>
      <h3> My body intro. </h3>
      <p> The perfect paragraph </p>
   </div>
</app-modal>

Instead of creating multiple modals for different use cases across our application, we are now able to create a single modal component that allows us to project whatever content we want in the body. This process is referred to as Content projection. With this, we can build reusable components for a single project or a reusable component library for all our projects.

There are three types of content projection

  1. Single-slot content projection
  2. Multiple-slot content projection
  3. Conditional content projection
In order to keep our article dry, We will discuss the conditional content projection in the last session of this series *ngTemplateOutlet

The first example above covers the single-slot content projection, where we were only required to project a single dynamic content. What happens if the content of the header and footer are also dynamic?

//app-modal.html

<div>
  <ng-content> </ng-content>
  <div class="body">
    <ng-content> </ng-content>
  </div>
  <ng-content> </ng-content>
<div>

In this case, Angular is going to scream Wooooooooh. Because Angular wouldn't know the exact place to project the content that we are trying to project. The multiple-slot content projection comes to our rescue.

Multiple-Slot Content Projection

The multiple-slot content projection is what allows us to project multiple dynamic content to our component. The ng-content provides us with the select attribute that allows us to specify an identifier for a specific ng-content tag. Below are various ways in which we can achieve this with different selectors.

//app-modal.html

<div>
  <ng-content select=".header"> </ng-content>
  <div class="body">
    <ng-content select="#body"> </ng-content>
  </div>
  <ng-content select="span"> </ng-content>
<div>

Here, we have specified unique selectors for each content that needs to be projected. anytime we want to reuse this modal component, we just need to ensure that we specify a class with the name header, a body with an id of body and also a span tag. When Angular sees this, it will automatically know where to position or project the content that you have projected as seen in the example below.

//app-parent.html

<app-modal> 
   <div class="header"> Our header </div>
   <div id="body">
      <h3> My body intro. </h3>
      <p> The perfect paragraph </p>
   </div>
   <span> The footer</span>
</app-modal>

NOTE: If for some reasons there is an ng-content tag without an identifier (select), it will automatically serves as a default projector for any content that has no identifier.

Other examples of multiple-slot content projection

//app-modal.html
 <div>
   <ng-content select="[slot=head]"></ng-content>
   <ng-content select="[slot=body]"></ng-content>
   <ng-content select="[slot=footer]"></ng-content>
 </div>

//app-parent.html
<app-modal>
   <div slot="head"> Our header </div>
   <div slot="body"> Our body </div>
   <div slot="footer"> Our footer </div>
</app-modal>
//app-modal.html
 <div>
   <ng-content select="[head]"></ng-content>
   <ng-content select="[body]"></ng-content>
   <ng-content select="[footer]"></ng-content>
 </div>

//app-parent.html
<app-modal>
   <div head> Our header </div>
   <div body> Our body </div>
   <div footer> Our footer </div>
</app-modal>

you can also use custom components tags/selectors.

//app-modal.html
 <div>
   <ng-content select="app-head"></ng-content>
   <ng-content select="app-body"></ng-content>
   <ng-content select="app-footer"></ng-content>
 </div>

//app-parent.html
<app-modal>
   <app-head> Our header </app-head>
   <app-body> Our body </app-body>
   <app-footer> Our footer </app-footer>
</app-modal>

Summary

With the ng-content You can build your own reusable components without you worrying about the contents or the styles of the contents the users needs to project.

22