为啥只有当我从 iframe 调用 postMessage() 方法时我的 Angular 属性才会更新
Posted
技术标签:
【中文标题】为啥只有当我从 iframe 调用 postMessage() 方法时我的 Angular 属性才会更新【英文标题】:Why does my Angular attribute is updated only when I call a postMessage() method from my iframe为什么只有当我从 iframe 调用 postMessage() 方法时我的 Angular 属性才会更新 【发布时间】:2022-01-17 02:48:54 【问题描述】:我有一个名为 counter
的属性,我希望每次单击 iframe 中的按钮时都将其增加 1。
这是我的代码:https://stackblitz.com/edit/angular-fznrnf?file=src/app/app.component.ts
app.component.ts
:
export class AppComponent implements AfterViewInit
counter = 0;
@HostListener('window:message', ['$event']) onPostMessage()
console.log('Message received');
ngAfterViewInit(): void
let iframe = document.getElementById('iframe') as htmlIFrameElement;
iframe?.addEventListener('load', () =>
let iframeWindow = iframe.contentWindow;
if (iframeWindow)
const doc = iframeWindow?.document;
doc.getElementById('iframeButton')?.addEventListener('click', () =>
this.counter++;
window?.postMessage('update ?', '*');
);
);
app.component.html
:
<iframe
id="iframe"
srcdoc="<button id='iframeButton'>Increase my counter !</button>"
></iframe>
<p>Counter = counter </p>
现在,我想了解为什么我需要 window?.postMessage('update ?', '*');
和 @HostListener
来增加我的 counter
,具体来说,真正在幕后发生了什么。
【问题讨论】:
【参考方案1】:postMessage
是一个 Web API,它允许窗口之间的通信。因此,如果您有一个网页和一个使用 postMessage
的 iframe,您可以促进这两个窗口之间的通信。
我玩过你的项目。当您注释掉 window?.postMessage('update ?', '*');
并单击按钮时,AppComponent
上的计数器属性会增加,但 Angular 没有响应更改。
我的猜测是增量是在 Angular 的变更检测系统之外运行的。另一方面,当您还执行postMessage
时,更改检测会正确触发,因为@HostListener('window:message', ['$event'])
会处理此问题。
如果您按如下方式更新组件,它将在AppComponent
中显示counter
属性的增量。
export class AppComponent implements AfterViewInit
counter = 0;
constructor(public cd: ChangeDetectorRef)
@HostListener('window:message', ['$event']) onPostMessage()
console.log('Message received');
ngAfterViewInit(): void
let iframe = document.getElementById('iframe') as HTMLIFrameElement;
iframe?.addEventListener('load', () =>
let iframeWindow = iframe.contentWindow;
if (iframeWindow)
const doc = iframeWindow?.document;
doc.getElementById('iframeButton')?.addEventListener('click', () =>
this.counter++;
this.cd.detectChanges();
// window?.postMessage('update ?', '*');
);
);
调用 this.cd.detectChanges();
将明确要求 Angular 运行更改检测,因此将显示计数器更新。
扩展答案:
将尝试更详细地解释我认为幕后发生的事情。
您的应用程序有两个“上下文”。主窗口,Angular 应用程序在其中运行,然后是 iframe 窗口,它只是纯 HTML。
Per article about change detection你在评论中提到,在主窗口Angular正在修补浏览器API,例如addEventListener
,setTimeout
等,以便能够检测到应该在视图中呈现的变化.
但是 iframe 窗口没有修补它的 API。
因此,如果您单击 iframe 中的按钮以增加 counter
,则视图不会更新(尽管 AppComponent 属性会更新)。您的 iframe 中的单击处理程序未修补以触发 Angular 更改检测和更新视图。
为了让 Angular 知道,视图中的某些内容是从“外部”更新的,我们需要通过 this.cd.detectChanges();
显式调用更改检测。
但是,如果您从 iframe 窗口执行 postMessage
,则会触发主窗口中的消息处理程序 onPostMessage()
。主窗口中的消息处理程序由 Angular 修补,因此当收到消息事件时,更改检测被启动,检测到 counter
增量并更新视图。
【讨论】:
这里的关键字似乎是“角度变化检测系统”,而 ChangeDetectorRef 对此有所帮助,所以谢谢!我发现这篇 Angular 博文 blog.angular-university.io/… 解释了其中的一部分。所以,虽然我仍然不知道到底发生了什么,但我明白了一点^^ 您附加的链接很好地解释了更改检测。因此,我将尝试扩展我的答案,让我知道它是否能让更多人了解正在发生的事情。 不幸的是,我发现了一个 postMessage() 有效的情况,但 detectChanges() 没有...但它可能是另一个问题。 答案更新了,是不是更清楚了,现在是怎么回事? 是的,它更清楚了,谢谢,我想我在阅读文章时理解了同样的事情。我现在遇到的事情有点复杂(stackblitz 的例子很简单,我认为它完全代表了我想要的,但似乎它没有)。我的问题是关于 Mozilla 的 PDF.js 查看器解决方案的滚动事件,它使用 iframe,我想从 Angular 组件自定义。我将尝试将我的确切问题放在 stackblitz 上,但我不确定我是否可以,因为 PDF.js 查看器包含一些大文件。然后我可能会问另一个问题^^'以上是关于为啥只有当我从 iframe 调用 postMessage() 方法时我的 Angular 属性才会更新的主要内容,如果未能解决你的问题,请参考以下文章
当我从单独的视图控制器中删除核心数据实体时,为啥会调用 UITableView 方法?