4

I have created a dynamic component in Angular 6 with componentFactoryResolver, and I need to update one of its properties every 3 seconds.

const componentFactory = this.cfr.resolveComponentFactory(componentType);
const component: ComponentRef<any> = vcr.createComponent(componentFactory);

component.instance.property = this.newData;

    setInterval(() => {
      this.loadNewData().then((data) => {
        this.newData = data;
      });
    }, 3000);

The property is not getting updated. I found out that dynamically generated components do not support property binding. So, how can I update the property with the new data? Is there any workaround for this?

Emanuel T
  • 53
  • 1
  • 5

4 Answers4

1

A bit late but I found your question interesting... :)

I made a Stackblitz example: https://stackblitz.com/edit/angular-3gvrrz

As I wasn't completely sure about your requirements I made the example using @Input() and using a BehaviorSubject in a shared service.

export class AppComponent implements OnInit {
  name = 'Angular';
  @ViewChild(ContainerDirective, { static: true }) container: ContainerDirective;
  DATA = ['Angular', 'Material', 'Nativescript', 'Ionic'];
  index = 0;

  constructor(private componentFactoryResolver: ComponentFactoryResolver,
    private commonService: CommonService) {}

  ngOnInit() {
    const viewContainerRef = this.container.viewContainerRef;
    viewContainerRef.clear();
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(HelloComponent);
    const componentRef = viewContainerRef.createComponent(componentFactory);
    (<HelloComponent>componentRef.instance).name = 'Starting...';

    setInterval(() => {
      if (this.index > this.DATA.length - 1) {
        this.index = 0;
      }
      const nextName: string = this.DATA[this.index++];
      (<HelloComponent>componentRef.instance).name = nextName;
      this.commonService.setName(nextName)
    }, 3000);
  }
}
Kari F.
  • 1,214
  • 10
  • 16
1

Use ng-interconnect for communication in these types of use cases.

  1. Create a broadcaster in your parent component

     let broadcaster = interconnect.createBroadcaster('home-messgae');   
    
  2. Receive from the broadcaster from inside the dynamic component

     interconnect.receiveFrom('home-message', '', (data) => {
        console.log('do something with ' + data);
     );
    
  3. Start emitting from the parent component

    broadcaster.emit('hello dynamic child');
    

Article: https://charlie-code.medium.com/simplify-your-angular-development-using-ng-interconnect-1b94c68e9141

Charlie
  • 22,886
  • 11
  • 59
  • 90
0

Using your code example. You create a component factory as follows

const componentFactory = this.cfr.resolveComponentFactory(componentType);

and then using the view container reference you create your component reference like this:

this.componentRef = vcr.createComponent(componentFactory);

You should be able to use this component reference to update your current component. By doing something like this: (this.componentRef.instance).value = this.value inside your setInterval function. In your code it would look like this:

setInterval(() => {
  this.loadNewData().then((data) => {
    (this.componentRef.instance).newData = data;
  });
}, 3000);

If you place an @Input() newData: any; in your component that must receive the new data then your component should receive the data. Hopefully that is helpful and fully answers your question.

0

https://github.com/BhadraAnirban/dynamic_component_to_HTMLElement/blob/main/README.md

We will pass data to the component properties.

It will also work if you are using [innerHTML] to render the html content coming from Server.

<div [innerHtml]="templateString"></div>

Create a Component (here I have created - SignatureControlComponent) and add it to the entryComponent of module.

@Component({
  selector: 'app-signature-control',
  templateUrl: './signature-control.component.html',
  styleUrls: ['./signature-control.component.scss']
})
export class SignatureControlComponent implements OnInit {
role: string;
}

In the app.nmodule-

entryComponents: [SignatureControlComponent]

In the main component (I have used EditTemplateComponent) where you are going to create the component and add to the HTML element.

Inject ComponentFactoryResolver, ViewContainerRef, Renderer2 using the constructor.

constructor( 
  private elem: ElementRef, private viewContainerRef: ViewContainerRef,
  private componentFactoryResolver: ComponentFactoryResolver, private renderer: Renderer2) { }

Use ngAfterViewChecked life cycle hook to get HTML element using class (I have used the classname 'Signature'). This will also work [innerHTML] to render the html content coming from Server.

ngAfterViewChecked() {
    if(!this.isElementsRendered)
    {
      
      let elements = this.elem.nativeElement.querySelectorAll('.Signature');
      if(elements.length > 0)
      {
        this.isElementsRendered = true;
        elements.forEach(element => {
          this.CreateSignatureComponent(element);
        })
      }
    }   
  }

Finally create the instance of the component and append to the element.

CreateSignatureComponent(element: HTMLElement){    
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SignatureControlComponent);    
    let componentInstance = this.viewContainerRef.createComponent(componentFactory);
    const loaderComponentElement = componentInstance.location.nativeElement;
  // Assign value to the property
    componentInstance.instance.role = 'PMKJ!';

    this.renderer.appendChild(element, loaderComponentElement);
  }

Anirban Bhadra
  • 221
  • 3
  • 5