Angular(2+) : Mutating an input param in child component

Viewed 2497

Is mutating an input param in a child component fine or should all input objects be considered read-only & then emit event for any changes back to parent & delegate the change to parent component? Are there any problems that could arise out of modifying input params.

class ChildComponent {

  @Input() parentParam;
  @Output() clickValEvent = new EventEmitter<boolean>();

  let parentParamClone = Object.assign({}, parentParam);

  childClickEvent(val) {
    //Update value locally.
    // parentParam.clickVal = val;

    //Inform parent & let it do necessary change.
    // this.clickValEvent.emit(val);

    //Only play with local clone.
    // parentParamClone.clickVal = val;
    }
}
2 Answers

By right, yes, you can modify the @input value in child component, it will reflect in your parent component. But then there are 2 reasons you should avoid this:

  1. it will not work for primitive value, if you expect to change @input primitive value and it reflect in your parent component, this will not work.

  2. this is not a good practice, it open the chance for all the children component to arbitrary update the @input object and make us hard to trace the value changes. So using @output save us a lot of effort in future in maintaining the code, because it is more declarative.

I find it weird that, I couldn't find any official explanation regarding to this from Angular docs. Unlike ReactJs and VueJs, they very discourage us to mutate the props in child component.

  1. Passing your @Inputas read-only is what I see most frequently, this way the parent's property stays imutable.

  2. If make sense work if a copy of the @Input for you, it means you want to mutate the value only inside the child's component, and all changes will not affect the parent.

  3. Using output when you have to change the object and persisting it in the parent component is the approach (that for me, is better), but i use to use two way binding instead of leading with the callback.

In your example, if you want a @input property where you change its value in the child's component and it mutate the father property, you should consider two way binding BANANA IN A BOX, so you code should look like this:

class ChildComponent {

  @Input() parentParam;
  @Output() parentParamChange = new EventEmitter<boolean>(); // <- this EventEmitter must have the parentParam type.

  let parentParamClone = Object.assign({}, parentParam);

  childClickEvent(val) {
    //Inform parent & let it do necessary change, when you
    // update the parent's value, it will be reflected in this component.
     this.parentParamChange.emit(val);
}

And you should use this way ni you html template:

...
[(parentParam)]="propertyYouArePassingHere"
...

instead of

...
    [parentParam]="propertyYouArePassingHere"
    (clickValEvent)="captureOutputCallback()" 
 ...

Note: To make it work you must have the output's name following this pattern: inputThatYouWantMakeTwoWayBindName+Change, that is the reason I renamed your output to parentParamChange.

Related