为什么我们不能在 angular 中操作通过 @input() 传递给组件的对象
Why can't we manipulate objects passed to component via @input() in angular
我最近将我的应用程序从 angular 7 升级到 angular 11。
一切正常,然后我实现了 angular 通用以启用服务器端渲染。
实施服务器端呈现后,我收到很多错误提示“无法更改对象 [Object Object] 的只读 'xyz' 成员”
所有这些成员都来自我通过@input()从父组件传递到子组件的对象
我的问题
- 操作作为输入传递的对象是否错误
- 为什么 Angular 通用(服务器端呈现)而不是客户端呈现
这是一个这样的组件
export class BannerComponent {
@Input() banners : Offer[]
constructor(private analyticService : AnalyticService) { }
ngOnChanges() {
if(this.banners) {
this.banners.forEach(banner => {
if(!banner.bannerImage.startsWith("http"))
banner.bannerImage = environment.imageHost + banner.bannerImage;
})
}
}
recordEvent(banner : Offer) {
this.analyticService.eventEmitter(banner.category.name, "Click on banner", banner.offerDetail + "-" + banner.merchant.name, AnalyticService.AVG_AFFILIATE_CLICK_VALUE);
}
}
这是我的报价class
import { Store } from "./store";
import { Category } from "./category";
export class Offer {
id: number;
merchant: Store;
offerDetail: string;
link: string;
openExternal: boolean;
logoPath: string;
lastDate: Date;
banner: boolean;
bannerImage: string;
category : Category;
offerDescription?: string;
}
Store 和 Category 是另外两个模型
关于问题 1:
简短的回答是肯定的,您应该避免在子组件中操作来自父组件的数据。
这是因为在Angular中,数据从父组件流向子组件,并且数据是单向流动的,这在angular docs中有所暗示。
在您的情况下,您正在修改父组件的数据。
考虑这个例子:
@Component({
template: `<app-banner-component [banners]="parentBanners"></app-banner-component>`
})
class ParentComponent {
parentBanners: Offer[] = {...};
}
调用 ngOnChanges
后,parentBanners
值也将更改其 bannerImage
。这是因为数组没有被复制,而是通过引用传入,所以在子组件中对数组所做的任何更改也会应用到父组件。
因为更改检测从父组件运行到子组件,这可能会导致可怕的 ExpressionChangedAfterItHasBeenChecked 错误,如 this video 中所述。
要解决此问题,请复制您的数组,例如:
@Input() set banners (values: Offer[]) {
this._banners = values.map(offer => ({...offer}));
}
get banners(): Offer[] {
return this._banners;
}
private _banners : Offer[];
关于 2,我没有使用 Angular Universal 的经验。我的猜测是它更严格地执行数据流,重构可能会解决这个问题。
我最近将我的应用程序从 angular 7 升级到 angular 11。 一切正常,然后我实现了 angular 通用以启用服务器端渲染。
实施服务器端呈现后,我收到很多错误提示“无法更改对象 [Object Object] 的只读 'xyz' 成员”
所有这些成员都来自我通过@input()从父组件传递到子组件的对象
我的问题
- 操作作为输入传递的对象是否错误
- 为什么 Angular 通用(服务器端呈现)而不是客户端呈现
这是一个这样的组件
export class BannerComponent {
@Input() banners : Offer[]
constructor(private analyticService : AnalyticService) { }
ngOnChanges() {
if(this.banners) {
this.banners.forEach(banner => {
if(!banner.bannerImage.startsWith("http"))
banner.bannerImage = environment.imageHost + banner.bannerImage;
})
}
}
recordEvent(banner : Offer) {
this.analyticService.eventEmitter(banner.category.name, "Click on banner", banner.offerDetail + "-" + banner.merchant.name, AnalyticService.AVG_AFFILIATE_CLICK_VALUE);
}
}
这是我的报价class
import { Store } from "./store";
import { Category } from "./category";
export class Offer {
id: number;
merchant: Store;
offerDetail: string;
link: string;
openExternal: boolean;
logoPath: string;
lastDate: Date;
banner: boolean;
bannerImage: string;
category : Category;
offerDescription?: string;
}
Store 和 Category 是另外两个模型
关于问题 1:
简短的回答是肯定的,您应该避免在子组件中操作来自父组件的数据。
这是因为在Angular中,数据从父组件流向子组件,并且数据是单向流动的,这在angular docs中有所暗示。
在您的情况下,您正在修改父组件的数据。
考虑这个例子:
@Component({
template: `<app-banner-component [banners]="parentBanners"></app-banner-component>`
})
class ParentComponent {
parentBanners: Offer[] = {...};
}
调用 ngOnChanges
后,parentBanners
值也将更改其 bannerImage
。这是因为数组没有被复制,而是通过引用传入,所以在子组件中对数组所做的任何更改也会应用到父组件。
因为更改检测从父组件运行到子组件,这可能会导致可怕的 ExpressionChangedAfterItHasBeenChecked 错误,如 this video 中所述。
要解决此问题,请复制您的数组,例如:
@Input() set banners (values: Offer[]) {
this._banners = values.map(offer => ({...offer}));
}
get banners(): Offer[] {
return this._banners;
}
private _banners : Offer[];
关于 2,我没有使用 Angular Universal 的经验。我的猜测是它更严格地执行数据流,重构可能会解决这个问题。