Dynamically load current components in Angular 2's final release

In my quest to dynamically load a component in the upcoming release version 2.0.0, I encountered some challenges.

Previously, in RC5, I utilized the following code for loading:

I created a directive responsible for loading the controls:

import {
  CheckboxComponent, CheckboxListComponent, DatePickerComponent
} from '../components/';

@Directive({
      selector: '[ctrl-factory]'
    })
    export class ControlFactoryDirective implements OnChanges {
      @Input() model: any;

      constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
      }

      create(cp) {
        this.resolver.resolveComponent(cp)
          .then(factory => {
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
            this.vcRef.createComponent(factory, 0, injector, []);
            let ch = this.vcRef.createComponent(factory, 0, injector, []).instance;
            ch.model = this.model;
        });
      }

      ngOnChanges() {
        if (!this.model) return;

        switch (this.model.type) {
          case 'checkbox':
            this.create(CheckboxComponent);
            break;
          case 'checkboxlist':
            this.create(CheckboxListComponent);
            break;
          case 'datepicker':
            this.create(DatePickerComponent);
            break;
          default:
            break;
        }
      }
    }

Then, I included that directive on my page like so:

<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div>

However, upon transitioning from rc5 to the final release version 2.0.0, I realized that the resolver was replaced by compiler.

I attempted to follow various suggestions on how to achieve this with different codes, but they all seemed too complex and I couldn't get it to work.

For example, check out this link: How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

Although it may be more tailored to a specific scenario, I simply needed to load the component and set an @Input named model.

During my attempts, I had to dynamically create a module for each component and add the component to it. However, I encountered issues indicating that the component was being added to more than one Module, despite trying to remove it unsuccessfully.

The majority of the code I used was sourced from this link:

I made a few modifications based on that reference.

Update

I eventually managed to make it functional by adopting the following approach:

The create method underwent a transformation as follows:

private create(cp) {
    @NgModule({
      imports: [BrowserModule, ControlsModule],
      declarations: []
    })
    class DynamicModule {}

    this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
      .then(({componentFactories}) => {
        const compFactory = componentFactories.find(x => x.componentType === cp);
        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
        const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []);
        cmpRef.instance.model = this.model;
      });
  }

Many sources suggested creating the Component and assigning it to the DynamicModule. However, this led to conflicts when the same component was declared in a different module. In my case, importing my ControlsModule - which contains all exported controls - proved to be the solution.

Answer №1

Latest News Update!

New Release: The NgComponentOutlet feature was first introduced in version 4.0.0-beta.3. Check it out on GitHub here.

Exciting update coming soon! Click here to learn more about NgComponentOutlet.

Interested in implementing this new feature? Here are two options for you:

1) Method using ComponentFactoryResolver:

This method involves utilizing the pre-generated factory and code structure as shown below:

constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { }
create(comp) {
  const factory = this.resolver.resolveComponentFactory(comp);
  const compRef = this.vcRef.createComponent(factory);

  (<any>compRef).instance.model = this.model;
}

In this case, make sure to define dynamic components in the declarations and entryComponents properties within the module's decorator.

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

2) Approach using Compiler:

In this scenario, you can execute module compilation by invoking

compiler.compileModuleAndAllComponentsAsync
and then locating the component from the componentFactories array. Below is a sample directive implementation:

constructor(private vcRef: ViewContainerRef, private loader: DynamicLoaderService) { }

create(comp) {
  this.loader.createComponentFactory(comp).then(factory => {
     const compRef = this.vcRef.createComponent(factory);

    (<any>compRef).instance.model = this.model;
  })
}

The DynamicLoaderService serves as a global service responsible for loading and storing component factories securely.

@Injectable()
export class DynamicLoaderService {
  constructor(protected compiler: Compiler) {}

  // Additional methods and caching logic for managing component factories

}

Explore a live example via Plunker.

Feel free to ask any questions or seek further assistance. Hope this information proves helpful!

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

Transform the image data retrieved from an external API into the appropriate format for displaying on the webpage

When I make a call to an external API, it returns image data that I want to render on my page. However, the response looks like this when I log it to the console: https://i.stack.imgur.com/GpDhH.png I'm not very familiar with image formats, so I&ap ...

A problem encountered in specific JavaScript code

As a newcomer to JavaScript, I have encountered an issue while trying to run this script: <html> <head> <title>Exploring javascript functionalities</title> </head> <body> <p id="demo">I ...

Passing arguments inside the source attribute of an image or link tag in Node.js and

