【问题标题】:Why can't we manipulate objects passed to component via @input() in angular为什么我们不能以角度操作通过@input() 传递给组件的对象
【发布时间】:2021-02-02 05:17:31
【问题描述】:

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

在实现服务器端渲染后,我收到很多错误提示“无法更改对象 [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);
  }

}

这是我的报价课程

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 是另外两个模型

【问题讨论】:

  • 你能运行ng build --aot --prod吗?
  • 你能给出Offer类的定义吗?
  • @OwenKelvin 会在今天晚上通知您
  • @David 查看编辑
  • 模板中的banners属性是Observable提供的吗?

标签: angular typescript angular-universal


【解决方案1】:

关于问题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 的经验。我的猜测是它更严格地执行数据流,重构可能会解决这个问题。

【讨论】:

  • 没试过,但觉得很有说服力
猜你喜欢
  • 2017-02-02
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多