4

I have a new Angular app (can use v 2.4.10 or 4.0.0-rc.5), which uses an iframe to embed some pre-existing content. Using a method called when the iframe loads -- (load)="processIframe(extFrame)" -- I'm able to set up listeners via the Angular Renderer, and to grab references to child DOM elements (this answer helped with that process).

It's easy enough to set the content of an element in the iframe by replacing its innerHtml. However, what I need to do is the replace this html with a compiled Angular component. This component grabs some data from an API via a service.

Is there a way to replace DOM element content in the iframe with the compiled component?

Community
  • 1
  • 1
Fiona
  • 1,211
  • 3
  • 16
  • 20
  • Since you have `ElementRef` you can try [Dynamic Component Loader](https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html) – Sasxa Mar 18 '17 at 14:48
  • Great question, i'm doing something similar and would love to know if this is possible. – crthompson May 24 '17 at 15:44
  • do you need a solution for Angular 2.4.10 or 4.0.0-rc.5 only? Are you ok with a newer version, like 4.1.3? – andreim May 25 '17 at 10:02
  • I don't see how you could add a component you created on your document to another document (iframe) directly. Are you ok with an alternative solution, for example, loading a separate route in your iframe and communicate between the main document and iframe document the intent of loading a component with some specific data, and any changes that may appear? Don't see any other way. – andreim May 25 '17 at 10:08
  • @andreim if you make the `src` in the iframe another route, does it load the app twice? – crthompson May 25 '17 at 14:51
  • @paqogomez yes it does. Another DOM another app instance. But if the app is not loading all resources ahead and it uses async loading (see routing loadChildren property) then is fine. – andreim May 25 '17 at 14:58
  • @andreim I've opted instead to put my code in a guard on the route, this seems to work well for what I'm doing, but is different that OP. If you want to make an answer as best you can, i'll give you the bounty. – crthompson May 25 '17 at 15:24
  • @fiona, for your situation, why not just make a component that loads the data and forego the iframe altogether? – crthompson May 25 '17 at 15:27
  • @fiona doing `Some random content for iframe` and then in template `` will not work because the ` – andreim May 25 '17 at 15:34

1 Answers1

16

You can create ComponentRef instance and then insert its compRef.location.nativeElement in desired place.

I would do it as follows Plunker Example:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="createComponent()">Create component</button>
    <iframe #iframe (load)="onLoad()"></iframe>
  `,
})
export class App implements AfterViewInit, OnDestroy {
  @ViewChild('iframe') iframe: ElementRef;

  doc: any;
  compRef: ComponentRef<DynamicComponent>;

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


  createComponent() {
    const compFactory = this.resolver.resolveComponentFactory(DynamicComponent);
    this.compRef = this.vcRef.createComponent(compFactory);

    this.doc.body.appendChild(this.compRef.location.nativeElement);
  }

  onLoad() {
    this.doc = this.iframe.nativeElement.contentDocument || 
               this.iframe.nativeElement.contentWindow;
  }

  ngAfterViewInit() {
    this.onLoad(); // in Firefox state is uninitialized while 
                   // in Chrome is complete so i use `load` event for Firefox
  }

  ngOnDestroy() {
    if(this.compRef) {
      this.compRef.destroy();
    }
  }
}

Don't forget to add DynamicComponent to `entryComponents array

DinoMyte
  • 8,737
  • 1
  • 19
  • 26
yurzui
  • 205,937
  • 32
  • 433
  • 399