Angular 父子绑定更改检测

Jay*_*yer 5 javascript angular-template angular

简而言之,我有一个用作文本输入的组件。父组件使用@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

和子组件

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处了解 Plunker 示例

我遇到的问题如下:

当在子输入中输入一个值并触发blur事件时,新值会向上冒泡到父级并应用验证 - 如果验证导致值被修改,它会正确地向下冒泡回子级的值 - 快乐的日子.

但是,如果前 4 个字符保持不变,并且您附加了其他字符,则在blur验证时仍将应用并且父级的值将正确更新,但子级将保留“无效”值 - 没有更多快乐的日子。

所以在我看来,Angular 没有检测到父数据的变化(足够公平,因为它在技术上没有改变)所以它没有将“最新”值发送回孩子。

我的问题是,即使它在技术上没有“改变”,我怎样才能让子文本输入始终显示来自父级的正确值?

Fab*_*ioG 3

更好的解决方案

@Jota.Toledo 的好评论让我意识到我的方法虽然当时确实为我提供了一种快速解决方法,但这并不是一个好的方法,所以我实际上对我的项目做了一些更改,这些更改可以为您工作好吧,也遵循他的建议

将“验证”逻辑委托给子组件

同时将验证定义保留在父级中作为函数,并作为参数传递给子级@Input

这样我就可以给父级 2 个公共变量

  • 一个物体(物品)
  • 函数(验证)

并将onValueChange函数更改为仅更新 item.key1,因为它已经被验证。

在子级中添加一个新的@Input类型参数(验证)Function,并使用该函数来验证 onBlur 内的 newValue,然后再将值发送给父级。

我感觉我在这里写的内容可能“听起来”有点令人困惑,所以我添加了我想要解释的代码。

家长

@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;
   }
}
Run Code Online (Sandbox Code Playgroud)

子级(保留模板部分,因为它未更改)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

以前的答案(不是一个好方法)

我过去也遇到过类似的问题,我解决的方法是清除我的 var,然后在 a 中给出最终值,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;
    });

}
Run Code Online (Sandbox Code Playgroud)