Javascript 角度父子绑定更改检测
简而言之,我有一个作为文本输入的组件。父组件使用Javascript 角度父子绑定更改检测,javascript,angular,angular-template,Javascript,Angular,Angular Template,简而言之,我有一个作为文本输入的组件。父组件使用@Input绑定将数据传递到此组件。当通过@Output绑定触发更改事件时,我会在父组件中执行一些自动验证。这是为了删除错误的值,并用一些合理的默认值替换它们,以获得更好的用户体验 下面是父组件的基本版本 @Component({ selector: 'parent', template: ` <div> <child [value]="item.key1" (
@Input
绑定将数据传递到此组件。当通过@Output
绑定触发更改事件时,我会在父组件中执行一些自动验证。这是为了删除错误的值,并用一些合理的默认值替换它们,以获得更好的用户体验
下面是父组件的基本版本
@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;
}
}
@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);
}
}
@组件({
选择器:'父',
模板:`
`
})
导出类父类{
项目={
键1:'测试值',
键2:'另一个测试值'
};
onValueChange(newValue){
//执行一些验证并设置默认值
如果(newValue.length>4){
newValue=newValue.substring(0,4);
}
this.item.key1=新值;
}
}
以及子组件
@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;
}
}
@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);
}
}
@组件({
选择器:'子',
模板:`
`
})
导出类子类{
@Input()值:字符串;
@Output()valueChange=neweventemitter();
onBlur(){
this.valueChange.emit(this.value);
}
}
看
我的问题如下:
在子输入中输入值并触发blur
事件时,新值会向上冒泡到父级,并应用验证-如果验证导致值被修改,则会正确地向下冒泡到子级值-快乐日子
但是,如果前4个字符保持不变,并且您添加了其他字符,则在blur
时仍将应用验证,并且父项的值将正确更新,但子项将保留“无效”值-不再有快乐的日子
因此,在我看来,Angular没有检测到父母数据的变化(这很公平,因为它在技术上没有变化),因此它没有将“最新”的值发送回孩子
我的问题是,如何让子文本输入始终显示来自父文本的正确值,即使它在技术上没有“更改”?您应该在
父组件上创建主题:
export class ParentComponent {
parentSubject:Subject<Item> = new Subject();
notifyChildren() {
this.parentSubject.next('new item value');
}
}
<child [parentSubject]="parentSubject"></child>
export class ChildComponent {
@Input() parentSubject:Subject<Item>;
ngOnInit() {
this.parentSubject.subscribe(event => {
//do your stuff with the updated value
});
}
最后,在子组件上订阅:
export class ParentComponent {
parentSubject:Subject<Item> = new Subject();
notifyChildren() {
this.parentSubject.next('new item value');
}
}
<child [parentSubject]="parentSubject"></child>
export class ChildComponent {
@Input() parentSubject:Subject<Item>;
ngOnInit() {
this.parentSubject.subscribe(event => {
//do your stuff with the updated value
});
}
导出类子组件{
@输入()父主题:主题;
恩戈尼尼特(){
this.parentSubject.subscribe(事件=>{
//用更新后的值完成你的工作
});
}
您可以使用以下命令更新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;
})
}
但是,如果前4个字符保持不变,则添加
其他字符,模糊后仍将应用验证
父项的值将正确更新,但子项将正确更新
保留“无效”值-不再有快乐的日子
所以在我看来,Angular没有检测到
家长数据(因为技术上没有改变,所以很公平)所以
未将“最新”值发送回子级
正确。事实是angular正在“缓存”通过value
输入属性传递的最新值,并且正如您所认识到的,如果不更改前4个字符,您就不会真正将新值推入其中
您可以通过向您的孩子添加ngOnChanges(changes)
并将更改值记录到console中来检查这一点;在上述情况下,不会记录任何内容,因为不会推送value
的新值
您可以通过以下方式克服此问题:
- 通过将值包装在对象中并在子组件中展开,强制始终通过输入属性推送新值
- 将该“验证”逻辑委托给子组件,以便仅通过输出属性发出“验证”值
更好的解决方案
@托莱多的好评论让我意识到我的方法,虽然它当时确实是我的一个快速解决方法,但它不是一个好方法,所以我实际上去对我的项目做了一些改变,也可以为你工作,也遵循了他的建议
将“验证”逻辑委托给子组件
@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;
}
}
@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);
}
}
同时将验证定义作为函数保留在父级中,该函数作为@Input
参数传递给子级
这样我会给父母两个公共变量
- 对象(项目)
- 函数(验证)
并更改onValueChange
功能以仅更新item.key1,因为它将被验证
在子对象中,添加一个新的@Input
param(validation),类型为Function
,并在将值发送给父对象之前,使用该函数验证onBlur中的新值
我有一种感觉,我在这里写的东西可能“听起来”有点混乱,所以我正在添加代码来解释我试图解释的内容
母公司
@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;
}
}
以前的答案(不是一个好方法)
我过去也遇到过类似的问题,我解决的方法是清除我的var,然后在setTimeout
中给出它的最终值,而不指定毫秒
在父组件中,它看起来像这样:
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;
});
}
Angular已经有了自己的通知更改的方法。因此没有理由使用Responsive复制此功能。虽然这解决了问题,但它缺乏对其工作原理的解释。就个人而言,我强烈反对使用setTimeout
作为解决方法,因为它取决于framewo的实现细节rk,即setTimeout
触发更改检测周期的事实。此外,它将应用程序与window.setTimeout
函数耦合,这在其他平台(SSR)中可能不可用。在大多数情况下,可以通过使用不同的方法来避免使用它