5

My controller has a list of components I want to have inside a page. Those other components are created and work correctly in isolation.

In my HTML template:

<ng-container *ngFor="let component of components"> <div {{component.name}}></div> </ng-container> 

The code should output a <div> like this:

<div my-component></div> 

But it is not and I am getting this error:

DOMException: Failed to execute 'setAttribute' on 'Element' 

It works well if I simply print the value inside the div tag:

<div>{{component.name}}</div> 

Is it even possible to achieve what I want?

4
  • How about generating a custom components tags like: <{{component.name}}></{{component.name}}> Commented Mar 6, 2017 at 8:19
  • <div {{component.name}}=""></div> try this? Commented Mar 6, 2017 at 8:25
  • @Jai, thanks but none of them worked. Commented Mar 6, 2017 at 8:29
  • For reference, <div my-component></div> placed just above the ng-container works well Commented Mar 6, 2017 at 8:39

1 Answer 1

4

You can create a component factory and put those components that you need as an entry component:

Component Factory View

<div #dynamicComponentContainer></div> 

Component Factory Controller

import { Component,Input,ViewContainerRef,ViewChild,ReflectiveInjector,ComponentFactoryResolver } from '@angular/core'; import { YourComponent } from './your-component.component'; @Component({ selector: 'component-factory', templateUrl: './component-factory.component.html', styleUrls: ['./component-factory.component.css'], entryComponents: [ YourComponent ] }) export class ComponentFactoryComponent { currentComponent = null; @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef; // component: Class for the component you want to create // inputs: An object with key/value pairs mapped to input name/input value @Input() set componentData (data: {component: any, inputs: any }) {+ if (!data) { return; } // Give you access to the inputs inside the component let inputProviders = Object.keys(data.inputs).map((inputName) => {return {provide: inputName, useValue: data.inputs[inputName]};}); let resolvedInputs = ReflectiveInjector.resolve(inputProviders); // This will prepare the inject of inputs on the factory let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector); // Creates a factory let factory = this.resolver.resolveComponentFactory(data.component); // Here we inject the inputs on the factory let component = factory.create(injector); // This will add the component in the DOM container this.dynamicComponentContainer.insert(component.hostView); // This destroys the old component and input the new one if (this.currentComponent) { this.currentComponent.destroy();+ } this.currentComponent = component; } constructor(private resolver: ComponentFactoryResolver) { } 

On the component you want to use you'll need to provide the inputs, we do this by the Injector:

Component You Want to Use

import { Component, Input, Injector } from '@angular/core'; @Component({ selector: 'your-component, [your-component]', templateUrl: './your-component.component.html', styleUrls: ['./your-component.component.css'] }) export class YourComponent { @Input() name: string; @Input() label: string; @Input() value: string; constructor(private injector: Injector) { this.name = this.injector.get('name'); this.label = this.injector.get('label'); this.value = this.injector.get('value'); } } 

Then on your app component, or whenever you want to use it:

App Component View

<component-factory [componentData]="componentData"></component-factory> 

App Component Controller

import { Component } from '@angular/core'; import {YourComponent} from './your-component.component' @Component({ selector: 'app', templateUrl: './app.html', styleUrls: ['./app.component.css'] }) export class AppComponent { componentData = null; constructor() { } ngOnInit() { this.componentData = { component: YourComponent, inputs: { name: 'example', label: 'John Doe', value: 'foo' } } } } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.