Angular2学习教程之ng中变更检测问题详解
前端之家 收集整理的这篇文章主要介绍了
Angular2学习教程之ng中变更检测问题详解 ,
前端之家 小编觉得挺不错的,现在分享给大家,也给大家做个参考。
开发中遇到的问题
在开发中遇到一个这样的问题,代码 不便透露,这里用简单的例子还原一下问题所在:
有三个组件,第一个是用来展示Todo列表的组件TodoComponent,Todo是个类,包含id和name属性 。
{{ item.name }}
`,})
export class TodoComponent{
@Input() todos: Todo[];
public getTodos():Todo[]{
return this.todos;
}
}
第二个组件同样是一个Todo列表展示组件TodoDataComponent ,不同的是该组件需要一个TodoComponent类型的输入,并从TodoComponent组件中获得需要展示的Todo数据。
{{ item.name }}
get data `,styleUrls: ['./todo-data.component.css'],inputs: ['todoComponent'],})
export class TodoDataComponent implements OnInit {
todoComponent: TodoComponent;
todos: Todo[]
constructor() { }
ngOnInit() {
}
getData(){
this.todos=this.todoComponent.getTodos();
}
}
最后一个是应用的根组件,根组件根据loading值来确定是否加载TodoComponent组件,并展示TodoDataComponent 组件。
//app.component.ts
@Component({
selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'],})
export class AppComponent implements OnInit {
todos: Todo[];
@ViewChild(TodoComponent)
todoComponent: TodoComponent;
loading: boolean = true;
constructor(private todoService:TodoService){
super(true);
}
ngOnInit(){
this.todoService.todos.subscribe(data => {this.todos=data});
this.todoService.load(0,3);
}
changeall(){
this.todoService.load(3,3);
}
}
这样问题就来了,TodoComponent 组件是否在页面 上展示是不确定的,在上面的例子中根组件最开始没有渲染TodoComponent组件,最后根据loading的值将TodoComponent渲染出来。而TodoDataComponent 组件的显示 又需要一个TodoComponent 进行初始化(跟组件通过@ViewChild(TodoComponent)
获得),这样造成在开发模式下出现以下错误 :
template:9:16 caused by: Expression has changed after it was checked. PrevIoU s value: 'undefined'. Current value: '[object Object]'
.
该错误 仅在开发模式下会报告出来的,解决 掉总是更好的选择,防止在生产环境下出现问题。
问题的原因及解决 办法
这个问题是ng2中的变更检测策略造成的,ng2并没有智能到一有数据变更就能自动 检测到的,执行变更检测的一些情况有:组件中的输入发生变化、组件中有事件响应、setTimeOut函数 等。
这样在上面的小例子中, @ViewChild(TodoComponent)todoComponent: TodoComponent;
从undefined到[object Object],而并没有触发ng的变更检测。
解决 办法也很简单,ng支持 手动触发变更检测,只要在适当的位置,调用 变更检测即可。
在上面的例子中,解决 办法为:
从@angular/core引入AfterViewInit,ChangeDetectorRef。注入ChangeDetectorRef对象,并在声明周期钩子ngAfterViewInit中调用 变更
ngAfterViewInit(){
this.cdr.detectChanges();
}
ChangeDetectorRef
用来处理ng变更的类,可以使用它来进行完全的手动变更检测,主要有一下方法 :
1.markForCheck()
标记 为需要进行变更检测,官方给的一下例子,setInterval不会触发变更检测,因此模板上的numberOfTicks 并不会发生变化。
{
this.numberOfTicks ++
// the following is
required ,otherwise the view will not be updated
this.ref.markForCheck();
},1000);
2.detach()从变更检测树上分离,即该组件不会进行自动 的变更检测,变更需要手动进行,使用detectChanges函数 。
3.detectChanges()手动检测变更,当变更检测代价较大时,可以设置为定时进行表更检测
{
this.ref.detectChanges();
},5000);
4.checkNoChanges()
进行变更检测,有变更时抛出异常
5.reattach()
与detach()
方法 的作用相反
其他一些变更检测知识
angular2中的每一个组件都关联到一个变更检测器,ChangeDetectorRef可以用来控制变更检测器进行检测。
浏览器的以下行为可以出发检测器进行检测:
1.所有浏览器事件
2.setTimeout()
和setInterval()
3.Ajax请求
OnPush变更检测模式
组件默认使用的是Default变更检测模式,只要组件的输入发生变化时,就会触发检测器的执行。除Default模式外,还有一种OnPush变更检测模式,使用该模式首先需要在组件声明修饰符中添加
声明为OnPush变更检测模式意味着当组件输入发生变化时,不一定会触发变更检测器,只有当该输入的引用发生变化时,检测器才会触发。例如在一个数组中某个下标的值发生变化时,检测器不会触发,视图不会更新,只有该数组引用发生变化时,视图才会更新。当然浏览器事件、observable发出的事件等还是会触发检测器的。
总结
以上就是这篇文章 的全部内容 了,希望本文的内容 对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家编程之家的支持 。
原文链接:https://www.f2er.com/js/38946.html