我创建了一个包含表单<address></address>
的自定义组件。我有一个父组件,其中包含以下数组:
@ViewChildren(AddressComponent) addressComponents: QueryList<AddressComponent>;
因此父可以包含这些元素的集合,用户可以根据输入的地址数添加和删除它们。
父级还有一个按钮,可在用户输入所有需要的地址后继续。但是,<address>
组件必须正确填写,因此我在<address>
组件上有一个公共getter:
get valid(): boolen {
return this._form.valid;
}
返回父级按钮。如果任何<address>
组件无效,则需要禁用它。所以我写了以下内容:
get allValid() {
return this.addressComponents && this.addressComponents.toArray().every(component => component.valid);
}
在父模板中:
<button [disabled]="!allValid" (click)="nextPage()">Proceed</button>
但angular不喜欢这样,因为在addressComponents
生命周期事件之前,ngAfterViewInit
未在父级中定义。并且因为它立即运行ngOnViewInit()
,我得到两个不同的表达式检查值,这会导致错误。 (至少这是我的想法)。
如何在模板中使用依赖于ngAfterViewInit
的属性?或者告诉我的父母其所有孩子都有效的最佳方式是什么?
错误讯息:
检查后表情发生了变化。上一个值:'false'。 当前价值:'true'
更新:
所以我console.log
修改了allValid
的返回值并注意到它第一次是undefined
。这是预期的,this.addressComponents
为undefined
,直到ngAfterInit
。下一个日志是true
,这是令人惊讶的,因为我在页面上还没有任何<address>
组件。我在父组件的ngOnInit
中使用模拟数据(尽管都是有效的)来创建组件。我确实知道([].every...
在空数组上返回true)。因此,对console.log
的第三次调用正在返回false
。再次,我有点惊讶,因为我的所有数据都是有效的。在第4个日志中它返回true
这是我的预期。所以我假设返回的最终值是Angular不喜欢的。
无论如何,我能够解决这个问题。我不知道我是否正在修复问题或者只是抑制错误。我不喜欢这个解决方案,因此我将继续提出问题以寻求更好的解决方案。
get allValid() {
return this.addressComponents && this.addressComponents.length > 0 && this.addressComponents().toArray().every(component => component.valid);
}
答案 0 :(得分:0)
所以我认为正在发生的事情:
第一波更改检测会使您的函数出错,然后您的父组件在实例化视图后找到此信息(然后返回true)。在“dev”模式下,Angular运行两次更改检测,以确保在更改检测后不会发生更改(当然,更改检测应检测所有更改!)
根据这里找到的答案:
Angular2 - Expression has changed after it was checked - Binding to div width with resize events
使用AfterViewInit会导致这些问题,因为它可能在更改检测完成后运行。
在超时时包装你的作业将解决这个问题,因为它会在设置值之前等待一个滴答。
ngAfterViewInit(){
setTimeout(_ => this.allValid = this.addressComponents && this.addressComponents.toArray().every(component => component.valid));
}
由于这些原因,我不会在模板变量上使用getter,因为视图初始化可能会在更改检测完成后更改值。
答案 1 :(得分:0)
如果我理解,您可能需要在父母中找到一种方法来跟踪孩子的实例数,并且孩子需要EventEmitter
来通知家长它有效或无效。
因此,在父级中,您可以使用数组来跟踪有多少个地址实例。
父组件
addressForms: Array<any> = [{ valid: false }];
addAddressForm() {
this.addressForms.push({ valid: false ));
}
checkValid() {
// somehow loop through the addressForms, make sure all valid
let allValid: boolean = false;
for (var i = this.addressForms.length - 1; i >= 0; i--) {
if (this.addressForms[i].value && allValid === false)
allValid = true;
}
return allValid;
}
父模板
<div *ngFor="let form of addressForms; let i = index">
<address (valid)="form.valid = true" (invalid)="form.valid = false"></address>
</div>
<button [disabled]="checkValid()">Next</button>
地址组件
@Output() valid: EventEmitter<any> = new EventEmitter();
@Output() invalid: EventEmitter<any> = new EventEmitter();
isValid: boolean = false;
check() {
// call this check on field blurs and stuff
if ("it's valid now" && !this.isValid) {
this.isValid = true;
this.valid.emit(null);
}
if ("it's not valid anymore" && this.isValid) {
this.isValid = false;
this.invalid.emit(null);
}
}
无论如何,这是一个基本的想法,有一些明显足以填补的漏洞。希望这与你正在做的事情有一些相关性,我开始理解这个问题。祝你好运!