为什么我们不能在 angular 中操作通过 @input() 传递给组件的对象

Why can't we manipulate objects passed to component via @input() in angular

我最近将我的应用程序从 angular 7 升级到 angular 11。 一切正常,然后我实现了 angular 通用以启用服务器端渲染。

实施服务器端呈现后,我收到很多错误提示“无法更改对象 [Object Object] 的只读 'xyz' 成员”

所有这些成员都来自我通过@input()从父组件传递到子组件的对象

我的问题

  1. 操作作为输入传递的对象是否错误
  2. 为什么 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 的经验。我的猜测是它更严格地执行数据流,重构可能会解决这个问题。