0

Whatever I try, I just cannot seem to trigger change detection inside the template for an object that is part of an array of objects inside an array of objects. This is the (top) object structure:

export interface CompatibleCards {
  appid: number,
  banned: boolean,
  items: { addedToTrade: boolean, appPosition: number, banned: boolean, classid: number, iconUrl: string, marketHashName: string, name: string, numberOfListings: number, position: number, price: number, quantity: number, type: string }[],
  name: string,
  numberOfCardsInBadge: number,
  numberOfDifferentCards: number,
  totalNumberOfItems: number
}

And here is the template:

<ng-container *ngIf="this.compatibleCards?.length else no_compatible_cards">
  <h3>{{ numberOfCompatibleCards }} cartes compatibles</h3>
  <div class="conteneur-ensembles">
    <div *ngFor="let currentApp of compatibleCards" class="conteneur-ensemble-compatible">
      <h3><a href="https://steamcommunity.com/id/raggart/gamecards/{{ currentApp.appid }}/" target="_blank">{{ currentApp.name }} ({{ currentApp.numberOfDifferentCards }} / {{ currentApp.numberOfCardsInBadge }} cards)</a></h3>
      <div class="conteneur-images-cartes">
        <span *ngFor="let currentItem of currentApp.items" title="{{ currentItem.name }}" [ngClass]="{ 'conteneur-carte': true, 'ajoute-echange': currentItem.addedToTrade }" (click)="ajouterRetirerObjetPourEchange(currentItem, 'ajouter')">
          <img class="image-carte" src="http://cdn.steamcommunity.com/economy/image/{{ currentItem.iconUrl }}" alt="{{ currentItem.name }}" title="{{ currentItem.addedToTrade }}">
          <span *ngIf="currentItem.price && currentItem.price > 0" class="prix-carte" title="{{ currentItem.name }}">{{ currentItem.price / 100 | currency }}</span>
          <span *ngIf="currentApp.banned" class="prix-carte" title="{{ currentItem.name }}">--</span>
        </span>
      </div>
    </div>
  </div>
</ng-container>

As you can see, I am using the [ngClass] directive to populate the css classes based on the addedToTrade property of the CompatibleCards.items object. It populates properly, meaning that if I change the addedToTrade property before the first render, the class is present. However, when I try to update the object at a later time, using the code below, the template does not show or remove the class.

  ajouterRetirerObjetPourEchange(objetAEchanger: any, operation : "ajouter" | "retirer") : void {
    console.log(objetAEchanger);

    if(operation == "ajouter") {
      this.itemsForTrade.push({...objetAEchanger});
    } else {
      const positionObjetEchangeTrouve = this.itemsForTrade.findIndex((currentItem: any) => currentItem.classid == objetAEchanger.classid && currentItem.position == objetAEchanger.position);
      this.itemsForTrade.splice(positionObjetEchangeTrouve, 1);
    }

    this.compatibleCards[objetAEchanger.appPosition].items[objetAEchanger.position].addedToTrade = ! this.compatibleCards[objetAEchanger.appPosition].items[objetAEchanger.position].addedToTrade;
    // this.compatibleCards[objetAEchanger.appPosition].items = [].concat(this.compatibleCards[objetAEchanger.appPosition].items);
    // this.compatibleCards = [].concat(this.compatibleCards);
 }

Even reassigning the arrays with [].concat (or using spread [...this.compatibleCards] does not update the template (see the comments at the end of the function above).

Is there any way I can force my template to update itself after an update of an object inside the items array?

1 Answer 1

1

1st Solution

Try forcing a detect changes programatically.

Use in your compnent's constructor (via Injection) changeDetectorRef: ChangeDetectorRef

and then where you update your table use this.changeDetectorRef.detectChanges()

angular doc

2nd Solution

If that does not work then you can also try a 2nd alternative. You can declare your component to have a different detection strategy changeDetection:ChangeDetectionStrategy.OnPush

This will be applied on component level like

@Component({
  selector: 'my-app',
  template: `some template code`,
  changeDetection: ChangeDetectionStrategy.OnPush
})

Please take care however as this can have side effects. Detection strategy works from top to bottom. This means that if you declare this strategy on your component all of it's children component will follow this strategy also.

Also on this type of strategy Angular does not detect the changes on primitive fields. It detects changes only on references.

So when you do this.compatibleCards = [...this.compatibleCards] you change the reference of that field so it must be caught as change from Angular.

angular detection strategy doc

Sign up to request clarification or add additional context in comments.

1 Comment

I have tried that but the changes are still not reflected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.