As a beginner in NodeJS, I am facing an issue with passing arguments inside links and image sources. In my template.html file, I have included various scripts and elements to create a search form. The issue arises when I try to incorporate values from the ...

Send multipart form data to a different server via pipe

I need assistance with handling a POST request on my Node Express server for uploading images through multipart form data. Currently, my Express app is set up to use body parser which does not support multipart bodies and suggests using alternative librari ...

Tips for incorporating css styles and javascript into handlebar files

I've been trying to figure out how to add styles and js to my handlebar files, but I'm hitting a roadblock. I attempted to use partials to store the stylesheet tags and then include those partials in the handlebars file, but it didn't work a ...

Passing data as a parameter from the view to the controller using AngularJS

I am attempting to retrieve data from a view, which must be passed as a parameter in a function in order to populate an array in the controller. However, I am not receiving any objects in return. Here is what I have tried: VIEW <div ng-repeat="cssfram ...

There was an error encountered: Uncaught TypeError - Unable to access the 'append' property of null in a Typescript script

I encountered the following error: Uncaught TypeError: Cannot read property 'append' of null in typescript export class UserForm { constructor(public parent: Element) {} template(): string { return ` <div> < ...

How to incorporate markdown files as strings in Next.js

Is there a way to bring in markdown files as strings in Next.js for use on both the client and server sides? ...

"Implementing a column template in jqgrid post-creation: A step-by-step

Can't apply column template with Free jqgrid once it's been created. I've attempted: var newOrderPriceTemplate = { align: "center", formatter: "showlink", formatoptions: { onClick: function() { alert('clicked&apos ...

What steps are required to transform a TypeScript class with decorators into a proper Vue component?

When I inquire about the inner workings of vue-class-component, it seems that my question is often deemed too broad. Despite examining the source code, I still struggle to grasp its functionality and feel the need to simplify my understanding. Consider th ...

Is it possible to modify the dropdown menu so that it opens on the right side instead of using a select tag?

Is there a way to make the drop-down from the select tag open to the right side(drop-right)? <label for="ExpLabel">Select Message Expiry:</label> <select name="ExpSelect" id="Expiry"> <option value="ExpiryDate">1 Day</opt ...

Set the values retrieved from the http get response as variables in an Angular application

Lately, I've been working on a settings application with slide toggles. Currently, I have set up local storage to store the toggle state. However, I now want to update the toggle status based on the server response. The goal is to toggle buttons accor ...

The JSON.stringify() function helps to convert data into a JSON-formatted string, avoiding any potential "c

connection.query( 'SELECT DeskName FROM desks WHERE stat = ?',["Booked"], function(err, rows){ if(err) { throw err; }else{ try{ var dataToParse = new Array(); dataToParse = rows; res.render('workspaces.html',{parsedArray : JS ...

The Hidden Power of jQuery: Unleashing the Full Potential of .text()

I'm trying to make two values equal, but it's not working... var rowID = $("#@idSelectObjectGuid").val(); $($(".ls-grid-body tr").children("<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="240a64524770454648410a74564d ...

Steps to deploy an ASP.NET Angular application that has been published

I am working on an Angular application that is being managed by an ASP.NET application following a similar setup as described in this tutorial. During development, I usually use the command dotnet run from within the ASP.NET project directory to build both ...

Error encountered while transitioning to Angular 6: "Declining to remove"..."lies beyond"..."and lacks a hyperlink"

As a newbie developer, I am currently working on my first app. Initially, I used Angular 5.2 to build it and now I'm attempting to upgrade to Angular 6. Following the guidelines provided at https://update.angular.io/, I executed the following command ...

What is the best way to establish a default search query within the vue-multiselect component?

I have incorporated vue-multiselect into my project. You can find more information about it here. This is a snippet of my template structure: <multiselect v-model="value" :options="options" searchable="true"></multiselect> When I open the mu ...

Tips for declaring the project npm registry within the package.json configuration file

Currently, I am juggling multiple projects simultaneously and facing the issue of each project having a different node module registry. For instance, project A sources its modules from http://registroy.foo.com, while project B pulls modules from http://re ...

Steps for displaying a bootstrap modal in alignment with the triggering button's position

Recently diving into the world of bootstrap and AngularJs has been quite the learning experience for me. One challenge I encountered was trying to implement a bootstrap modal dialog box to show additional details within a table column. My goal was to hav ...

Tips on saving every query outcome in a separate array and delivering it back to the controller upon completion

I am currently facing an issue where I receive data in a function from my controller, and inside my model function, I need to retrieve results using a query with a dynamic value of channel. The channel ID will be coming from each checkbox on my HTML view ...