Developing Angular dynamic components recursively can enhance the flexibility and inter

My goal is to construct a flexible component based on a Config. This component will parse the config recursively and generate the necessary components. However, an issue arises where the ngAfterViewInit() method is only being called twice.

@Component({
    selector: "dynamic-container-component",
    template: `
        <div #container
            draggable="true"
            (dragstart)="dragstart($event)"
            (drop)="drop($event)"
            (dragover)="dragover($event)"
            style="border: 1px solid; min-height: 30px"></div>
    `
})
export default class DynamicContainerComponent {

    @Input()
    dynamicConfig: DynamicConfig;

    @ViewChild("container", {read: ElementRef})
    private elementRef: ElementRef;

    private isContainer: boolean;
    private componentRef: ComponentRef<any>;
    private componentRefs: ComponentRef<any>[] = [];
    
    constructor(
        private componentFactoryResolver: ComponentFactoryResolver,
        private injector: Injector,
        private viewContainer: ViewContainerRef,
        private render: Renderer2
    ){
        console.log("running");
    }

    ngAfterViewInit(){
        
        if (this.dynamicConfig){
            console.log(this.dynamicConfig)
            if (this.dynamicConfig.getType() == ComponentType.INPUT){
                this.isContainer = false;
                let componetFactory: ComponentFactory<InputComponent> = 
                    this.componentFactoryResolver.resolveComponentFactory(InputComponent);
                this.componentRef = this.viewContainer.createComponent(componetFactory);
                this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
            }else {
                this.isContainer = true;
                let items: DynamicConfig[] = this.dynamicConfig.getItems();
                if (items){
                    for (var i=0; i<items.length; i++){
                        let item: DynamicConfig = items[i];
                        let componetFactory: ComponentFactory<DynamicContainerComponent> = 
                            this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
                        let componentRef: ComponentRef<DynamicContainerComponent> = 
                            this.viewContainer.createComponent(componetFactory);
                        componentRef.instance.dynamicConfig = item;
                        this.componentRefs.push(componentRef);
                        this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
                    }
                }
            }
        }else {
            console.log("config does not exist");
        }

    }

    dragstart(event){
        debugger;
    }

    drop(event){
        debugger;
    }

    dragover(event){
        debugger;
        event.preventDefault();
    }


}

The creation of this Component will be triggered by another component using the following code. The Dynamic Component can then create additional Dynamic Components through the componentFactoryResolver.

    var configJson = {
        type: ComponentType.CONTAINER,
        items: [
            {
                type: ComponentType.CONTAINER,
                items: [{
                    type: ComponentType.CONTAINER,
                    items: [{
                        type: ComponentType.CONTAINER,
                        items: [{
                            type: ComponentType.INPUT
                        }]
                    }]
                }]
            }
        ]
    }

    this.config = new DynamicConfig();
    this.config.assign(configJson);
    console.log(this.config);

Update I came across a similar issue on Github: https://github.com/angular/angular/issues/10762

I have implemented some suggestions provided by others, although I believe it's more of a quick fix than a permanent solution.

ngAfterViewInit(){
    setTimeout(function(){
        if (this.dynamicConfig){
            console.log(this.dynamicConfig)
            if (this.dynamicConfig.getType() == ComponentType.INPUT){
                this.isContainer = false;
                let componetFactory: ComponentFactory<InputComponent> = 
                    this.componentFactoryResolver.resolveComponentFactory(InputComponent);
                this.componentRef = this.viewContainer.createComponent(componetFactory);
                this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
            }else {
                this.isContainer = true;
                let items: DynamicConfig[] = this.dynamicConfig.getItems();
                if (items){
                    for (var i=0; i<items.length; i++){
                        let item: DynamicConfig = items[i];
                        let componetFactory: ComponentFactory<DynamicContainerComponent> = 
                            this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
                        let componentRef: ComponentRef<DynamicContainerComponent> = 
                            this.viewContainer.createComponent(componetFactory);
                        componentRef.instance.dynamicConfig = item;
                        this.componentRefs.push(componentRef);
                        this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
                    }
                }
            }
        }else {
            console.log("config does not exist");
        }
    }.bind(this))
}

Answer №1

Once your dynamic component is created, Angular is nearly finished with the change detection cycle.

To handle this situation, you have a couple of options:

componentRef.changeDetectorRef.detectChanges()

Please note: Using setTimeout can achieve a similar effect, but it triggers the change detection cycle for the entire application.

Another tip is to rename the lifecycle hook to ngOnInit.

Additionally, ensure that you are passing the correct input to the dynamic component:

let item: DynamicConfig = items[i];
          ^^^^^^^^^^^^^
    However, it should not be a DynamicConfig instance, but rather a plain object.
...
const config = new DynamicConfig();
config.assign(item);
componentRef.instance.dynamicConfig = config;

Make sure to follow the correct steps as shown in this Ng-run Example.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to ensure that a URL is distinct once a link has been clicked

On my website list.php, I have a code snippet that changes the video in an iframe when a link is clicked. However, the URL remains as list.php instead of changing to something like list.php/Anohana10. How can I update the URL to reflect the selected video? ...

