When to use Angular’s Content Projection
Content projection is not among the first concepts you learn when starting to use Angular but it is a powerful feature that can be useful in a few scenarios.
What is Content Projection ?
Content Projection allows us to inject HMTL elements or custom components inside a component’s template. We can then reference the projected content and place it where we want it inside the component’s template.
Let’s look at an example, you’re building a container for a card component and it needs to support a number of different layouts for the content of the card and these vary a lot from one another.
We can make our card component have the generic visuals such as border, box shadow, and a close icon button, and make it accept injected content.
We have defined some CSS to give our component a card feel and an icon that will be our close button. The bit that does the magic is <ng-content></ng-content>
, we’ll see how that works when we use the <card-container>
component.
The HTML elements we placed inside the <card-container>
tag make up the content projected into <ng-content></ng-content>
. This way we can make the card-container truly reusable and delegate the layout of its inner content to the parent component.
Play around with the above example.
The above is a simple example for illustration purposes but let’s look at some use cases where content projection comes in handy.
Skeleton Loader
Some interesting applications of content projection can be found where there are components that are to be reused all over the UI such as loading spinners and skeletons. Let’s look at how the latter would look like if implemented with content projection.
Similarly to our first example this component accepts any type of content and it displays it only when this is not loading. This is very convenient as it allows this component to be focused on the CSS and behaviour of the skeleton only.
Let’s use the skeleton-container
in our app:
Here we’re injecting in our <skeleton-container>
three types of content each of which has a different shape. The first is an image, follows a header, and the last one is a div with a long piece of text.
The point here is that with appropriate styling the skeleton can be used for all sorts of content that may vary in shape and size:
Play around with the above example.
Custom Dropdown
The idea we’re developing with these use cases is that content projection is a good solution when a container component needs to display different types of content and have a number of different behaviours. There are a few reasons why we may not want to use the traditional way to control a component’s look and behaviour through inputs and outputs.
Maybe we don’t want to hide certain elements’ attributes and events inside the component as these may be too many or unknown in some cases when using third-party components. Or perhaps the component needs to support different behaviours and looks and we don’t know what these are going to be but we know they will vary wildly.
Let’s say we have a scenario similar to the last one described where we are building a custom dropdown component, we know it has to be reused in a few places but we don’t have the designs for some of these yet. We can code a dropdown container that accepts injected content for its option items. The items can have different looks and behaviours without having to be configured through the container component.
We’re defining a dropdown component that has a button for the opening and closing of the dropdown, and a div to contain the selection items. You may have noticed we’re using <ng-content>
in two places here.
The first one has the select
attribute; this accepts a CSS selector as value and it will extract the matching elements from the injected content. In the above we’re using it to get the placeholder for the dropdown.
The second one is a ‘generic’ <ng-content>
which will get all the injected content that is not been matched by any other <ng-content>
element.
We can now use this dropdown component and pass to it any type of items we want:
Apart from injecting different placeholder values, notice how the injected items have wildly different structures. The two types of items could definitely be custom components themselves and have their own inputs and outputs.
As a bonus, any event fired within our selection items won’t have to be bubbled up from the <dropdown-container>
but it can be handled directly in the higher level component (very likely that’s where we want to handle it).
Play around with the above example.
Conclusion
Content projection is used to create highly customisable components for which the content shape and structure can vary a lot without having to rely on long lists of inputs and outputs or intricate pieces of configuration logic.
Other components that tend to have the above issues are forms and inputs, so these are also good candidates for being implemented using content projection.
Resources & Credits
- Checkout my other article on When to use Angular’s Dynamic Components
- For a more detailed tutorial: https://blog.angular-university.io/angular-ng-content/
- Other well explained use cases: https://medium.com/angular-in-depth/use-cases-ng-content-83127165a95f
- CSS for the skeleton was adapted from: https://blog.prototypr.io/how-to-create-placeholder-loading-animations-in-css-2334255aa74c