著名的select2
终于有热心的大牛构建了angular
版本,并命名为ng-select2
。但(猜想)由于其核心的代码依赖于jquery
,所以在结合formControl
使用时,会发生如下异常:Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.
情景再现
比如我们有以下组件树:
一个普通的A组件,一个可以实现了ControlValueAccessor
接口并声明为FormControl
的B组件,一个引用的ng-select2
组件。
其中A组件包含B组件、B组件又包含ng-select2
组件。
ng-select2
组件中的数据列表是通过资源请求得到的列表,所以是异步数据,B组件V层示例如下:
<ng-select2
[value]="datum"
[data]="data"
(valueChanged)="onChanged($event)"
</ng-select2>
B获取select2使用的数据列表data示例代码如下:
ngOnInit(): void {
this.service.getAll()
.subscribe(data => {
// 在这设置ng-select2的数据列表及已选择的值
this.data = data;
});
}
此时在A组件将会发生如下错误:Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.
[error] 此示例违反了angular的单元向数据流原则,应当在实际开发中予以规避。
原因猜想
我们知道,由于在大牛们会详细的解释异常发生的原因,所以解决问题的第一步也是最重要的一步便是翻译:严重错误:表达式被检测完毕以后却发生了变更错误:当表达式被检测完毕以后却又发生了变更。检测完毕时,检测的'ng-pristine'的值为'true'. 当前值为:'false'
。
同时pristine
可译为原始的
。
所以我猜错主大概是如下发生的:
也就是说:将A组件在渲染完毕的情况下,发现FormControl
的值发生了变化,则会发生ExpressionChangedAfterItHasBeenCheckedError
异常。
解决方法
ng-select2的官方demo中给出了当列表值异步时的解决方案,但遗憾的是该方法同样会引发上述异常。
该异常发生的主要原因是由于A组件处于渲染完毕,使得A中做为FormControl的组件B同样处于tHasBeenChecked
状态,所以解决该问题也可以如下着手:
如上图所示,当组件B在接收到异步数据时,为了防止其子组件ng-select2
进行数据弹射时A组件处于渲染完毕
状态,我们在此手动的触发重新渲染所有组件的方法。此时将A组件接收到最新的值时,其状态处于渲染中
,当然也就不会出现HasBeenChecked异常
了。
代码如下:
constructor(private service: Service,
private changeDetectorRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.service.getAll()
.subscribe(data => {
// 在这设置ng-select2的数据列表
this.data = data;
// 手动调用组件渲染方法,重新渲染各组件
this.changeDetectorRef.detectChanges();
});
总结
排错的路千万条,翻译、瞎猜第一条。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。