baris usanmaz
baris usanmaz

Reputation: 849

Angular2 - Issue getting iframe.contentWindow successfully and error calling postMessage()

I have an Angular 2 project and a NodeJs project. I have an iframe in Angular 2 app and I want to show NodeJS app in it. I would like to use postMessage() method from Angular2 to NodeJs and then reverse(from NodeJs to Angular2). Angular2 Address is http://localhost:3001 and NodeJs address is http://localhost:3005.

In Angular 2 I have a template in component like this;

template: `<iframe id="ifrm" #ifrm [src]="iframeURL()" width="500" height="200"> <p> Your browser does not support iframes</p> </iframe> `


iframeURL() method body used in template;

iframeURL() {
 return this.sanitizer.bypassSecurityTrustResourceUrl('http://localhost:3005');
}


When I run the application I can see the page in iframe in Angular2. But when I want to get contentWindow of the iframe (code below), I get the explanation below (not error);

@ViewChild('ifrm') iframe: ElementRef;

Exception: DOMException: Blocked a frame with origin "http://localhost:3001" from accessing a cross-origin frame.


When I use postMessage() method like below I get the exception;

this.iframe.nativeElement.contentWindow.postMessage('{}','http://localhost:3005');

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://localhost:3005') does not match the recipient window's origin ('http://localhost:3001').


By the way I am opening this component page using angular routing. Whole component code is below:

import {Component, OnInit, ElementRef} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser'
import {ViewChild} from "@angular/core/src/metadata/di";

@Component({
    selector: 'app',
    template: `<div> <iframe id="ifrm" #ifrm [src]="iframeURL()" width="500" height="200" style="/*display:none;*/"> <p> Your browser does not support iframes</p> </iframe> </div>`
})
export class AppComponent implements OnInit{
    constructor(private sanitizer: DomSanitizer){}

    @ViewChild('ifrm') iframe: ElementRef;

    ngOnInit(){
        console.log(this.iframe.nativeElement.contentWindow);
        this.iframe.nativeElement.contentWindow.postMessage('{}', 'http://localhost:3005');
    }

    iframeURL() {
        return this.sanitizer.bypassSecurityTrustResourceUrl('http://localhost:3005');
    }
}

Upvotes: 2

Views: 9131

Answers (2)

Hieu Nguyen
Hieu Nguyen

Reputation: 511

For simplifying code, you can use Renderer2 of Angular and you can use tag name instead of ElementRef to get the iframe element.

import { Component, Renderer2, AfterViewInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  template: `<iframe [src]="iframeURL" #iframeTagName (load)="loadIframe(iframeTagName)" style="visibility: hidden; position: absolute; left: 0; top: 0; height:0; width:0; border: none;"><iframe>`
})
export class AppComponent implements AfterViewInit {
  iframeURL;
  private isInited;

  constructor(
    private domSanitizer: DomSanitizer,
    private renderer: Renderer2
  ) {
    this.iframeURL= this.domSanitizer.bypassSecurityTrustResourceUrl('http://iframe-url.com');
  }

  loadIframe(iframeTagName) {
    if (this.isInited) {

      // post message to iframe
      iframeTagName.contentWindow.postMessage('message' , 'http://iframe-url.com');

      // You can receive response from iframe if any
      this.renderer.listen('window', 'message', (event) => {

        if (event.origin !== 'http://iframe-url.com') {
          return;
        }

        // handle event here

     });
   }

   ngAfterViewInit() {
      this.isInited = true;
   }
}

Upvotes: 0

gocam
gocam

Reputation: 93

This is how i handle the problem. only postMessage after ngAfterViewInit. Though it may not work for you, you can try it:

<iframe #iframe class="iframe-map" (load)="onIframeLoad()"></iframe>
import { Component, AfterViewInit, ChangeDetectionStrategy, ViewChild, ElementRef, Inject, Optional, Renderer } from '@angular/core';
import { URLSearchParams } from '@angular/http';
import { DialogRef, ModalComponent } from 'angular2-modal';
import { BSModalContext } from 'angular2-modal/plugins/bootstrap';
import { stringify } from 'querystringify';

import { AmapPickerOptions, AmapLocation, AMAP_PICKER_OPTIONS, AMAP_KEY, stringify as amapstringify } from '../amap';

export class AmapPickerModalData extends BSModalContext {
  public center: string;
}

@Component({
  selector: 'amap-picker-modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AmapPickerModalComponent implements ModalComponent<AmapPickerModalData>, AfterViewInit {

  @ViewChild('iframe') iframe: ElementRef;

  receiveMessage: EventListener;

  private isInited: boolean;

  constructor(
    @Inject(AMAP_KEY) private key: string,
    @Optional() @Inject(AMAP_PICKER_OPTIONS) private options: AmapPickerOptions,
    private renderer: Renderer,
    public dialog: DialogRef<AmapPickerModalData>) { }

  ngOnInit() {
    let center = this.dialog.context.center;
    this.options = Object.assign({ key: this.key }, this.options, center && { center });
    let query = stringify(this.options, true);
    this.renderer.setElementProperty(this.iframe.nativeElement, 'src', `https://m.amap.com/picker/${query}`)

    this.receiveMessage = (event: MessageEvent) => {
      if (event.origin !== 'https://m.amap.com') {
        return;
      }
      this.dialog.close(amapstringify(<AmapLocation>event.data));
    };
  }

      ngAfterViewInit() {
        this.isInited = true;
      }

  ngOnDestroy() {
    window.removeEventListener('message', this.receiveMessage);
  }

  onIframeLoad() {
    if (this.isInited) {
      setTimeout(() => {
        this.iframe.nativeElement.contentWindow.postMessage('hello', 'https://m.amap.com/picker/');
        window.addEventListener('message', this.receiveMessage, false);
      }, 500);
    }
  }

}

Upvotes: 6

Related Questions