Search code examples
arraysangularecmascript-6clonengonchanges

Angular2 ngOnChanges clone @Input array


I'm using a DashboardComponent that gets the data from my DashboardService. This Component then passes my array of objects to my form component.

(Plunkr link at bottom of post)

DashboardComponent.ts

 private bottleArray: Bottle[] = [];

  ngOnInit() {
    // Get bottle types from service to the form needing them
    this.dashboardService.getBottleTypesAndNames()
      .subscribe(bottlesData => {
        bottlesData.forEach(bottle => {
          // Convert to Bottle type
          let bottleObject: Bottle = new Bottle(bottle.bottleTypeId, bottle.bottleName);
          this.bottleArray.push(bottleObject);
        });
      });
  }

DashboardComponent.html

<ct-create-order-form [bottleArray]="bottleArray"> </ct-create-order-form>

I did it that way so that my form components linked to my Dashboard won't be doing any call to my service.

I'm trying to clone my @Input so that my data updated from the form is not linked to my parent component (Dashboard), but I can't seem to do it... See code below :

CreateOrderFormComponent.ts

export class CreateOrderFormComponent implements OnChanges {
  @Input() private bottleArray: Bottle[];

  constructor() { }

  private clonedBottleArray: BottleCommand[];

  ngOnChanges(changes) {

    if (changes.bottleArray) {
      let test: BottleCommand[] = changes.bottleArray.currentValue;

      // Cloning
      console.log(test);  // Array of 6 Bottles

      this.clonedBottleArray = [...test];       
      console.log(this.clonedBottleArray);         // Empty Array
      this.clonedBottleArray = Array.from(test);
      console.log(this.clonedBottleArray);         // Empty Array
      this.clonedBottleArray = test.slice();
      console.log(this.clonedBottleArray);         // Empty Array

      this.clonedBottleArray = test;
      console.log(this.clonedBottleArray);         // Array of 6 bottles
   }
}

Is there any way to achieve what I am doing ? I don't understand why I can't clone my Input when I get the data ?

From this Youtube video made by AngularConnect, he is doing the exact same except that he is manipulating an Object, and I'm manipulating an Array of Objets.

https://youtu.be/-nsedZwvl9U?t=12m22s


EDIT : After creating a Plunkr, this seems to be working correctly in there.

https://plnkr.co/edit/js1vl0fcgOKtQNqXsWTL?p=preview


EDIT 2 : At the ngOnInit() from my DashboardComponent, if I mock the data, it is cloned correctly in my child component.


Solution

  • Looks like angular OnChange not firing due to it specific way of checking, here's brief explanation from this answer:

    During change detection, when Angular checks components' input properties for change, it uses (essentially) === for dirty checking. For arrays, this means the array references (only) are dirty checked. Since the rawLapsData array reference isn't changing, ngOnChanges() will not be called.

    And in your example, you're .pushing bottles in bottleArray, so OnChange doesn't fire on the same array reference.

    To get the changes, you could use DoCheck:

    ngDoCheck() {
      console.log(this.bottleArray);
      this.clonedBottleArray = [...this.bottleArray].slice(0, 4);
      console.log(this.clonedBottleArray);
    }
    

    it will fire when you push new values to the bottleArray. Working plunker here.