Angular Content Projection using <ng-content>

Anjali Tanpure
4 min readJul 30, 2020

Angular provides lots of features, and one of the most important feature is component reusability. To reuse the components we can use content projection or ng-content. Let’s understand how we can use <ng-content> to reuse the component and project the contents dynamically.

Let’s take a simple example. Suppose we have two forms as following:

We want this one to be a ‘Create User’ form & the other one to be a ‘Login’ form. And we just need a different form title and the button text for each of the forms with the same fields i.e ‘Email Address’ & ‘Password’. So it’s not a good practice to create two separate forms/components. Then using a single form/component how can we solve this problem ?

Here we can use content projection to change a piece of form. Let’s see how we can do this ? Look at the code below:

<auth-form (submitted)=”createUser($event)”></auth-form><auth-form (submitted)=”loginUser($event)”></auth-form>

For both Login and Create Account forms we are using the same selector of AuthFormComponent. If we look at the template code:

<form #form=”ngForm” (ngSubmit)=”onSubmit(form.value)”>
<h3>My Form</h3>

<div class=”form-group”>
<label for=”email”>Email Address:</label>
<input type=”text” class=”form-control” id=”email”>
</div>
<div class=”form-group”>
<label for=”password”>Password:</label>
<input type=”password” class=”form-control” id=”pwd”>
</div>
<button mat-button color=”success”>Save</button>
</form>

Using this code it will render the forms the same as above images. Now we want the title to be ‘Create Account’ for one form and ‘Login’ to another instead ‘My Form’. So this is where the content projection is useful. We need to pass pieces of information in between these component tags, like this:

<auth-form (submitted)="createUser($event)">
<h3>Create Account</h3>
</auth-form>
<auth-form (submitted)="loginUser($event)">
<h3>Login</h3>
</auth-form>

But if we do only this much part it’s not going to work. So what we need to actually do is, project the content through something called as ng-content. We need to replace form title which we are showing using <h3> tag in auth component with <ng-content> like this:

<form #form=”ngForm” (ngSubmit)=”onSubmit(form.value)”>
<ng-content></ng-content>

<div class=”form-group”>
<label for=”email”>Email Address:</label>
<input type=”text” class=”form-control” id=”email”>
</div>
<div class=”form-group”>
<label for=”password”>Password:</label>
<input type=”password” class=”form-control” id=”pwd”>
</div>
<button mat-button color=”success”>Save</button>
</form>

In the browser’s console we can see that the content has been projected inside our forms. See the below image for reference.

Ok! Now what if we have multiple contents to project dynamically within a single form?

Projection slots with ng-content

Now our next use case is we want to inject different buttons based on form. For example, we want button text as ‘Sign Up Free’ for create account form & ‘Login’ for login form instead of ‘Save’.

To do this we need to add a button like we did to show dynamic form title:

<auth-form (submitted)="createUser($event)">
<h3>Create Account</h3>
<button type="submit">Sign Up Free</button>
</auth-form>
<auth-form (submitted)="loginUser($event)">
<h3>Login</h3>
<button type="submit">Login</button>
</auth-form>

After doing above changes our forms look like:

Oops! Here we have dynamic buttons as per our need but the placement of buttons are wrong. How can we fix this issue ? So the answer is using content projection slots. It tells us where to inject each particular piece of information.

Let’s see how we can do this. First we will remove the extra button ‘Save’ which we can see at the bottom & this is from our common auth component. And instead at that place we can add another ng-content tag to display our buttons at the bottom. Now the way to know which ng-content is for which selector is, using a ‘select’ attribute. Syntax for this is, select=“<tag_name>”.

Check the code below how we can do this:

<form #form=”ngForm” (ngSubmit)=”onSubmit(form.value)”>
<ng-content select=”h3"></ng-content>
<div class=”form-group” style=”padding-top: 20px;”>
<label style=”font-weight: 500;” for=”alterEgo”>Email Address:</label>
<input type=”text” class=”form-control” id=”alterEgo”>
</div>
<div class=”form-group”>
<label style=”font-weight: 500;” for=”alterEgo”>Password:</label>
<input type=”text” class=”form-control” id=”alterEgo”>
</div>
<ng-content select=”button”></ng-content>
</form>

And doing above changes our forms look like this:

This works similar to a document query selector. Instead of tag-name to select, we can also pass CSS class or id name like this: select=“.class_name” or select=“#id_name”.

Note: <ng-content> does not produce a content, it simply projects existing content.

I hope you enjoyed this article. If you have any queries, you can ask me in the comments section below. Thank You !

--

--