postMessage в iframe, созданный в angular * ngFor не работает - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть приложение Angular, где мне нужно иметь возможность загружать несколько iframe.Веб-страницы, которые я загружаю в iframes, также создаются с использованием 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 $ является наблюдаемым для данных пожарного хранилища.Данные принимаются в iframe другим приложением Angular, в которое добавляется прослушиватель событий, например:

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> 

и отправить сообщение, как указано выше, я никогда не получаю никаких сообщений.Почему это и как я могу это исправить?

ОБНОВЛЕНИЕ

По предложению pr @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 голосов
/ 14 февраля 2019

Думаю, я понял, по крайней мере, это решение работает в Chrome, Firefox и Safari на Mac.

Мой 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...