如何按值将对象传递给 Angular2+ 组件
How to pass object by value into Angular2+ component
假设我有一个组件接收一个 Foo
实例并显示一个表单来编辑它。
export class ChildComponent implements OnInit {
@Input() foo : Foo;
@Output() onChange : EventEmitter<Foo> = new EvenEmitter<Foo>();
constructor() {
}
ngOnInit() {
}
}
我将 ChildComponent
嵌套在 ParentComponent
中。
<div id="parent">
<app-child-component [foo]="parentFoo"></app-child-component>
</div>
现在即使我在这里使用单向绑定,因为 foo
是一个对象并因此通过引用传递,所以在 ChildComponent
中对其所做的任何更改也会反映在 ParentComponent
.
我该如何预防?有没有办法按价值传递?有什么最佳做法吗?
正如冈特在这个回答中所说:
Primitive values (string, num, boolean, object references) are passed by value (copied), objects and arrays are passed by reference (both components get a reference to the same object instance).
这与 Angular 无关,这只是 Javascript 以及 Typescript 的工作方式。
事实上,我想说的是,如果您将一个对象传递给子组件并尝试在那里更改它,那么您就是在更新同一个对象,所以它在某种程度上有点 "nice"值保持同步。
但是,如果您想分叉该值,则需要以某种方式创建本地副本。如评论中所述,您可以通过对子组件中的对象进行深度复制然后更改该值来实现。下面是一个 Stackblitz 小示例,说明了此方法:https://stackblitz.com/edit/angular-axr5zf.
好的,我目前为止最好的解决方案是这样的:
export class ChildComponent implements OnInit {
private _foo : Foo;
@Input()
set foo(value : Foo) { this._foo = Object.create(value || null); }
get foo() : Foo { return this._foo; }
@Output() onChange : EventEmitter<Foo> = new EventEmitter<Foo>();
constructor() {
}
ngOnInit() {
}
}
这看起来确实有效,但是它是一个单一属性的大量代码。我仍然不明白为什么 Angular 中没有这样的功能(例如 @InputByValue
装饰器)。
我认为当我希望将更改从子项反射回父项时,我会使用双向绑定 [(foo)]="foo"
。我在这里错过了什么吗?有什么理由不做我想做的事吗?
你可以试试
const 复制 = { ...原始 }
用于创建对象的副本
您需要生成一个新对象,而不是直接使用输入引用。一个快速的解决方案是使用 Spread syntax 生成对象的新副本并在模板中使用它。
@Input('foo') fooRef : Foo;
foo: Foo;
.
.
.
ngOnInit() {
// creating a new object using the spread syntax
this.foo = {...this.fooRef};
}
假设我有一个组件接收一个 Foo
实例并显示一个表单来编辑它。
export class ChildComponent implements OnInit {
@Input() foo : Foo;
@Output() onChange : EventEmitter<Foo> = new EvenEmitter<Foo>();
constructor() {
}
ngOnInit() {
}
}
我将 ChildComponent
嵌套在 ParentComponent
中。
<div id="parent">
<app-child-component [foo]="parentFoo"></app-child-component>
</div>
现在即使我在这里使用单向绑定,因为 foo
是一个对象并因此通过引用传递,所以在 ChildComponent
中对其所做的任何更改也会反映在 ParentComponent
.
我该如何预防?有没有办法按价值传递?有什么最佳做法吗?
正如冈特在这个回答中所说:
Primitive values (string, num, boolean, object references) are passed by value (copied), objects and arrays are passed by reference (both components get a reference to the same object instance).
这与 Angular 无关,这只是 Javascript 以及 Typescript 的工作方式。
事实上,我想说的是,如果您将一个对象传递给子组件并尝试在那里更改它,那么您就是在更新同一个对象,所以它在某种程度上有点 "nice"值保持同步。
但是,如果您想分叉该值,则需要以某种方式创建本地副本。如评论中所述,您可以通过对子组件中的对象进行深度复制然后更改该值来实现。下面是一个 Stackblitz 小示例,说明了此方法:https://stackblitz.com/edit/angular-axr5zf.
好的,我目前为止最好的解决方案是这样的:
export class ChildComponent implements OnInit {
private _foo : Foo;
@Input()
set foo(value : Foo) { this._foo = Object.create(value || null); }
get foo() : Foo { return this._foo; }
@Output() onChange : EventEmitter<Foo> = new EventEmitter<Foo>();
constructor() {
}
ngOnInit() {
}
}
这看起来确实有效,但是它是一个单一属性的大量代码。我仍然不明白为什么 Angular 中没有这样的功能(例如 @InputByValue
装饰器)。
我认为当我希望将更改从子项反射回父项时,我会使用双向绑定 [(foo)]="foo"
。我在这里错过了什么吗?有什么理由不做我想做的事吗?
你可以试试 const 复制 = { ...原始 } 用于创建对象的副本
您需要生成一个新对象,而不是直接使用输入引用。一个快速的解决方案是使用 Spread syntax 生成对象的新副本并在模板中使用它。
@Input('foo') fooRef : Foo;
foo: Foo;
.
.
.
ngOnInit() {
// creating a new object using the spread syntax
this.foo = {...this.fooRef};
}