【问题标题】:Does @Input give a two way binding?@Input 是否提供两种方式绑定?
【发布时间】:2018-11-08 14:09:46
【问题描述】:

我认为@Input 工作的方式会直接给出一个很大的“不!”对这个问题。但是,今天我偶然发现了一些奇怪的行为,或者我一直对@Input 的看法是错误的..

我已经创建了一个stackblitz 来说明这个问题。这发生在 Angular 7.0.1 的 stackblitz 中,但在我的本地项目中,它也发生在 Angular 6.1.2 上。

stackblitz 展示了一个具有对象的简单父组件。该对象通过@Input 传递给子组件。子组件和父组件都具有更改对象的功能。它们还都在模板中显示对象的值。

我希望看到,当父级更改对象时,它会在子级中更改它。然而,我没想到,当孩子改变对象时,它也为父母改变了它。然而,stackblitz 确实显示了这种行为。我一直认为您需要通过 @Output 显式发出一个事件,以向父组件发送一个流并从子组件那里更改它。

【问题讨论】:

  • 一句“不?!”也打在我脸上。

标签: angular typescript input


【解决方案1】:

答案是“不”。在您的示例中,您传递给 @Input 属性的值是对对象的引用。如果你有双向绑定,你可以在子组件中为该属性分配一个新对象:

this.thing = { name: "world", nbm: 10 };

并且父组件中的相应属性将相应更新。情况并非如此,正如您在this stackblitz 中看到的那样。

但是,由于父组件和子组件都引用了同一个对象,因此它们都可以修改其中一个属性,并且该更改将在另一个组件中观察到。


为了实现双向绑定,可以在Change后面加上一个同名的@Output属性,并在发生变化时发出事件:

@Input() thing: any;
@Output() thingChange = new EventEmitter();

setNewObject(){
  this.thing = { name: "world", nmb: 10 };
  this.thingChange.emit(this.thing);
}

如果使用双向绑定语法,则更改将反映到父组件:

<child2 [(thing)]="thing"></child2>

请参阅this stackblitz 以获取演示。


如果要防止子组件修改原始对象,应该绑定对象属性而不是对象本身:

@Input() thingName: string;
@Input() thingNmb: number;
<child [thingName]="thing.name" [thingNmb]="thing.nmb"></child>

【讨论】:

  • 如果我理解正确:最初可能让我感到困惑的是,变化检测与变量的实际更新不同。如果传递了引用,并且在子组件中更新了实例,这实际上是直接在父组件中编辑的。触发更改检测后,仅完成视觉更新。
  • 在所有这些情况下,视图都会更新以反映数据中实际发生的情况。重要的一点是 (1) 更新子组件中 Input 对象的属性和 (2) 通过分配一个新对象来修改 Input 属性的值之间的区别。后一种情况不会通过单向绑定传播到父组件。
  • 嗨 @ConnorsFan 程序员是否应该在 Angular 中双重绑定引用类型以获得良好实践,或者将其作为问题的输入?
【解决方案2】:

是的,Angular Change Detectionparent 流向child,但是您遇到的与Object reference 无关。由于parentchild 中的共享对象指向同一个reference,因此它将在两个组件中更新。

每当您通过子组件进行任何更改时,父组件中的共享对象也会发生更改,Angular 会检测到更改并触发 UI 更新。

但是,这与原始数据类型不同。您认为原始数据类型是正确的。万一原始数据类型更改从父级移动到子级,反之亦然。

这是相同的演示 - https://stackblitz.com/edit/angular-1wusrc

【讨论】:

  • 您好,程序员是否应该在 Angular 中双重绑定引用类型以获得良好实践,还是将其作为问题的输入?
  • 除非需要,否则不要进行双重绑定。这会不必要地影响性能。如果它符合您的目的,您可以保持原样。
【解决方案3】:

双向数据绑定是event 绑定和property 绑定的组合类似香蕉的语法 - 其中[(ngModel)][ngModel](ngModelChange) 事件的组合 p>

Change 这个词带来了魔力,当模型名称后跟 Change 词 angular 时,它将识别出这将是双向数据绑定并使其像那样工作

来到Child 组件@Input() 是将数据作为property 绑定传递给子组件的一种,而@Output() 是将数据从子组件作为event 绑定传递给父组件的那种- 所以实现两种方式都需要的绑定 - 检查下面的示例

getModelList: any[];
    @Output() modelListChange: EventEmitter<any[]> = new EventEmitter<any[]>();
    @Input()
    get modelList(): any[] {
        return this.getModelList;
    }
    set modelList(value) {
        this.getModelList = value;
        this.modelListChange.emit(this.getModelList);
    }

在上面的代码中,我们有modelList 属性和modelListChange 属性以及这两个绑定的组合,因此我们可以将modelList 属性作为[(modelList)]="value" 访问,这是您的双向绑定属性

从父级访问 - &lt;multi-dropdown [(modelList)]="value"&gt;&lt;/multi-dropdown&gt; 现在您的父级属性将基于两种方式数据绑定进行更新 - 确保正确拼写 Change,这也是区分大小写的

希望这会对您有所帮助 - 谢谢快乐编码!

【讨论】:

  • 这对我来说很有意义。但是,我不明白为什么当我只将@Input 放在组件中时它的行为是这样的,因此它没有双向绑定。
  • 相同的对象引用将始终进行双向绑定。它不是关于 Angular,而是关于 oops 的工作原理。
  • 但是你永远不会有一个非原始类型的单向绑定(参考@SunilSingh 的评论)?
  • 因为@Input@Output有不同的绑定方式
【解决方案4】:

当更改检测策略设置为 ChangeDetectionStrategy.Default 时,角度通常会触发更改检测,是的,这会起作用。

将其设置为 ChangeDetectionStrategy.OnPush,您将看到不同的行为。

这个设置是在组件注解中配置的:

@Component({
    selector: 'my-awesome-component',
    templateUrl: './my-awesome-component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyAwesomeComponent{

}

编辑:我刚刚尝试在您的 stackblitz 中将 ChangeDetectionStrategy 更改为 OnPush,并且这些更改仍在反映。我刚刚再次阅读了文档,很明显,为什么总是更新它的原因是因为你的按钮上的点击事件触发了更改检测,并且更改检测是从子级到父级触发的。因此,您可以说更改检测会像 html 事件一样冒泡到根目录。

【讨论】:

  • 这对我来说仍然毫无意义。如果我添加此检测策略,更改父项中的对象会更改父项,但不会更改子项。更改子对象中的对象会更改两个组件中的对象。这几乎看起来好像有一个向上绑定到父组件,但没有通过输入向下绑定。我的预期结果是,当您更改父组件中的变量时,它会在子组件中更改(通过输入),但反过来只能通过输出。显然情况似乎并非如此?
  • 我刚刚编辑了答案,你是对的:更改检测在组件树中向上传播,但不会向下传播。而变更检测的发起者就是你的点击事件。
  • 嗨,戴维,你好,程序员是否应该在 Angular 中双重绑定引用类型以获得良好实践,或者将其作为问题的输入?
猜你喜欢
  • 2017-04-06
  • 2016-02-27
  • 2017-09-05
  • 2014-02-13
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 2018-12-09
  • 2012-01-14
相关资源
最近更新 更多