A versatile tool for creating customizable components on the fly

I am looking to create a function within my service that generates dynamic components into a ViewChild reference...

I attempted to do this by:

public GenerateDynamicComponent(ComponentName: string, viewContainerRef: ViewContainerRef, data?: any) {   
 switch (ComponentName.toUpperCase()) {
 case 'DYNAMICFORMS':
   const componentFactory = this.resolver.resolveComponentFactory(DynamicFormsComponent);
   const formref = viewContainerRef.createComponent(componentFactory);
   formref.instance.Data = data;
   return formref;
   break;

 default:
  return null;
 }

This method works well, but I want to avoid using a switch statement and passing the component name as a string.

Instead, I would like it to be like:

 public GenerateDynamicComponent<T>(viewContainerRef: ViewContainerRef, data?: any ) {
 const componentFactory = this.resolver.resolveComponentFactory<T>(typeof T);
 const formref = viewContainerRef.createComponent(componentFactory);
 formref.instance.Data = data;
 return formref;
 }

However, this approach does not work because typeof T <> component type. Is it possible to achieve this or does it need to be like in the first example?

Thank you!

Answer №1

If you need a function that can create dynamic components on the fly, you can use the following generic function.

public createDynamicComponent<T>(component: Type<T>, viewRef: ViewContainerRef): ComponentRef<T> {
    const factory = this.cfr.resolveComponentFactory<T>(component);
    return viewRef.createComponent(factory);
  }

When calling the generic function, make sure to specify the component type and the ViewContainerRef.

this.createDynamicComponent<DynamicComponent>(DynamicComponent, this.vc /* ViewContainerRef */);

For a working example, check out this Stackblitz example app.

Note: Remember to include dynamic components in the entryComponents array in the module file.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { DynamicComponent } from './dynamic.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, DynamicComponent ],
  entryComponents: [DynamicComponent],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Full Component Context

import {
  AfterViewInit, Component, ComponentFactoryResolver, ComponentRef, Type, ViewChild, ViewContainerRef
} from '@angular/core'
import { DynamicComponent } from './dynamic.component'


@Component({
  selector: 'my-app',
  template: `
  <h1>App component</h1>
  <div class="insert-dynamic-component">
    <ng-container #vc></ng-container>
  </div>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit  {
  @ViewChild('vc', {read: ViewContainerRef, static: false}) vc: ViewContainerRef;

  public constructor(private cfr: ComponentFactoryResolver) {}

  ngAfterViewInit() {
    // setTimeout() to prevent error "Expression has changed after it was checked"
    // See: https://blog.angular-university.io/angular-debugging/
    setTimeout(() => {
      const componentRef: ComponentRef<DynamicComponent> =
        this.createDynamicComponent<DynamicComponent>(DynamicComponent, this.vc);
      componentRef.instance.data = 'New data';
    });
  }

  public createDynamicComponent<T>(component: Type<T>, viewRef: ViewContainerRef): ComponentRef<T> {
    const factory = this.cfr.resolveComponentFactory<T>(component);
    return viewRef.createComponent(factory);
  }
}

DynamicComponent

import { Component } from '@angular/core';

@Component({
  selector: 'dynamic-component',
  template: `<h1>Dynamic Component!</h1>
  <p>Data: {{data}}</p>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class DynamicComponent  {
  public data: any
}

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

Unexpected INTERNAL error encountered with Firebase's Cloud Function update

Currently, I am immersed in a project involving Vue.js 3, Typescript, and Firebase. However, while attempting to integrate new cloud functions, I came across an unexpected issue: Failed to load resource: the server responded with a status of 500 () Un ...

Navigating through JSON object array using *ngFor directive in Angular 4

I am trying to iterate through an array of objects stored in my JSON file. JSON [ { "name": "Mike", "colors": [ {"name": "blue"}, {"name": "white"} ] }, { "name": "Phoebe", "colors": [ {"name": "red"}, { ...

What is the solution for the error "does not exist on type 'HTMLTableDataCellElement'" in Angular?

When working on my Angular project, I implemented a PrimeNG p-table component. <p-table [value]="users" class="user-roles-table" [rows]="5" [showCurrentPageReport]="true" [ ...

Retrieving the row value of a checkbox in an Angular table

I'm facing a challenge where I have a table with three columns, one of which is a checkbox. Here is an image for reference: https://i.sstatic.net/4U6vP.png Here is the code snippet: <div nz-row> <nz-table nz-col nzSpan="22" [nzLoading] ...

Loading game resources in advance for future or immediate utilization

I'm currently developing a game UI that involves a large number of image files, totaling around 30MB in size. I've been caching these images to the disk using service workers, but some of them are quite large at 3MB each. Even when they are retri ...

reposition content according to screen size

In my web development project, I am utilizing both bootstrap and angular to create a component that includes a menu feature. My goal is to have the menu displayed in the navbar when the screen size is large, but switch it to a dropdown menu on smaller scr ...

List with pulldown options

I am trying to implement a drop-down list with bullets using Angular 2, JavaScript, and CSS. Although I have managed to create the drop-down list, I am facing difficulty in adding bullets to the list items. Unfortunately, I have found that jQuery and Boot ...

Utilizing a variable string name for a method in Typescript Vue

My objective is to trigger a different function based on the value of a variable. However, when working in VS Code, I receive an error message that states: 'method' implicitly has a type of 'any' because 'type1Func' and &apos ...

Ensure your TypeScript class includes functionality to throw an error if the constructor parameter is passed as undefined

My class has multiple parameters, and a simplified version is displayed below: class data { ID: string; desp: string; constructor(con_ID:string,con_desp:string){ this.ID = con_ID; this.desp = con_desp; } } When I retrieve ...

Creating a Carousel in Angular 2 Without Using External Libraries

I am attempting to create a custom carousel component for my Angular 2 project, however, I am unsure of how to proceed. Are you able to provide me with some examples without using any libraries such as ngx-bootstrap? ...

Is TypeScript failing to enforce generic constraints?

There is an interface defined as: export default interface Cacheable { } and then another one that extends it: import Cacheable from "./cacheable.js"; export default interface Coin extends Cacheable{ id: string; // bitcoin symbol: stri ...

What are the steps to avoid TypeScript from automatically installing and referencing its own @types in the AppDataLocal directory?

I'm encountering a perplexing issue where it appears that TypeScript is setting up its own version of React in its unique global cache system (not entirely sure what to call it? presuming that's the case) and utilizing it within my project. In p ...

What is the mechanism behind Typescript interface scope? How can interfaces be defined globally in Typescript?

I'm diving into the world of Typescript and Deno, but I'm struggling to understand how interfaces scopes work. Here's the structure of my application: The first layer (App.ts) contains the core logic of my application. This layer can refer ...

angular restore reactive form

After sending my form, I am attempting to reset it but only the value is set to null. component.html <div *ngIf="!loading" fxLayout="row" class="note-textarea"> <form fxFlex fxLayout="column" fxLayoutGap="10px" [formGroup]="noteForm"> ...

Why does JavaScript not wait for the completion of forEach and instead executes the next line immediately?

While creating my api in nodejs and attempting to push the mongoose return count to a newly created array, it does not wait for the forEach loop to finish before executing json.res() and returning a null response. However, when I use setTimeout(), the re ...

How to Delete an Item from an Array in BehaviorSubject Using Angular/Typescript

I have encountered an issue while trying to delete a specific element from my array upon user click. Instead of removing the intended item only, it deletes all elements in the array. I attempted to use splice method on the dataService object, but I'm ...

Issue: Unable to find Store provider while using @ngrx/store in Angular 4.0

Issue: Error: No provider for Store! I am trying to initialize the store module in main.ts: platformBrowserDynamic().bootstrapModule(AppModule,[ provideStore({ characters, vehicles }) ]); Then I am injecting it into vehicle.component.ts: c ...

Angular universal triggers an "Error at XMLHttpRequest.send" issue

After updating my project to Angular 10 and incorporating angular universal, I encountered a strange error. While the application builds without any issues, I face an error when trying to run it on my development environment: ERROR Error at XMLHttpReque ...

The function service.foo is not recognized in Angular

My service function is not being recognized by my component import { Injectable } from '@angular/core'; import { ToastController } from '@ionic/angular'; @Injectable({ providedIn: 'root' }) export class LocationService { ...

Having trouble with TypeScript error in React with Material-UI when trying to set up tabs?

I have developed my own custom accordion component hook, but I am encountering the following error export default const Tabs: OverridableComponent<TabsTypeMap<{}, ExtendButtonBase<ButtonBaseTypeMap<{}, "button">>>> Check ...