0

Just need to start with a bit of app background (simplified)...

I have an existing Angular application that has just updates to lists of items received by polling a back end service.

For example, the following data containers...

class IdDescription {
      public id: string
      public description : string
}

class MyItem {
       public prop1 : IdDescription;
       public prop2 : IdDescription;
       public prop3 : IdDescription; 
       // etc
}

class IncomingUpdates {
      public updates: Array<MyItem>;
}

In my component I will have property to hold the array of items..

public data: Array<T>;

And in the markup I have the ngFor over the data

<div *ngFor="let item of data [@slideInOut]='slideInOutState' >
  <div tappable (click)='clickItem(item)'>
    <ion-ripple-effect></ion-ripple-effect>
    <ion-grid>
      <ion-row>            
        <ion-col class='card-main-data-col'>
          <div <b>{{item.prop1.description}}</b></div>
          <div [@valueUpdated]='item.prop2'>{{item.prop2.description}}</div>
          <div [@valueUpdated]='item.prop3'>{{item.prop3.description}}</div>              
         </div>
        </ion-col>
      </ion-row>
    </ion-grid>
  </div>

When either prop2 or prop3 is updated, I have a trigger (valueUpdated) to run a small animation...

 trigger('valueUpdated', [
    state('void => *', style({ transform: 'translate3d(0, 0, 0)' })),
    state('* <=> *', style({ transform: 'translate3d(0, 0, 0)' })),

    transition('void => *', []),
    transition('* => void', []),
    transition('* <=> *', [
      animate(1000, style({ color: 'green', fontWeight: 'bold' }))
    ])
  ]),

So, the relevant part of, when these change I see them highlight bold and green.

Until now, when I have incoming data I have code to check the values, look for an existing MyItem and then update any references to the IdDescription property IF they have changed (ie I find by the id). So this has always worked, and I only see them change to green (the animation) when I update the reference to the property.

Now, I am wondering if I am doing too much, (ie all this checking), and if Angular can actually do most of this for my. Also, I am considering starting to use ngrx, where the pattern is normally t change the whole reference to the list on any updates, and let Angular work out the DOM updates.

To test now I update the public data: Array<T>; reference. Initially I could see (via the UI and via dev tools) that when I did this, the whole list DOM was being regenerated.

this.data = newState; // now replace reference

I then found I could add a trackBy to do the comparisons... ie

<div *ngFor="let item of daya;trackBy: trackByFunction" 

with

public trackByFunction(index: number, item: MyItem) {
  if (!item)
    return null;

  return item.prop1.code;
}

ie prop1 is the id of the whole item.

Now, from what I could see, the items where no longer being replaced in the DOM, exactly what I was after - so Angular is doing the compare and determining the data data was the same, so not updating the main items.

However, I DID still see the property animations firing, so I am assuming these "child" DOM elements wee still being replaced.

My question is, is there a way to also use some sort of "trackBy" for these "child" properties, so Angular knows they have the same values, and so does not update the DOM (and hence the animations do not fire)?

halfer
  • 19,824
  • 17
  • 99
  • 186
peterc
  • 6,921
  • 9
  • 65
  • 131
  • you could create your own hash for tracking like `code-child1-id-child2-id` or something which is unique for all the items. Is that what you are looking for? – joyBlanks Sep 24 '19 at 04:03
  • I think similar to the above, I just assigned the trigger to a string, rather than the property object, eg the `description`, ie `
    {{item.prop2.description}}
    , and as far as I can see it "seemed" to work - need to test a little more.
    – peterc Sep 24 '19 at 08:08

1 Answers1

0

Try using NgZone runOutsideAngular(). You have to run your code outside of angular.

like this https://stackoverflow.com/a/39626378/1710501

GeetT
  • 315
  • 3
  • 10
  • thanks, but I do still want it to fire when the values are actually different. As I mentioned above I think just binding to a string within the object is working (ie @valueUpdated]='item.prop2.description). I have made a note of the above link... – peterc Sep 24 '19 at 08:12