How to correctly pass props to a component with Rx

2019-06-04 06:44发布

问题:

Here is my component:

@Component({
  selector: 'bc-goods-detail',
  template: `
    <span>good id: {{good?.id}}</span>
    <input [value]="good?.name" (input)="onInput($event)" />
    <button (click)="onClick()">Save</button>
  `,
  styles: []
})
export class GoodsDetailComponent {
  @Input() good: Good;
  @Output() save  = new EventEmitter<Good>();

  onClick() {
    this.save.emit(this.good);
  }

  onInput ($event) {
    this.good.name = $event.target.value;
  }
}

When I change the name in input and then I am pressing save button and this.good is NOT CHANGED good. It is old good, like it was passed to the component.

I started to debug the problem. I added onInput handler. I found that when I do this instruction: this.good.name = $event.target.value; I get this error in console:

ERROR TypeError: Cannot assign to read only property 'name' of object '#<Object>'
    at GoodsDetailComponent.webpackJsonp.435.GoodsDetailComponent.onInput (goods-detail.ts:24)

Here is usage of the component:

<bc-goods-detail
  [good]="selectedGood$ | async"
  (save)="onSave($event)"
></bc-goods-detail>

Here is how I receive data for this component:

/*…*/
selectedGood$: Observable<Good>;

constructor(private store: Store<fromRoot.State>) {
  /*…*/
  this.selectedGood$ = store.select(fromRoot.getGoodSelectedEntity);
}

Here is the full code of container component: here.

Thoughts: I think the problem is because Observable returns immutable structure. I don’t think it is totally bad idea, but how to handle it?

I am trying to get same behaviour there: http://plnkr.co/edit/gdxEcSvC0v6JwoLEZDkJ?p=preview. It does not reproduce. I think this is because

How to solve my problem? I don’t want to get such error. When I press save I want this.good to contain mutated object. How to achieve this?

回答1:

You could create a copy of the original object in your presentation component and emit the value of that copy onClick of save. After emiting the mutated object, the action that you dispatch should store it as payload and the reducer should take care of replacing the old object with the mutated one. That is at least the approach that I use in my presentation components :)

For example:

export class GoodsDetailComponent {
  private _original: Good;
  goodClone: Good; // use this inside of the component template
  @Input('good')
  get good(){return this.goodClone;}
  set good(value:  Good){
    this.goodClone= //generate a clone of the object
    this._original = value;
  }

  @Output() 
  save  = new EventEmitter<Good>();

  onClick() {
    this.save.emit(this.goodClone);
  }
}