这个问题是上周的,当时觉得这个问题的解决办法太简单了,不用写博客记录,但是潘老师今天今天又遇到了需要使用这个的地方,感觉问题虽然不难,但是,写篇博客,方便自己查询,也给了其他人搜索到解决办法的机会。问题描述问题描述问题描述项目中使用到了ifame,理想状态是:当点击ifame中的按钮时,将会调用通过angular写的一个函数,函数将会修改一个ngif的判断条件,显示一个弹窗,但现实是:当点击ifame中的按钮时,将会调用通过angular写的一个函数,函数将会修改一个ngif的判断条件,然后就没有然后了. 开始的时候自己直接懵了, 方法确实执行了, 但界面没修改, 问了问潘老师, 潘老师说看看生命周期函数是否执行了, 果然, 所有的生命周期函数都没有调用。解决办法解决办法解决办法既然生命周期函数没调用,我们让他调用不就行了,值已经变化了,但是界面不变化,说明,angular 不知道值变化了,所以我们可以让angular 主动进行变更检测,让它知道已经发生了变化。对此我们可以使用 ChangeDetectorRefChangeDetectorRef变化监测类 - ChangeDetectorRef变化监测类 - ChangeDetectorRef变化监测类 - ChangeDetectorRefAngular 在整个运行期间都会为每一个组件创建 ChangeDetectorRef 的实例,该实例提供了相关方法来手动管理变化监测。有了这个类,我们自己就可以自定义组件的变化监测策略了,如停止/启用变化监测或者按指定路径变化监测等等。它有以下方法:

markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。

detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。

reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。

detectChanges():手动触发执行该组件到各个子组件的一次变化监测。
markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。detectChanges():手动触发执行该组件到各个子组件的一次变化监测。所以,我们可以使用 detectChanges() 来达到目标detectChanges()使用方法
// 在组件中注入
constructor(private changeDetectorRef: ChangeDetectorRef) {
}

// 直接使用
test() {
this.changeDetectorRef.detectChanges()
}
// 在组件中注入
constructor(private changeDetectorRef: ChangeDetectorRef) {
}

// 直接使用
test() {
this.changeDetectorRef.detectChanges()
}angular何时进行变化检测angular何时进行变化检测angular何时进行变化检测总结起来, 主要有如下几种情况:

用户输入操作,比如点击,提交等

请求服务端数据(XHR)

定时事件,比如 setTimeout , setInterval
用户输入操作,比如点击,提交等请求服务端数据(XHR)定时事件,比如 setTimeout , setIntervalsetTimeoutsetIntervalAngular并不是捕捉对象的变动,它采用的是在适当的时机去检验对象的值是否被改动,这个时机就是这些异步事件的发生。这个时机是由 Zone.js 去掌控的,它获取到了整个应用的执行上下文,能够对相关的异步事件发生、完成或者异常等进行捕获,然后驱动 Angular 的变化监测机制执行。Zone.jsZone.js的作用Zone.js的作用Zone.js的作用实际上 Zone,js 有一个叫猴子补丁的东西。在 Zone.js 运行时,就会为这些异步事件做一层代理包裹,也就是说Zone.js运行后,调用 setTimeout、addEventListener 等浏览器异步事件时,不再是调用原生的方法,而是被猴子补丁包装过后的代理方法。代理里setup了钩子函数, 通过这些钩子函数, 可以方便的进入异步任务执行的上下文Zone,jsZone.jssetTimeout、addEventListener
//以下是Zone.js启动时执行逻辑的抽象代码片段
function zoneAwareAddEventListener() {...}
function zoneAwareRemoveEventListener() {...}
function zoneAwarePromise() {...}
function patchTimeout() {...}
window.prototype.addEventListener=zoneAwareAddEventListener;
window.prototype.removeEventListener=zoneAwareRemoveEventListener;
window.prototype.promise = zoneAwarePromise;
window.prototype.setTimeout = patchTimeout;
//以下是Zone.js启动时执行逻辑的抽象代码片段
function zoneAwareAddEventListener() {...}
function zoneAwareRemoveEventListener() {...}
function zoneAwarePromise() {...}
function patchTimeout() {...}
window.prototype.addEventListener=zoneAwareAddEventListener;
window.prototype.removeEventListener=zoneAwareRemoveEventListener;
window.prototype.promise = zoneAwarePromise;
window.prototype.setTimeout = patchTimeout;关于 Zone.js 的详细内容可以看 这篇文章 。Zone.js这篇文章 angular变化检测策略angular变化检测策略angular变化检测策略angular 提供了两种变更检测策略,除了上述得Default外还有一种 OnPush 的检测机制OnPushOnPush 与 Default 之间的差别: 当检测到与子组件输入绑定的值没有发生改变时,变化检测就不会深入到子组件中去 。一个OnPush的例子app.comonent.ts
@Component({
selector: 'app-root',
template: `

{{title}}


user.name: {{user.name}}





`,
})
export class AppComponent {
title = 'OnPush Demo';
user: User = new User({name: 'yunzhi'});

changeUserName() {
this.user.name = 'new name';
}

changeUserObject() {
this.user = new User({name: 'new user'});
}
}
@Component({
selector: 'app-root',
template: `

{{title}}


user.name: {{user.name}}





`,
})
export class AppComponent {
title = 'OnPush Demo';
user: User = new User({name: 'yunzhi'});

changeUserName() {
this.user.name = 'new name';
}

changeUserObject() {
this.user = new User({name: 'new user'});
}
}test.component.ts
@Component({
selector: 'app-test',
template: `


test 组件







{{user.name}}


`,
// 使用OnPush模式只需要加上下面这段代码
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestComponent implements OnInit {
@Input() user: User;

constructor() {
}

ngOnInit() {
}

}
@Component({
selector: 'app-test',
template: `


test 组件







{{user.name}}


`,
// 使用OnPush模式只需要加上下面这段代码
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestComponent implements OnInit {
@Input() user: User;

constructor() {
}

ngOnInit() {
}

}这时当我们点击改变属性按钮时test组件显示的并不会变化,只有改变user得引用test组件显示的才会变化,如下图所示总结总结总结本来以为这个问题没什么可写的,直接解决好像就完事了,但没想到写着写着感觉能写的越来越多,比如 变更检测的顺序 ,还有 ExpressionChangedAfterItHasBeenCheckedError 都是应该知道的问题,但是感觉这些和主题又没有什么关系,想了想还是算了。变更检测的顺序ExpressionChangedAfterItHasBeenCheckedError