Using the data from two input fields, an ajax request is triggered to dynamically populate the jQuery autocomplete feature

I've been struggling with this issue for quite some time now. My goal is to pass 2 arguments (the values of 2 input fields in a form) in my ajax call so I can use them for a jQuery autocomplete feature (the search is based on a MySQL query using the v ...

Methods for altering the color of a div using addEventListener

Why doesn't the color change when I click on the div with the class "round"? Also, how can I implement a color change onclick? var round = document.querySelector(".round"); round.addEventListener("click", function() { round.style.backgroundCol ...

Troubleshooting Angular2: SVG Disappearing Upon Page Reload with PathLocationStrategy

I encountered a strange problem with SVG while using Angular2's PathLocationStrategy. The SVG code in my HTML template looks like this: <svg class="bell__icon" viewBox="0 0 32 32"> <use xlink:href="#icon-notificationsmall"></use&g ...

Issues with Angular routing after upgrading to Angular 4

After making updates in this way, they can be found here To update on Linux/Mac: run the command npm install @angular/{common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router,animations}@latest type ...

Using AngularJS to dynamically bind HTML content based on a variable’s value

I am trying to dynamically display an image of a man or a woman on a scene, depending on the gender of the person logged into my website. The ng-bind-html directive is working fine when I define "imageToLoad" statically. However, it fails when I try to gen ...

customize your selections in p-multiselect with dynamic choices primeng angular 2

I am currently working on implementing the Primeng datatable. I have put together an array containing headers, fields, and options called headersList. Here is how it looks: { header: "Time", field: "time", options: "timeOptions" }, { header: ...

Develop a personalized Angular module utilizing ngx-translate functionality

I recently developed my own personal Angular library filled with various components to streamline my projects. I followed a helpful tutorial to create the library successfully. Testing it in another project went smoothly. Now, the challenge is incorporati ...

Obtain the initial URL when initializing an Angular application

In the process of creating an Angular application for a landing page of a SaaS platform, the SaaS redirects to the Angular app using the URL http://localhost:4200/token=<token>. Shortly after, Angular switches to the home component at http://localhos ...

The usage of @Inject('Window') in Angular/Jasmine leads to test failures, but removing it results in code failures

Currently, I am encountering a dilemma related to using Angular's @Inject('Window') within a web API service. This particular issue arises when the Window injection is utilized in the service constructor, leading to test spec failures in the ...

Assistance needed with selecting elements using jQuery

After some practice with jQuery, I managed to select this specific portion from a lengthy HTML file. My goal is to extract the values of subject, body, and date_or_offset (these are name attributes). How can I achieve this? Let's assume this snippet i ...

Can the AJAX URL be loaded onto a new page?

https://i.stack.imgur.com/5l63v.pngPardon the poorly phrased question, but I need some guidance on a specific requirement regarding opening a URL in a new page. Currently, I have designed an AJAX URL and I'm wondering if it's possible to open thi ...

Uploading CouchDB document attachments using an HTML form and jQuery functionality

I am currently in the process of developing a web form that, upon submission, will generate a couchdb document and attach file(s) to it. I have followed tutorials and visited forums that suggest a two-stage process similar to futon's approach. While I ...

The anchor tag is failing to register when placed inside a dynamically created table cell

I'm attempting to place an anchor tag within a cell of an HTML table. Here is the code I am using, but for some reason, the tag is not being identified and no hyperlink is displayed in that cell. Is there an issue with the syntax? $("#tranTbodyI ...

Daily loop countdown timer

Looking to implement a daily countdown timer that ends at 10am, I have the following code set up: setInterval(function time(){ var d = new Date(); var hours = 09 - d.getHours(); var min = 60 - d.getMinutes(); if((min + '').length == 1){ ...

Utilizing AuthGuard for login page security

As a router guard, my role is to prevent users who are already logged in from accessing the /login route. However, I have noticed that even when I am logged in, I can still navigate to the /login route. Meet AuthGuard export class AuthGuard implements Ca ...

What is the method to ensure that the option group is selectable?

Is there a way to enable the selection of an option group? <select> <optgroup value="0" label="Parent Tag"> <option value="1">Child Tag</option> <option value="2">Child Tag</option> </optgroup> ...

TypeORM: establishing many-to-one relationships between different types of entities

Trying to represent a complex relationship in TypeORM: [ItemContainer]-(0..1)---(0..n)-[ContainableItem]-(0..n)---(0..1)-[ItemLocation] In simpler terms: A ContainableItem can exist either within an ItemContainer or at an ItemLocation. Although it cannot ...

Dropdown box not displaying any choices

I developed a basic reusable component in the following way: Typescript (TS) import {Component, Input, OnInit} from '@angular/core'; import {FormControl} from '@angular/forms'; @Component({ selector: 'app-select', templa ...

Retrieve the duplicated items from an array by comparing two specific properties in JavaScript

I need assistance in identifying and retrieving duplicate objects within an array that share similarities in 2 specific properties. Consider the object structure below: let arry = [ {Level: "A-1", Status: "approved"}, {Level: &q ...