Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/418.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 角度父子绑定更改检测_Javascript_Angular_Angular Template - Fatal编程技术网

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)中可能不可用。在大多数情况下,可以通过使用不同的方法来避免使用它