post消息到在角度 *ngFor 中创建的 iframe 不起作用



我有一个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$是firestore数据的可观察对象。数据由另一个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订阅。那么我错过了什么?

我想我已经明白了,至少这个解决方案可以在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);

最新更新