7

I have created dynamic component instances by selecting pre-existing components. For example,

@Component({

    selector: 'dynamic-component',
    template: `<div #container><ng-content></ng-content></div>`

})
export class DynamicComponent {
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef; 

    public addComponent(ngItem: Type<WidgetComponent>,selectedPlugin:Plugin): WidgetComponent {
    let factory = this.compFactoryResolver.resolveComponentFactory(ngItem);
    const ref = this.container.createComponent(factory);
    const newItem: WidgetComponent = ref.instance;
    newItem.pluginId =  Math.random() + ''; 
    newItem.plugin =   selectedPlugin;
    this._elements.push(newItem);                 
    return newItem;
  }
}

My pre-existed components are ChartWidget and PatientWidget which extended the class WidgetComponent that I wanted to add in the container. For example,

@Component({
selector: 'chart-widget',
templateUrl: 'chart-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() => ChartWidget) }]
})

export class ChartWidget extends WidgetComponent implements OnInit {
       constructor(ngEl: ElementRef, renderer: Renderer) {
    super(ngEl, renderer);
    }
    ngOnInit() {}
     close(){
      console.log('close');
    }
    refresh(){
      console.log('refresh');
    }
    ...
}

chart-widget.compoment.html (using primeng Panel)

<p-panel [style]="{'margin-bottom':'20px'}">
    <p-header>
        <div class="ui-helper-clearfix">
           <span class="ui-panel-title" style="font-size:14px;display:inline-block;margin-top:2px">Chart Widget</span>
            <div class="ui-toolbar-group-right">                
               <button pButton type="button" icon="fa-window-minimize" (click)="minimize()"</button>
              <button pButton type="button" icon="fa-refresh" (click)="refresh()"></button>
              <button pButton type="button"  icon="fa-expand" (click)="expand()" ></button>
             <button pButton type="button" (click)="close()" icon="fa-window-close"></button>
                    </div>
                </div>
    </p-header>
      some data
</p-panel>

data-widget.compoment.html (same as chart-widget using primeng Panel)

@Component({
selector: 'data-widget',
templateUrl: 'data-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() =>DataWidget) }]
})

export class DataWidget extends WidgetComponent implements OnInit {
       constructor(ngEl: ElementRef, renderer: Renderer) {
    super(ngEl, renderer);
    }
    ngOnInit() {}
    close(){
      console.log('close');
    }
    refresh(){
      console.log('refresh');
    }
    ...
}

WidgetComponent.ts

@Component({
  selector: 'widget',
  template: '<ng-content></ng-content>'
})
export  class WidgetComponent{
}

Now I added the components by selecting a component from the existed components (e.g. chart-widget and data-widget) in the following way and stored the instances into an array.

@Component({
templateUrl: 'main.component.html',
entryComponents: [ChartWidget,  DataWidget], 
})
export class MainComponent implements OnInit {
private elements: Array<WidgetComponent>=[];
 private WidgetClasses = {
    'ChartWidget': ChartWidget,
    'DataWidget': DataWidget        
}
@ViewChild(DynamicComponent) dynamicComponent: DynamicComponent;  

 addComponent(): void{                         
   let ref= this.dynamicComponent.addComponent(this.WidgetClasses[this.selectedComponent], this.selectedComponent);    
   this.elements.push(ref); 

   this.dynamicComponent.resetContainer();                     
}
}

Now, I am facing problem to render the components using innerHtml in main.component.html. It render the html but I am not able to use button click event or other event on it. I have also tried to render chart using primeng but its also not working.

main.component.html

 <dynamic-component [hidden]="true" ></dynamic-component>                           
 <widget *ngFor="let item of elements">
     <div [innerHTML]="item._ngEl.nativeElement.innerHTML | sanitizeHtml">
     </div>
 </widget>

I have also implemented a sanitizeHtml Pipe but its giving still same result. So, as I understand innerHTML is only showing the html data but I can't use any button event as well as the js chart. I have also tried to show the items like this {{item}} under tag. But it display like a text [object object]. So, could anyone give a solution for it? How can I render the components allowing the button events and js chart? Thanks.

EDIT: See my Plunker here https://plnkr.co/edit/lugU2pPsSBd3XhPHiUP1?p=preview You can see here, it is possible to add chart or data widget dynamically and I am showing it using innerHTML. So, the button events are not working here. If I coding like {{item}} then it shows [object object] text. You can also see in console the component array data. The main Question is, How can I active the button events on it (e.g. if i click close or refresh button then it will call the related functions)?

4
  • Plunker would be helpful Commented Apr 19, 2017 at 15:10
  • Html created via innerHTML doesn't support angular bindings Commented Apr 19, 2017 at 15:13
  • @yurzui yes. thats why I used sanitizeHtml pipe but getting same result. I tried also {{item}} in <div> tag. But its showing [object object] like text. Commented Apr 19, 2017 at 15:23
  • Hi @yurzui, I give my Plunker link. Could you please check it and give me a solution for it. Thanks. plnkr.co/edit/lugU2pPsSBd3XhPHiUP1?p=preview Commented Apr 20, 2017 at 9:54

1 Answer 1

4

I would create structural directive like:

view.directive.ts

import { ViewRef, Directive, Input, ViewContainerRef } from '@angular/core';

@Directive({
    selector: '[view]'
})
export class ViewDirective {
  constructor(private vcRef: ViewContainerRef) {}

  @Input()
  set view(view: ViewRef) {
    this.vcRef.clear();
    this.vcRef.insert(view);
  }

  ngOnDestroy() {
    this.vcRef.clear()
  }
}

then

app.component.ts

private elements: Array<{ view: ViewRef, component: WidgetComponent}> = [];

...
addComponent(widget: string ): void{
  let component = this.dynamicComponent.addComponent(this.WidgetClasses[widget]);
  let view: ViewRef = this.dynamicComponent.container.detach(0);
  this.elements.push({view,component});

  this.dynamicComponent.resetContainer();
}

and

app.component.html

<widget *ngFor="let item of elements">
  <ng-container *view="item.view"></ng-container>
</widget>

So i have just moved view from dynamic component container to desired place.

Plunker Example

Sign up to request clarification or add additional context in comments.

8 Comments

Hi @yurzui, Thanks for your support. Its working for me. I little bit modify the code with drag and drop functionality with ng2-dragular. I use two arrays and want to drag and drop from one container to another and finally want to save both array values. But the problem is, when I drag from one to another container it gives an error like "Cannot read property 'parent' of undefined". please see my update Plunker plnkr.co/edit/h7h0GXxhRwFTQMaHXy9t?p=preview. Do you have any solution for it? thanks a lot.
Seems it works with my version angular :) plnkr.co/edit/ohhZAHXjKvcNJEVXkXpP?p=preview
great :) . Thanks a lot. Its working nicely. So, I have to switch in angular 4. Is there any solution for angular2? I have already done several tasks of my project with angular2 version. So, I would like keep continue with angular2 at this moment. It would be great for me to have some solution for angular2 version :) Thanks.
Hi @yurzui, I've updated my code from angular2 to angular4. But I traced something wrong it the code. angular4 don't show the error "Cannot read property 'parent' of undefined" but the error still exist their. You can see in your code, after drag and drop it lost its view event and the mouse event won't work on that case. Just test it one or more time then you can identify the problem :). it would be nice to get help regarding the problem. thanks.
It works one side, when I tried to drag and drop it back to the previous position then it shows again the same error.
|

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.