web-dev-qa-db-fra.com

Comment déclencher la détection de changement dans Angular2?

Je crée un service Facebook qui appelle l'API Facebook Facebook et je me demande comment implémenter au mieux la détection des modifications lorsque mes valeurs sont mises à jour.

J'ai un UserService qui a une propriété currentUser qui est un BehaviorSubject:

currentUser: Subject<User> = new BehaviorSubject<User>(new User(null));

Et quand je veux mettre à jour l'utilisateur en réponse au sdk facebook javascript m'informant que l'utilisateur s'est connecté ou déconnecté, je le mets à jour et j'ai besoin d'appeler tick() sur un ApplicationRef:

updateUser(user: User) {
  console.log('UserService.updateUser:', user);
  this.currentUser.next(user);
  this.appRef.tick(); // UI does not update without this
}

constructor(facebook: Facebook) {
  this.facebook.facebookEvents.filter(x => x != null 
         && x.eventName == 'auth.authResponseChange')
      .subscribe((event) => {
        this.updateUser(new User(event.data));
      }
}

Dans mon composant, je stocke le 'currentUser' du service utilisateur dans le constructeur et je lie à la propriété value:

<h2>Logged into Facebook as {{currentUser.value.name}}</h2>
<p>Is this you? &nbsp; <img src="{{currentUser.value.profilePicUrl}}"></p>

Est-ce que je fais quelque chose de mal? Existe-t-il un meilleur moyen que d'appeler ApplicationRef.tick () après un changement déclenché depuis une bibliothèque externe?

Éditer

J'ai essayé d'utiliser NgZone et cela ne fonctionne pas, en utilisant un événement différent qui renvoie les messages dans un flux en tant que pages de service à travers eux:

constructor(userService: UserService, private ref: ApplicationRef, private zone: NgZone)

...

this.postsSubject.subscribe((post) => {
  this.zone.runOutsideAngular(() => { // doesn't do anything
    this.posts.Push(post);
    console.log('postsSubject POST, count is ', this.posts.length);
    ref.tick(); // required to update bindings
  });
}

La console affiche le nombre incrémenté, mais la liaison html {{posts.length}} N'est mise à jour que si j'ajoute l'appel ref.tick() ...

Je pense avoir vu quelque part que vous pouvez mettre à disposition des `` entrées '' pour n'importe quel composant à partir du composant d'application de niveau supérieur, ce qui pourrait être la voie à suivre pour l'utilisateur connecté, mais pas d'autres appels comme obtenir des messages dans un flux ...

14
Jason Goemaat

Est-ce que je fais quelque chose de mal? Existe-t-il un meilleur moyen que d'avoir à appeler ApplicationRef.tick() après un changement déclenché depuis une bibliothèque externe?

Ça dépend. Si vous appelez les API Facebook en dehors de Angular (c'est-à-dire, l'enregistrement de gestionnaires d'événements asynchrones en dehors d'Angular), vous devrez exécuter manuellement la détection des modifications dans le cadre de la gestion des événements. Vous pouvez Soit

  • injectez NgZone puis appelez run() sur cet objet, qui exécutera la fonction passée (callback) à l'intérieur de la zone Angular, puis appellera automatiquement ApplicationRef().tick(), qui exécutera la détection des modifications pour l'ensemble de l'application, ou
  • injectez ApplicationRef et appelez tick() vous-même, ou
  • injectez ChangeDetectorRef et appelez detectChanges() vous-même - cela ne lancera la détection de changement que sur le composant et ses enfants

Notez que si vous injectez NgZone, vous ne voulez pas utiliser runOutsideAngular(). Cela exécutera la fonction passée (rappel) en dehors de la zone Angular, et il n'appellera pas ApplicationRef().tick(), donc la détection de changement ne s'exécutera pas.

Si vous appelez les API Facebook à l'intérieur d'Angular, vous pourrez peut-être éviter tout ce qui précède, car alors Angular (via son utilisation de Zone.js) devrait patcher les appels d'événements asynchrones de manière singulière. Ensuite, lorsque vos événements se déclencheront, ApplicationRef.tick() sera automatiquement appelée. Voir https://stackoverflow.com/a/34593821/215945 pour plus de discussion sur cette approche.

18
Mark Rajcok

Je ne sais pas si c'est mieux. Il y a deux autres façons de penser.

Utilisation de NgZone

Vous pouvez injecter NgZone et exécuter la méthode run avec un rappel:

 NgZone.run(()=>this.currentUser.next(user));

Utilisation de setTimeout

setTimeout déclenchera automatiquement la détection des modifications:

setTimeout(()=>this.currentUser.next(user));
13
pixelbits

Pourriez-vous nous donner la façon dont vous initialisez le fournisseur Facebook et vous votre service dans votre composant?

Option # 1

Votre comportement me fait changer le fait que le code Facebook s'exécute en dehors d'une zone Angular, donc Angular ne s'exécute pas lorsqu'un événement se déclenche. Vous

Vous pouvez également exécuter manuellement la détection des modifications:

import { Component, ChangeDetectorRef } from 'angular2/core';

@Component({
  (...)
})
export class MyComponent {
  constructor(private cdr:ChangeDetectorRef) {
  }

  someMethod() {
    this.cdr.detectChanges();
  }
}

Voir ces questions:

Option # 2

Peut-être que votre événement est déclenché avant que le composant enregistre un rappel sur le sujet currentUser. Dans ce cas, vous pourriez déclencher l'événement plus tard ...

5
Thierry Templier