Use scenarios
Let’s first clarify the usage scenarios of dynamic components. When the code is running, the components must be loaded dynamically. If it is a normal person, the code needs to determine that certain components are loaded in certain places based on the specific situation (such as user operations, requesting results from the background). These components are not static (not fixed).
The official website gives an example that when building dynamic advertising bars, the advertising components will continue to launch new ones, and it is obviously unrealistic to use templates that only support static component structures.
Let me give you another common example. Dynamic pop-up boxes, the pop-up components are uncertain and constantly updated. A purchase box pops up here and there, and then the style selection box is needed. The static component structure template cannot meet the growing demand of the people.
How to achieve it
Then we find a handle to see what is needed to implement dynamic components.
1. Where to place dynamic components
We need to know where to add dynamic components, that is, anchor points. So what can be used to load components?
You may want to say, don’t the components just load the anchor point? Isn’t the anchor point a DOM node? Of course it is loading in the DOM node.
Let’s first review the common methods of Angular operating DOM. Don’t think about the method of native JS operating DOM. Do you think it can be returned to an object that can load Angular components?
Angular provides a technology called DOM Query, which mainly comes from @ViewChild and @ViewChildren decorators, both of which have the same basic functions.
@ViewChild: Returns a single reference, looking for the first element or directive in the view's DOM that matches the selector.
@ViewChildren: Returns multiple references wrapped by the QueryList object, and finds all elements or instructions that can match the selector in the DOM of the view.
Basic syntax:
@ViewChild([reference from template], {read: [reference type]});
DOM Query's technology finds objects are divided into three categories:
ElementRef: If it mounts a simple html element similar to span;
TemplateRef: If it mounts the template element;
ViewContainerRef: It is impossible to infer, generally the programmer must indicate in the read that any DOM element can be used as a view container.
Through the official website query API, we can see that only ViewContainerRef is a container that can attach one or more views to a component, that is, only it can load the component. However, it should be noted that it is a DOM element (container) that can use a new component as its brother (node), it is a brother, not a father and son.
OK, we have determined to use ViewContainerRef to load the container. There are two ways to get the ViewContainerRef:
The first one is to get it through DOM Query query @ViewChild
<ng-container #addComp></ng-container> @ViewChild('addComp', {read: ViewContainerRef}) adComp:ViewContainerRef;
The second type is the example in the official website, using dependency injection
import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[ad-host]', }) export class AdDirective { constructor(public viewContainerRef: ViewContainerRef) { } }
Set the anchor point on ng-template, get the ViewContainerRef through instruction injection
template: ` <div class="ad-banner-example"> <h3>Advertisements</h3> <ng-template ad-host></ng-template> </div>
2. How to obtain component instances
When loading components into the view, they cannot be instantiated by just a new one, and then append, insert, etc. can be attached. Dynamic components need to be compiled and stored in advance by the compiler, and then encapsulated by ComponentFactory. The subsequent Component instances must be created through ComponentFactory. You can read this article [Translation] What you need to know about Angular dynamic components, but the use of SystemJsNgModuleLoader module loader has been eliminated.
ComponentFactoryResolver A simple registry that maps Components to generated classes that ComponentFactory can be used to create component instances. It can be used to get the factory of a given component type and then create the component of that type using the factory's create() method.
Let's look at the official website example code, the following is not the complete code
//Inject ComponentFactoryResolverconstructor(private componentFactoryResolver: ComponentFactoryResolver) { } loadComponent() { ...... // Get the ComponentFactory that can be used to create the generation of the ad component instanceconst componentFactory = (); //Get the container that loads the componentconst viewContainerRef = ; (); // Put the component into the container and pass some parameters to itconst componentRef = (componentFactory); (<AdComponent>).data = ; }
Do you think this is over, do you think the code written like this can run? Too young, let the previous wave tell you a few points to pay attention to:
The components, instructions, and pipelines in Angular are all encapsulated in modules. Taking components as an example, if you want to use other module components, other modules must have export, and you also need to import other modules in your own module. So remember to import and export the instructions for the official website example.
If it is a dynamic component, the component must be registered in the module's entryComponents property, but export is not used, and the import module is still required.
The above is a detailed explanation of Angular dynamic components. For more information about Angular dynamic components, please follow my other related articles!