web-dev-qa-db-fra.com

Pourquoi dois-je appeler detectChanges () avec la stratégie de détection de changement par défaut?

Je travaille sur une application Angular 4, mais j'ai un problème dans la mesure où je dois appeler this.changeDetectorRef.detectChanges(); pour mettre à jour la vue lorsque le modèle change. Par exemple, J'ai ce code qui est pour la pagination:

changePage(pageNumber) {
  this.currentPage = pageNumber;
  // why do I need to do this?
  this.changeDetectorRef.detectChanges();
}

J'ai explicitement défini la stratégie de détection des modifications sur le composant sur ChangeDetectionStrategy.Default mais cela n'a aucun effet. Cela se produit ici aussi lors de la souscription à un observable:

showResults(preference) {
  this.apiService.get('dining_autocomplete/', `?search=${preference}`)
    .subscribe((results) => {
      this.searchResults = results;
      // why do I need to do this?
      this.changeDetectorRef.detectChanges();
  });
}

Si je console.log()this.searchResults dans le TypeScript, j'obtiens les résultats attendus, mais si j'utilise {{ searchResults }} dans le HTML, il ne se met pas à jour jusqu'à un autre événement arrive, probablement quand il passe par un autre cycle de digestion.

Que pourrait-il se passer?

== EDIT ================================================ ============

Le code de mon composant ressemble à ceci:

import {ChangeDetectorRef, Component, Input, OnChanges} from "@angular/core";

import * as _ from 'lodash';

@Component({
  selector: 'dining-search-results',
  templateUrl: './dining-search-results.template.html',
  styleUrls: ['./dining-search-results.style.scss'],

})
export class DiningSearchResultsComponent {
  @Input() searchResults: any[];
  @Input() hotTablesOnly: boolean = false;
  @Input() memberBenefitsOnly: boolean = false;

  numberOfTagsToDisplay: number = 3;
  resultsPerPage: number = 5;
  currentPage: number = 1;

  constructor(private changeDetectorRef: ChangeDetectorRef) {
  }

  get filteredResults() {
    return this.searchResults ?
      this.searchResults.filter((r) => !((this.hotTablesOnly && !r.has_hot_table)
        || (this.memberBenefitsOnly && !r.has_member_benefit))) : [];
  }

  get pagedResults() {
    return _.chain(this.filteredResults)
      .drop(this.resultsPerPage * (this.currentPage - 1))
      .take(this.resultsPerPage)
      .value();
  }

  get totalPages(): number {
    return Math.ceil(this.filteredResults.length / this.resultsPerPage);
  }

  getInitialTags(tagsArray: any[], count: number): any[] {
    return _.take(tagsArray, count);
  }

  changePage(pageNumber) {
    this.currentPage = pageNumber;
    // why do I need to do this?
    this.changeDetectorRef.detectChanges();
  }
}

Lorsque changePage() est appelé et this.currentPage est mis à jour, les modifications ne sont pas reflétées dans le code HTML sauf si j'appelle detectChanges().

8
serlingpa

J'ai résolu le problème.

Un autre composant envoyant des notifications à ce composant exécutait Observable.fromEvent() en dehors de la zone Angular, donc la détection des modifications ne se produisait pas automatiquement en réponse à ces événements. Ce message sur zones et ce post StackOverflow sur la question a tenu la solution!

8
serlingpa

Vous pouvez entrer votre searchResults dans les composants enfants

@Input() searchResults;

après cela, vous le passez à travers le modèle parent

// parent template
<app-child [searchResults]="searchResults"></app-child>

vous pouvez l'utiliser dans le modèle enfant

// child template 
<my-date-picker [ngModel]="searchResults"></my-date-picker>

après cela, vous pouvez "écouter" les modifications de cette propriété dans le composant enfant

export class ChildComponent implements OnChanges {

  @Input() searchResults;

  constructor() { }

  ngOnChanges() {
    // some code
  }

Chaque fois que searchResults est modifié, les modifications seront renseignées dans le composant enfant, les valeurs dans le modèle enfant obtiendront la nouvelle valeur.

0
onetwo12