Developing interconnected dynamic components in Angular

Can you help me figure out how to create nested dynamic components while maintaining the parent-child relationship?

For instance, if I have data structured like this:

- A
--A.1
--A.2
-B
--B.1
-C 

I want to build components that reflect this structure, such as:

<A>
   <A1></A1>
   <A2></A2>
</A>
<B>
   <B1></B1>
</B>
<C></C>

However, my code only allows me to create either a parent component or a child component, but not both at the same time.

This is the snippet of my current code:

  setRootViewContainerRef(view: ViewContainerRef): void {
    this.rootViewContainer = view;
  }

  createComponent(content: any, type: any) {
 console.log(content);
    if (content.child && content.child.length > 0) {
      content.child.forEach(type => {
        const typeP = this.contentMappings[type.type];
        this.createComponent(type, typeP);
      });
    } else {
      this.renderComp(content,type)
    }
  }

  renderComp(content,type) {
    if (!type) {
      return
    }
    this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    this.componentReference = this.rootViewContainer.createComponent(this.componentFactory);

    if (this.componentReference.instance.contentOnCreate) {
      this.componentReference.instance.contentOnCreate(content);
    }
  }

When running this code, I encounter an issue where I can't successfully create both parents and child elements simultaneously.

If you'd like to see a working example, please visit StackBlitz.

I would greatly appreciate your assistance in resolving this matter.


Update:

Even after incorporating the ViewChild, I'm still facing the error message ViewChild not defined.

Please refer to this image which shows that the ViewChild element is not visible within the component.instance.

https://i.sstatic.net/waGp9.png

Here is the updated link to StackBlitz for further reference: https://stackblitz.com/edit/angular-dynamic-new-mepwch?file=src/app/content/a/a.component.ts

Answer №1

To effectively render child components, it is recommended to create a ViewContainer for each level:

a.component.html

<p>
a component works!
</p>
<ng-container #container></ng-container>

a.component.ts

export class AComponent implements OnInit {
  @ViewChild('container', { read: ViewContainerRef, static: true }) embeddedContainer: ViewContainerRef;

You can then render the component to its dedicated container by using the following service:

create-dynamic-component.service.ts

@Injectable()
export class CreateDynamicComponentService {
  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    @Inject(CONTENT_MAPPINGS) private contentMappings: any,
    private inlineService: InlineService
  ) { }

  createComponent(content: any, type: any, vcRef) {
    const componentRef = this.renderComp(content, type, vcRef);
    if (content.child && content.child.length) {
      if (!componentRef.instance.embeddedContainer) {
        const cmpName = componentRef.instance.constructor.name;
        throw new TypeError(`Trying to render embedded content. ${cmpName} must have @ViewChild() embeddedContainer defined`);
      }

       content.child.forEach(type => {
        const typeP = this.contentMappings[type.type];
        this.createComponent(type, typeP, componentRef.instance.embeddedContainer);
      });
    }
  }

  renderComp(content, type, vcRef: ViewContainerRef) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    const componentRef = vcRef.createComponent<any>(componentFactory);

    if (componentRef.instance.contentOnCreate) {
      componentRef.instance.contentOnCreate(content);
    }

    return componentRef;
  }
}

Take note of how the renderComp method utilizes the ViewContainerRef from the component with children:

 this.createComponent(type, typeP, componentRef.instance.embeddedContainer);

Forked Stackblitz

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

Glistening tabPanel and Analytics by Google

I recently completed a comprehensive tutorial here that delves into the intricacies of Google Analytics. Despite grasping the concepts explained in the tutorial, my understanding of jQuery remains at ground zero. In my ShinyApp, I utilize multiple tabPanel ...

Dynamic route fails to return value for ID search

Currently, I am testing by creating an array of users containing their respective IDs and names. There is also a button that triggers an onclick function to add the element's ID to the page's URL in order to establish a dynamic route. However, wh ...

Exploring Elasticsearch: Uncovering search results in any scenario

I've been working on a project where my objective is to receive search engine results under all conditions. Even if I enter a keyword that is not included in the search data or if it is an empty string, I still want to get some kind of result. How can ...

Post request in limbo

Looking for a way to delay post requests in Angular before they are sent? I've tried using pipe(delay(xxx)) and setTimeout, but haven't had any luck. Any suggestions on how to solve this issue? ...

