在有角的* ngFor中创建到iframe的postMessage不起作用

时间:2019-02-12 12:41:16

标签: angular iframe

我有一个Angular应用程序,需要在其中加载多个iframe。我在iframe中加载的网页也是使用Angular构建的。如果我这样写HTML

<div id="frame">
    <div>
        <iframe id='iframeId0' [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
    <div>
        <iframe id='iframeId1' [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
    <div>
        <iframe id='iframeId2' [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
    <div>
        <iframe id='iframeId3' [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
    <div>
        <iframe id='iframeId4' [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
</div>

我可以使用postMessage这样将数据发送到iframe:

ngOnInit() {
   for (let i = 0; i < this.numberOfIframes; i++) {
      this.iframeData.push({ url: '', iframeNumber: i });
   }
   this.dbService.dbData$.subscribe(dbData => {const message: PmData = {
      command: 'url
      payload: url,
   }
   const win = window.frames[this.pdfFrame];
   win.postMessage(JSON.stringify(message), environment.docViewerUrl);
可以观察到

dbData $来存储数据。 数据是通过另一个Angular应用程序在iframe中接收的,其中添加了事件侦听器,如下所示:

ngOnInit() {
    window.addEventListener("message", async (event) => {
       console.log('called from', event.origin);

它工作正常。

如果我这样使用* ngFor:

<div *ngFor="let frame of iframeData">
    <iframe [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
</div> 

并像上面一样发送postMessage,我从没收到任何消息。为什么会这样,我该如何解决?

更新

作为@ConnorsFan建议,我尝试了以下操作:

<div id="frame">
<div #iframeContainer *ngFor="let iframe of iframeData">
    <div [hidden]="pdfFrame !== 0">
        <iframe [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
    </div>
</div>

export class OneComponent implements OnInit, AfterViewInit {
@ViewChildren('iframeContainer') iframeContainers: QueryList<ElementRef>;

sanitizedURL: SafeResourceUrl;
pdfFrame = 0;
docIdx = 0;
iFrameHeight = '';
numberOfIframes = 5;
dbData: DbData;
pageNumber = 1;
pdfLoading = false;
iframeData: IframeData[] = [];


constructor(
    private sanitizer: DomSanitizer,
    private dbService: DbService
) { }

ngOnInit() {
    console.log('one ngOnInit');

    for (let i = 0; i < this.numberOfIframes; i++) {
        this.iframeData.push({ url: '', iframeNumber: i });
    }

    const docId = document.getElementById('frame'); // I can't get height=100% to work in the HTML
    this.iFrameHeight = `${docId.clientHeight - 20}px`;

    this.sanitizedURL = this.sanitizer.bypassSecurityTrustResourceUrl(environment.docViewerURL);

}

async ngAfterViewInit() {

    this.dbData = await this.dbService.dbData$.pipe(first()).toPromise();

    //await this.iframeContainers.changes.pipe(first()).toPromise();

    this.iframeContainers.changes.subscribe((list: QueryList<ElementRef>) => {
        console.log('iframeContainers.changes', list);

    });
    this.handleLoadedDocs(this.dbData.docs[this.pdfFrame].downloadURL);

    this.dbService.dbData$.subscribe(dbData => this.dbData = dbData);

    window.addEventListener('message', async (event) => {
        if (typeof event.data === 'string') {
            const responce = JSON.parse(event.data);
            if (responce.command === 'PdfLoaded') {
                this.pdfLoading = false;
            }
        }
    });
}

private handleLoadedDocs(url: string) {
    if (this.pdfLoading) return;

    const idx = this.iframeData.findIndex(data => data.url === url);
    if (idx >= 0) {
        // URL is already loaded, make it active
        this.pdfFrame = this.iframeData[idx].iframeNumber;
        // and move it to the top array position
        const topIframe = this.iframeData.splice(idx, 1);
        this.iframeData.push(topIframe[0]);
    } else {
        const oldIframe = this.iframeData.shift();
        this.iframeData.push({ url: url, iframeNumber: oldIframe.iframeNumber });
        this.pdfFrame = oldIframe.iframeNumber;

        // send URL to doc-viewer
        this.sendCommandToPdfViewer(PdfViewerCommand.URL, url);
    }
    console.log('this.iframeData', this.iframeData);
}

private sendCommandToPdfViewer(command: PdfViewerCommand, payload: any) {
    const message = {
        command,
        payload,
    }
    const win = window.frames[this.pdfFrame];
    win.postMessage(message, environment.docViewerURL);
}

使用此代码,我在日志中获得以下输出:

QueryList {dirty: false, _results: Array(5), changes: EventEmitter, length: 5, last: ElementRef, …}
    changes: EventEmitter {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
    dirty: false
    first: ElementRef {nativeElement: div}
    last: ElementRef {nativeElement: div}
    length: 5
    _results: (5) [ElementRef, ElementRef, ElementRef, ElementRef, ElementRef]
    __proto__: Object

,但是如果我移动this.handleLoadedDocs(this.dbData.docs[this.pdfFrame].downloadURL); 进入iframeContainers订阅(这是我想我需要做的吗?),日志中什么也没有。因此,看起来我必须发送一些内容才能发出iframeContainers订阅。那我想念什么?

1 个答案:

答案 0 :(得分:0)

我想我已经知道了,至少该解决方案可以在Mac上的Chrome,Firefox和Safari中使用。

我的HTML:

<div id="frame">
    <div  *ngFor="let dummy of ' '.repeat(numberOfIframes).split(''); let i = index">
        <div [hidden]="activeFrame !== i">
            <iframe #iframeContainer [src]=sanitizedURL [height]="iFrameHeight" width="100%" frameborder="0"></iframe>
        </div>
    </div>
</div>

在.ts文件中:

export class OneComponent implements OnInit, AfterViewInit {
   @ViewChildren('iframeContainer') iframeContainers: QueryList<ElementRef>;
   ...
   ngAfterViewInit() {

        this.iframeContainers.forEach(_iframe => {
            this.iframeRefs.push(_iframe);
        });

然后我可以与每个iframe通信:

const win = this.iframeRefs[iframeIdx].nativeElement.contentWindow;
win.postMessage(message, environment.docViewerURL);