24

I have an iframe inside a angular2 component, and I am trying to change the content of the iframe by accessing the contentWindow.
The iframe should contain a simple button.

My code:

    import { Component } from '@angular/core';
    @Component({
      moduleId: module.id,
      selector: 'component-iframe',
      template: '<iframe id="iframe"></iframe>'
    })
    export class ComponentIframe  {
      constructor() {
        let iframe = document.getElementById('iframe'); 
        let content = '<button id="button" class="button" >My button </button>';
        let doc =  iframe.contentDocument || iframe.contentWindow;
        doc.open();
        doc.write(content);
      doc.close();
    }
   }

If I comment the constructor's code and start the app, it compiles and runs correctly. Then I uncomment and all runs perfectly (the button is present in the iframe).

If I decomment the code then start the app (npm start) I have compilation bugs with the message:

Property 'contentWindow' does not exist on type 'HTMLElement'

.

I also tried the alternative of putting the costructor's code into ngOnInit(), ngAfterContentInit(), ngAfterViewInit() but the error persists.

limonik
  • 499
  • 1
  • 6
  • 28
gabriela
  • 285
  • 1
  • 2
  • 12

7 Answers7

31

The template doesn't exist in the DOM yet when the constructor is executed

Use instead

import { Component, ViewChild, ElementRef } from '@angular/core';
@Component({
  moduleId: module.id,
  selector: 'component-iframe',
  template: '<iframe #iframe></iframe>'
})
export class ComponentIframe  {
  @ViewChild('iframe') iframe: ElementRef;

  ngAfterViewInit() {
    let content = '<button id="button" class="button" >My button </button>';
    let doc =  this.iframe.nativeElement.contentDocument || this.iframe.nativeElement.contentWindow;
    doc.open();
    doc.write(content);
    doc.close();
  }
}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • What about when the content is dynamic where the user will write his own `html template` then I want to show a **preview** to him. Angular is not letting rendering the style and Can't find a safe way to do it :\ – PlayHardGoPro Jan 19 '23 at 15:27
  • 1
    Look for "dynamic components". This is somewhat advanced in Angular2 because templates are processed at build time. Angular.js did it on runtime, which made this use case easy. – Günter Zöchbauer Jan 19 '23 at 20:48
29

use this:

let iframe = document.getElementById('iframe') as HTMLIFrameElement
Eldos Narbay
  • 319
  • 4
  • 12
5

I solved the problem in the following way:

const element: HTMLIFrameElement = document.getElementById('iframe') as HTMLIFrameElement;
const iframe = element.contentWindow;
if (iframe !== null) {
  ...
}
Pablo
  • 586
  • 5
  • 14
3

If the content of the IFRAME is created by the same origin then I would suggest to use the IFRAME attribute srcDoc to set and change content in IFRAME.

@Component({
  selector: 'my-app',
  template: `
    <p><label for="text">Write content here...</label></p>
    <textarea 
        #text
        rows="10" 
        cols="47" 
        placeholder="Write some HTML content here..." 
        [(ngModel)]="srcDocContent"></textarea>

    <p>Preview HTML content in IFRAME</p>
    <iframe 
        sandbox="allow-same-origin" 
        [attr.srcDoc]="srcDocContent"></iframe>
  `
})
export class App {

  srcDocContent:string

  constructor() {
    this.srcDocContent='Some <strong>HTML</strong> content here...'
  }
}

See this PLUNKER DEMO

or this Stackblitz DEMO

This will let the native HTML elements untouched to remain compatible with Angular Universal.

Yoraco Gonzales
  • 727
  • 10
  • 18
2

As Günter Zöchbauer has already answered it correctly. I would like to modify it a bit.

The DOM is not ready in Constructor yet but you can find it ready in ngOnInit event though by using { static: true } property something like this:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
@Component({
  moduleId: module.id,
  selector: 'component-iframe',
  template: '<iframe #iframe></iframe>'
})
export class ComponentIframe implements OnInit {
  @ViewChild('iframe', { static: true }) iframe: ElementRef;

  ngOnInit() {
    let content = '<button id="button" class="button" >My button </button>';
    let doc =  this.iframe.nativeElement.contentDocument || this.iframe.nativeElement.contentWindow;
    doc.open();
    doc.write(content);
    doc.close();
  }
}
Jameer Khan
  • 373
  • 2
  • 10
0

I solved the problem in the following way:

 var ID = document.getElementById("Iframe");
 var Iframe = eval("(ID.contentWindow || ID.contentDocument)");
 Iframe.CallIframeFunction();
0

Or use the now famous Typescript work around:

iframe['contentWindow']
SoEzPz
  • 14,958
  • 8
  • 61
  • 64