What advantages does CfnAppSync provide over using AppSync in a CDK project?

We are in the process of enhancing our API by adding new JS resolvers and phasing out the VTL resolvers for an AWS AppSync CDK project, specifically built with Cfn<> Cloud Front CDK. The code snippet below illustrates how this can be achieved: ...

Examining the dimensions of a div element in AngularJS

As I delve deeper into understanding AngularJS and tackling the intricacies of how $watch operates, a specific scenario has caught my attention. I want to monitor and track changes in the dimensions of the div element with an ID of "area". My intention is ...

Creating an easy-to-update catalog utilizing an external file: A step-by-step guide

I am looking to create a product catalog with 1-4 products in a row, each displayed in a box with details and prices. I would like to be able to generate the catalog easily using an XML/CSV file that can be updated. Can anyone provide guidance on how to ac ...

Utilize the power of AJAX for efficiently sorting search results retrieved from MySQL

I'm in the process of designing a flight search page. The initial page contains a form, and when the submit button is clicked, the search results are displayed on the second page. Here's the link to the first page: To test it out, please follow ...

Steps for assigning a 404 status code upon promise rejection

My approach to testing the login functionality involves using promises and chaining them. If the user enters an invalid password, the data is rejected; otherwise, it is resolved. I then verify if the user is logged in successfully by chaining these methods ...

Encountering a "Duplicate identifier error" when transitioning TypeScript code to JavaScript

I'm currently using VSCode for working with TypeScript, and I've encountered an issue while compiling to JavaScript. The problem arises when the IDE notifies me that certain elements - like classes or variables - are duplicates. This duplication ...

Incorporating transitions within a styled component using @emotion/core

I'm currently working on adding a smooth transition effect when a button is clicked. The code that adjusts the isOpen property is functioning correctly. However, I'm facing an issue where instead of animating, the content just flips abruptly. I a ...

Exploring the isolated scopes of AngularJS directives

Exploring directives in AngularJS led me to this interesting code snippet: var app = angular.module('app', []); //custom directive creation app.directive("myDir", function () { return { restrict: "E", scope: { title: '@ ...

How can you establish the default value for a form from an Observable?

Check out my TypeScript component below export interface Product{ id?:string, name:string, price:string; quantity:string; tags:Tags[]; description:string; files: File[]; } product$:Observable<Product | undefined>; ngOnIn ...

The PopupControlExtender in ajaxToolkit seems to be malfunctioning when used with a textbox that has Tinymce

I recently built a website using ASP.NET, and I have a feature where a small tooltip appears on the right side of a text box when the user focuses on it. To achieve this, I am using the ajaxToolkit:PopupControlExtender in my code. <asp:TextBox ...

Replicate the styling of CSS class A and apply it to class B

Let's take a look at some code: <button id="test" class="ui-state-hover" value="Button"> In Context: I'm utilizing the JQuery UI CSS class ui-state-hover on an HTML button to ensure it always appears as if it's being hovered over. H ...

Applying unique textures to individual sides in Three.js

Here is the code for my textured cube: const textureLoader: TextureLoader = new TextureLoader(); const textureArray: MeshBasicMaterial[] = [ new MeshBasicMaterial({ map: textureLoader.load("./model/front.jpeg") }), new MeshBasicMaterial({ map ...

Adding images to chart labels in vue-chartjs explained

I'm facing a challenge in adding flag icons below the country code labels, and I could really use some guidance. Here is an image of the chart with my current code The flag images I need to use are named BR.svg, FR.svg, and MX.svg, located under @/a ...

Have you utilized the Remember Me feature on the Angular login page before?

Here is the Angular Html code I have written: <form action="#" [formGroup]="login" (ngSubmit)="onSubmit()" class="login-form"> <label for="email">Email</label> <input type=" ...

Get your hands on large files with ease by utilizing jQuery or exploring alternative methods

Within my User Interface, there exists an Advanced Search section where the user can select from 7 different options as depicted in the diagram. Based on these selections, a web service is triggered. This web service returns search results in JSON format, ...

Resetting Laravel passwords without relying on user emails, with the additional use of Angular for the front end interface

I'm currently working on a laravel-angular application that requires a password reset feature. However, the default password-reset in Laravel relies on email verification, which is not necessary for this particular application. I tried setting $confir ...