iframe inside angular2 component, Property ‘contentWindow’ does not exist on type ‘HTMLElement’
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.
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();
}
}
use this:
let iframe = document.getElementById('iframe') as HTMLIFrameElement
I solved the problem in the following way:
const element: HTMLIFrameElement = document.getElementById('iframe') as HTMLIFrameElement;
const iframe = element.contentWindow;
if (iframe !== null) {
...
}
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.
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();
}
}
I solved the problem in the following way:
var ID = document.getElementById("Iframe");
var Iframe = eval("(ID.contentWindow || ID.contentDocument)");
Iframe.CallIframeFunction();
Or use the now famous Typescript work around:
iframe['contentWindow']