7

I have dynamically added a component on the click of a button .

Below is the code for my widget . Simple div with color property being set as input.

Widget.tmpl.html

 div class="{{color}}" (click)="ChangeColor()"

In the Widget component i am taking color as my input . This component works fine when i add it manually . But now i am trying to dynamically add the component and also need to pass the Color Value to the Widget Component.

Below is the code in the app.component.ts where i am calling addItem() on the button click .

app.component.ts

export class AppComponent  {
  @ViewChild('placeholder', {read: ViewContainerRef}) viewContainerRef;
  private componentFactory: ComponentFactory<any>;

  constructor(componentFactoryResolver: ComponentFactoryResolver, compiler: Compiler) {
    this.componentFactory = componentFactoryResolver.resolveComponentFactory(MyAppComponent);

  }

  addItem () {
   this.viewContainerRef.createComponent(this.componentFactory, 0);
  }

 public  myValue:string = 'red';

 onChange(val: any) { this.myValue = val; } }

In the addItem() method i am dynamically adding my widget component to my view. The component gets added fine . But the problem is how to pass the color property when dynamically adding . Based on what color i pass when creating the widget i want it to be displayed in red or green etc. How to property bind in this scenario?

Here is some of the Code :

export class MyAppComponent { 

    @Input() color; 
    @Output('changes') result: EventEmitter<any> = new EventEmitter(); 

    public constructor() { 
    }

    ChangeColor() {
        this.ToggleColor();
        this.result.emit(this.color);// Emitting the color to the parent.        
    }

    ToggleColor() {
        if (this.color == "red")
            this.color = "blue";
        else
            this.color = "red";
    }
}

In the above code i am emitting my color to the parent app.component.ts but since i have dynamically added the widget component , i dont know where to add this code (changes)="onChange($event)". I tried to add this code in the div as shown below :

<div class="{{color}}" (click)="ChangeColor()" (changes)="onChange($event)"></div>

But it does not work.

3 Answers 3

15
var cmpRef = this.viewContainerRef.createComponent(this.componentFactory, 0);
cmpRef.instance.someProp = 'someValue';
cmpRef.instance.someObservable.subscribe(val => this.someProp = val);
Sign up to request clarification or add additional context in comments.

12 Comments

Also when i try to emit a event from my widget component using @Output . it is not firing . I have binded my emit event to the div of my widget : <div class="{{color}}" (click)="ChangeColor()" (changes)="onChange($event)"> </div> below is the code for widget.component.ts.
Can you please edit your question and add the code there? Where is the HTML above? Is this in the view of the dynamically added component? Please add more code to make it obvious what is happening where.
Please edit the question and add the code there. Code in comments is unreadable.
(someEvent) doesn't listen on any event. The event has to be emitted by the <div> but it's emitted by AppComponent. Events emitted by @Outputs() also don't bubble.
that array is unneeded duplicity since the ViewContainerRef maintains order of viewRef's. The only value I see in the array approach is that it is a more accurate read on length since printing to the DOM takes more time than pushing onto an array. Just submitted github.com/angular/angular/issues/11223 Fingers crossed. Upvote it if you can. Thank you as always for your help Günter
|
0

I will put this article if you can't find it in google https://netbasal.com/dynamically-creating-components-with-angular-a7346f4a982d It explains perfectly how to pass data, It helps me a lot to understand the resolveComponentFactory, Sorry for the answer is more like a comment but I do not have enough reputation.

1 Comment

It's great that you linked an article, however, links don't last forever. Please paste the relevant parts fo the article into your post just in case the link dies.
0

I have faced with the same issue: component processes input data and creates own appearance based on this data. The only one issue was adding component as regular one with html template (and predefined input data) and dynamically with componentFactory and setting data after ngInit angular event. It broke all initialization component logic due to triggering ngInit event before setting input data for component. One possible solution might be adding flexible component initialization based on allow initialization flag, that is set before adding this component and after settings input data.

 initComponentData() {
        const canInitData = !this.appService.deniedInitData.value;
        if (canInitData) {
            this.initData();
        } else {

            this.subscriptions.push(
                this.appService.deniedInitData.pipe(filter(x => !x),take(1)).subscribe(() => {
                    this.initData();
                }));
        }
    }

appService - Injectable angular service with

deniedInitData: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

Place when we are dynamically adding component:

this.appService.deniedInitData.next(true);
const cmpRef = this.viewContainerRef.createComponent(this.componentFactory, 0);

const instance = cmpRef.instance;
instance.someProp = 'someValue';                    
this.appService.deniedInitData.next(false);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.