Dynamic Elements in Angular with Best Practice
In this series, I will demonstrate you how we can create elements dynamically in run-time and which design patterns we are using for best paractice. After this serial of stories, you will be able to use dynamically generated component, element, form, table and even pipe. Also it is great opportunity for understanding Dependency Injection(DI) and Angular DI Framework from all aspects in Angular.
Desing Patterns
Lets dive into patterns used while we are generating instances dynamically. Each dynamic element, form and table has host element, injector, provider, resolver.
1. Host Element
Host elements get configurations as an input which includes type
property as a token of component and settings
property as a data supply to this component. With these configurations, it uses this type
property to find component and uses settings
property when create an injector. Then the host element sends these component constructor and injector to NgComponentOutlet directive for generating component instance and inserting into the view.
2. Injector
While loading components dynamically, we will use injectors for storing providers and transfering data from host element to component because we can’t bind input and output to NgComponentOutlet
.
3. Provider
We are using a function for generating provider whose first parameter represents registration name of a component and second parameter is the component itself. This function returns a ValueProvider.
It means that it provides a given value. In our example, ValueProvider
will provide type and component as an object. Also it should have an injection token in provide
property because tokens are the unique name of the providers and they are necessary for finding providers among others.
Providers can provide value using same token and optional multi
property. This multi
property is used for storing values provided from different providers as a collection. After multiple components call this function and inject it in module, component or directive, we will read these values as collected in one array.
Now, let’s look at a header component to understand how components provide element.
As we can see, it calls provideElement
function and sends this function to its own type and component. Then it stores this provider in a variable named HEADER_PROVIDER
to add in providers array of module.
We added HeaderComponent
in entryComponents
because of it will be created dynamically at run-time. If we don’t add, Angular tree-shake mechanism will destroy this component while compilation process(with Angular 9, entryComponents
is not necessary anymore). We will apply this process for banner, footer and slider components.
4. Resolver
When are we using our resolver and how do we resolve type?. Let’s look at our resolver.
When our resolver resolves a type, it will use providers array and return component which paired with given type. Because of easy to look up, resolver coverts this array to a map before resolve them.
After all these steps, our components will be generated dynamically and look like this.
You can see all code files in github repository.