Angular2之ng中变更检测问题的示例分析

这篇文章主要介绍Angular2之ng中变更检测问题的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

成都创新互联是专业的江北网站建设公司,江北接单;提供网站建设、网站设计,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行江北网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!

开发中遇到的问题

在开发中遇到一个这样的问题,代码不便透露,这里用简单的例子还原一下问题所在:

有三个组件,第一个是用来展示Todo列表的组件TodoComponent,Todo是个类,包含id和name属性。

@Component({
 selector: 'todo-list',
 template: `
  {{ item.name }}

 `, }) export class TodoComponent{  @Input() todos: Todo[];  public getTodos():Todo[]{   return this.todos;  } }

第二个组件同样是一个Todo列表展示组件TodoDataComponent ,不同的是该组件需要一个TodoComponent类型的输入,并从TodoComponent组件中获得需要展示的Todo数据。

@Component({
 selector: 'app-todo-data',
 template: `{{ 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.htm
       next  
 
//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. Previous value: 'undefined'. Current value: '[object Object]'.

该错误仅在开发模式下会报告出来的,解决掉总是更好的选择,防止在生产环境下出现问题。

问题的原因及解决办法

这个问题是ng2中的变更检测策略造成的,ng2并没有智能到一有数据变更就能自动检测到的,执行变更检测的一些情况有:组件中的输入发生变化、组件中有事件响应、setTimeOut函数等。

这样在上面的小例子中, @ViewChild(TodoComponent)todoComponent: TodoComponent;从undefined到[object Object],而并没有触发ng的变更检测。

解决办法也很简单,ng支持手动触发变更检测,只要在适当的位置,调用变更检测即可。
在上面的例子中,解决办法为:

从@angular/core引入AfterViewInit, ChangeDetectorRef。注入ChangeDetectorRef对象,并在声明周期钩子ngAfterViewInit中调用变更

constructor(private todoService:TodoService, private cdr: ChangeDetectorRef){}

ngAfterViewInit(){
 this.cdr.detectChanges();
}

ChangeDetectorRef

用来处理ng变更的类,可以使用它来进行完全的手动变更检测,主要有一下方法:

1.markForCheck()标记为需要进行变更检测,官方给的一下例子,setInterval不会触发变更检测,因此模板上的numberOfTicks 并不会发生变化。

setInterval(() => {
this.numberOfTicks ++
 // the following is required, otherwise the view will not be updated
 this.ref.markForCheck();
}, 1000);

2.detach()从变更检测树上分离,即该组件不会进行自动的变更检测,变更需要手动进行,使用detectChanges函数。

3.detectChanges()手动检测变更,当变更检测代价较大时,可以设置为定时进行表更检测

ref.detach();
setInterval(() => {
 this.ref.detectChanges();
}, 5000);

4.checkNoChanges()进行变更检测,有变更时抛出异常

5.reattach()detach()方法的作用相反

其他一些变更检测知识

angular2中的每一个组件都关联到一个变更检测器,ChangeDetectorRef可以用来控制变更检测器进行检测。
浏览器的以下行为可以出发检测器进行检测:

1.所有浏览器事件

2.setTimeout()setInterval()

3.Ajax请求

OnPush变更检测模式

组件默认使用的是Default变更检测模式,只要组件的输入发生变化时,就会触发检测器的执行。除Default模式外,还有一种OnPush变更检测模式,使用该模式首先需要在组件声明修饰符中添加

@Component({
 selector: 'todo-list',
 changeDetection: ChangeDetectionStrategy.OnPush,
})

声明为OnPush变更检测模式意味着当组件输入发生变化时,不一定会触发变更检测器,只有当该输入的引用发生变化时,检测器才会触发。例如在一个数组中某个下标的值发生变化时,检测器不会触发,视图不会更新,只有该数组引用发生变化时,视图才会更新。当然浏览器事件、observable发出的事件等还是会触发检测器的。

以上是“Angular2之ng中变更检测问题的示例分析”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注创新互联行业资讯频道!


名称栏目:Angular2之ng中变更检测问题的示例分析
分享URL:http://hbruida.cn/article/piceoo.html

其他资讯