Angular parent 到 child 绑定更改检测
Angular parent to child binding change detection
简而言之,我有一个用作文本输入的组件。 parent 组件使用 @Input
绑定将数据传递到此组件。当通过 @Output
绑定触发更改事件时,我在 parent 组件中执行一些自动验证。这是为了删除错误值并用一些合理的默认值替换它们以获得更好的用户体验。
这是 parent 组件的基本版本
@Component({
selector: 'parent',
template: `
<div>
<child [value]="item.key1"
(valueChange)="onValueChange($event)">
</child>
</div>
`
})
export class Parent {
item = {
key1: 'test value',
key2: 'another test value'
};
onValueChange(newValue) {
// Perform some validation and set default
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
this.item.key1 = newValue;
}
}
和 child 组件
@Component({
selector: 'child',
template: `
<div>
<input type="text"
[(ngModel)]="value"
(blur)="onBlur($event)" />
</div>
`
})
export class Child {
@Input() value: string;
@Output() valueChange = new EventEmitter();
onBlur() {
this.valueChange.emit(this.value);
}
}
我遇到的问题如下:
当在 child 输入中输入一个值并触发 blur
事件时,新值冒泡到 parent 并应用验证 - 如果验证导致要修改的值,它会正确地冒泡回 child 的值 - 快乐的日子。
但是,如果前 4 个字符保持不变,并且您附加了其他字符,则在 blur
时仍将应用验证并且 parent 的值将正确更新,但是child 将保留 "invalid" 值 - 不再有快乐的日子。
所以在我看来 Angular 没有检测到 parents 数据的变化(足够公平,因为它在技术上没有改变)所以它没有发送 "latest" 值回落到 child.
我的问题是,如何让 child 文本输入始终显示 parent 的正确值,即使它在技术上没有 "changed"?
您应该在 ParentComponent
上创建一个 Subject
:
export class ParentComponent {
parentSubject:Subject<Item> = new Subject();
notifyChildren() {
this.parentSubject.next('new item value');
}
}
在 ChildComponent
上将其作为 Input()
传递:
<child [parentSubject]="parentSubject"></child>
最后 subscribe
到 ChildComponent
:
export class ChildComponent {
@Input() parentSubject:Subject<Item>;
ngOnInit() {
this.parentSubject.subscribe(event => {
//do your stuff with the updated value
});
}
您可以使用以下
更新parent.ts中onValueChange方法的代码
onValueChange(newValue) {
this.item.key1 = null;
setTimeout(() => {
console.log('Initial: ' + newValue);
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
console.log('Final: ' + newValue);
this.item.key1 = newValue;
})
}
However, if the first 4 characters stay the same, and you append
additional characters, upon blur the validation will still be applied
and the parent's value will get updated correctly, but the child will
preserve the "invalid" value - no more happy days.
So it looks to me like Angular isn't detecting the change in the
parents data (fair enough because it hasn't technically changed) so it
isn't sending the "latest" value back down to the child.
正确。事实是 angular 是 "caching" 通过 value
输入 属性 传递的最新值,正如您所认识到的,如果您不更改,就不会真正将新值推入其中前 4 个字符。
您可以通过将 ngOnChanges(changes)
添加到 child 并将 changes 值记录到控制台来检查;在上述情况下,没有记录任何内容,因为没有推送 value
的新值。
你可以通过以下方式克服这个问题:
- 通过 wrapping object 和 unwrapping 它在 child 组件中。 (IMO 的错误方法)
- 将 "validation" 逻辑委托 到 child 组件中,这样只有 "validated" 值通过输出 属性.
更好的解决方案
@Jota.Toledo的好评让我意识到我的方法,虽然当时对我来说确实是一个快速的解决方法,但它不是一个好方法,所以我实际上做了一些改变我的项目也可以为你工作,也遵循他对
的建议
Delegating that "validation" logic into the child component
同时将 parent 中的验证定义作为函数传递给 child 作为 @Input
参数。
这样我会给 parent 2 public 变量
- 一个object(项目)
- 一个函数(验证)
并更改 onValueChange
函数以仅更新 item.key1,因为它已经过验证。
在 child 中添加一个 Function
类型的新 @Input
参数(验证)并使用该函数验证 onBlur 中的新值,然后再将值发送到 parent.
我觉得我在这里写的内容可能 "sound" 有点令人困惑,所以我添加了我要解释的代码。
Parent
@Component({
selector: 'parent',
template: `
<div>
<p><b>This is the parent component</b></p>
<child [value]="item.key1"
[validation]="validation"
(valueChange)="onValueChange($event)">
</child>
<p><b>Variable Value:</b> {{item | json}} </p>
</div>
`
})
export class Parent {
item = {
key1: 'test value',
key2: 'another test value'
};
validation = (newValue) => {
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
return newValue;
}
onValueChange(newValue) {
this.item.key1 = newValue;
}
}
Child(模板部分未更改,因为它未更改)
export class Child {
@Input() value: string;
@Input() validation: Function;
@Output() valueChange = new EventEmitter();
onBlur() {
this.value = this.validation(this.value)
this.valueChange.emit(this.value);
}
}
上一个答案(不是一个好方法)
我过去遇到过类似的问题,我解决的方法是清除我的 var,然后在 setTimeout
中给它最终值而不指定毫秒数。
在您的 parent 组件中,它看起来像这样:
onValueChange(newValue) {
console.log('Initial: ' + newValue);
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
console.log('Final: ' + newValue);
this.item.key1 = '';
setTimeout(() => {
this.item.key1 = newValue;
});
}
简而言之,我有一个用作文本输入的组件。 parent 组件使用 @Input
绑定将数据传递到此组件。当通过 @Output
绑定触发更改事件时,我在 parent 组件中执行一些自动验证。这是为了删除错误值并用一些合理的默认值替换它们以获得更好的用户体验。
这是 parent 组件的基本版本
@Component({
selector: 'parent',
template: `
<div>
<child [value]="item.key1"
(valueChange)="onValueChange($event)">
</child>
</div>
`
})
export class Parent {
item = {
key1: 'test value',
key2: 'another test value'
};
onValueChange(newValue) {
// Perform some validation and set default
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
this.item.key1 = newValue;
}
}
和 child 组件
@Component({
selector: 'child',
template: `
<div>
<input type="text"
[(ngModel)]="value"
(blur)="onBlur($event)" />
</div>
`
})
export class Child {
@Input() value: string;
@Output() valueChange = new EventEmitter();
onBlur() {
this.valueChange.emit(this.value);
}
}
我遇到的问题如下:
当在 child 输入中输入一个值并触发 blur
事件时,新值冒泡到 parent 并应用验证 - 如果验证导致要修改的值,它会正确地冒泡回 child 的值 - 快乐的日子。
但是,如果前 4 个字符保持不变,并且您附加了其他字符,则在 blur
时仍将应用验证并且 parent 的值将正确更新,但是child 将保留 "invalid" 值 - 不再有快乐的日子。
所以在我看来 Angular 没有检测到 parents 数据的变化(足够公平,因为它在技术上没有改变)所以它没有发送 "latest" 值回落到 child.
我的问题是,如何让 child 文本输入始终显示 parent 的正确值,即使它在技术上没有 "changed"?
您应该在 ParentComponent
上创建一个 Subject
:
export class ParentComponent {
parentSubject:Subject<Item> = new Subject();
notifyChildren() {
this.parentSubject.next('new item value');
}
}
在 ChildComponent
上将其作为 Input()
传递:
<child [parentSubject]="parentSubject"></child>
最后 subscribe
到 ChildComponent
:
export class ChildComponent {
@Input() parentSubject:Subject<Item>;
ngOnInit() {
this.parentSubject.subscribe(event => {
//do your stuff with the updated value
});
}
您可以使用以下
更新parent.ts中onValueChange方法的代码onValueChange(newValue) {
this.item.key1 = null;
setTimeout(() => {
console.log('Initial: ' + newValue);
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
console.log('Final: ' + newValue);
this.item.key1 = newValue;
})
}
However, if the first 4 characters stay the same, and you append additional characters, upon blur the validation will still be applied and the parent's value will get updated correctly, but the child will preserve the "invalid" value - no more happy days.
So it looks to me like Angular isn't detecting the change in the parents data (fair enough because it hasn't technically changed) so it isn't sending the "latest" value back down to the child.
正确。事实是 angular 是 "caching" 通过 value
输入 属性 传递的最新值,正如您所认识到的,如果您不更改,就不会真正将新值推入其中前 4 个字符。
您可以通过将 ngOnChanges(changes)
添加到 child 并将 changes 值记录到控制台来检查;在上述情况下,没有记录任何内容,因为没有推送 value
的新值。
你可以通过以下方式克服这个问题:
- 通过 wrapping object 和 unwrapping 它在 child 组件中。 (IMO 的错误方法)
- 将 "validation" 逻辑委托 到 child 组件中,这样只有 "validated" 值通过输出 属性.
更好的解决方案
@Jota.Toledo的好评让我意识到我的方法,虽然当时对我来说确实是一个快速的解决方法,但它不是一个好方法,所以我实际上做了一些改变我的项目也可以为你工作,也遵循他对
的建议Delegating that "validation" logic into the child component
同时将 parent 中的验证定义作为函数传递给 child 作为 @Input
参数。
这样我会给 parent 2 public 变量
- 一个object(项目)
- 一个函数(验证)
并更改 onValueChange
函数以仅更新 item.key1,因为它已经过验证。
在 child 中添加一个 Function
类型的新 @Input
参数(验证)并使用该函数验证 onBlur 中的新值,然后再将值发送到 parent.
我觉得我在这里写的内容可能 "sound" 有点令人困惑,所以我添加了我要解释的代码。
Parent
@Component({
selector: 'parent',
template: `
<div>
<p><b>This is the parent component</b></p>
<child [value]="item.key1"
[validation]="validation"
(valueChange)="onValueChange($event)">
</child>
<p><b>Variable Value:</b> {{item | json}} </p>
</div>
`
})
export class Parent {
item = {
key1: 'test value',
key2: 'another test value'
};
validation = (newValue) => {
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
return newValue;
}
onValueChange(newValue) {
this.item.key1 = newValue;
}
}
Child(模板部分未更改,因为它未更改)
export class Child {
@Input() value: string;
@Input() validation: Function;
@Output() valueChange = new EventEmitter();
onBlur() {
this.value = this.validation(this.value)
this.valueChange.emit(this.value);
}
}
上一个答案(不是一个好方法)
我过去遇到过类似的问题,我解决的方法是清除我的 var,然后在 setTimeout
中给它最终值而不指定毫秒数。
在您的 parent 组件中,它看起来像这样:
onValueChange(newValue) {
console.log('Initial: ' + newValue);
if (newValue.length > 4) {
newValue = newValue.substring(0, 4);
}
console.log('Final: ' + newValue);
this.item.key1 = '';
setTimeout(() => {
this.item.key1 = newValue;
});
}