Create Advanced Components in Angular
In this article, we are going to learn how to create a dynamic component that uses ng-content
. We are going to learn advanced techniques like how to create an injector and passing TemplateRef
or a Component
as content.
To demonstrate all this, we will create a tooltip. Our final result will be the following:
Build the Tooltip Component
First, we need to create the component UI. We will use ng-content
so that the user can pass a dynamic content.
In the end, we will dynamically create this component through the tooltip directive, so let’s stop here and build the directive (we will come back to this component later).
Build the Tooltip Directive
If you are not familiar with the basics of creating components dynamically in Angular, I highly recommend stopping here and reading my article — Dynamically Creating Components With Angular.
What we need to do now is to listen to the mouseenter
event and create the tooltip component.
We see two new parameters that we did not discuss in the previous article.
Injector —
We can use the resolveAndCreate()
method, which resolves an array of providers and creates an injector from those providers. The config
parameter is the same as @ngModule.providers
.
In our case, we pass an object with only one property, the host
element, so we can calculate the tooltip position. In real life, you will pass more things, such as the tooltip placement, animation, etc.
Note that we can pass the same object as an Input()
to the tooltip component, for instance:
this.componentRef.instance.config = config
but we want to avoid dealing with errors like “EXCEPTION: Expression has changed after it was checked.”
Now we can use the injector providers in our tooltip component and calculate the tooltip position.
Pay attention to the technique we use to grab the .tooltip-container
. We are leveraging directives to target the element and query for him with the @ViewChild
decorator that provides an API to obtain the native element.
We are done with the tooltip component, now let’s see how we can project elements to the ng-content
tag.
ngContent —
The fourth parameter is what Angular will pass as ng-content
. This parameter expects to receive a two-dimensional array of DOM elements. Let’s see the generateNgContent()
method.
Handling text —
This is the simplest case. We check that our Input()
is a string and create a text node with the help of the renderer
service.
Handling TemplateRef —
We instantiate an Embedded View based on the TemplateRef
. This will give us reference to a View
object that has one special property — rootNodes
. The rootNodes
is an array of DOM nodes that are extracted from the template, and that’s exactly what we need.
Handling Components —
We need to create the component, and then we can pass the nativeElement as ng-content
.
If you want to learn more about Views
in Angular you can read this article by Maxim Koretskyi.
The final step is to remove the component on mouseout
.
You can find the full source code in the following link.
Things to not miss:
Follow me on Medium or Twitter to read more about Angular, Vue and JS!