web-dev-qa-db-fra.com

Comment attendre la liaison dans Angular 1.5 (sans $ scope. $ Watch)

J'écris une directive Angular 1.5 et je rencontre un problème odieux d'essayer de manipuler des données liées avant qu'elles n'existent.

Voici mon code:

app.component('formSelector', {
  bindings: {
    forms: '='
  },
  controller: function(FormSvc) {

    var ctrl = this
    this.favorites = []

    FormSvc.GetFavorites()
    .then(function(results) {
    ctrl.favorites = results
    for (var i = 0; i < ctrl.favorites.length; i++) {
      for (var j = 0; j < ctrl.forms.length; j++) {
          if (ctrl.favorites[i].id == ctrl.newForms[j].id) ctrl.forms[j].favorite = true
      }
     }
    })
}
...

Comme vous pouvez le constater, je passe un appel AJAX) pour obtenir les favoris, puis je le compare à ma liste de formulaires reliés.

Le problème est que la promesse est remplie avant même que la liaison ne soit remplie ... de sorte qu'au moment où je lance la boucle, ctrl.forms n'est toujours pas défini!

Sans utiliser une montre $ scope. $ (Qui fait partie de l'appel de 1,5 composants), comment puis-je attendre que la liaison soit terminée?

35
tcmoore

Vous pouvez utiliser les nouveaux points d'ancrage du cycle de vie , plus précisément $onChanges , pour détecter le premier changement d'une liaison en appelant la méthode isFirstChange. En savoir plus sur ceci ici .

Voici un exemple:

<div ng-app="app" ng-controller="MyCtrl as $ctrl">
  <my-component binding="$ctrl.binding"></my-component>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js"></script>
<script>
  angular
    .module('app', [])
    .controller('MyCtrl', function($timeout) {
      $timeout(() => {
        this.binding = 'first value';
      }, 750);

      $timeout(() => {
        this.binding = 'second value';
      }, 1500);
    })
    .component('myComponent', {
      bindings: {
        binding: '<'
      },
      controller: function() {
        // Use es6 destructuring to extract exactly what we need
        this.$onChanges = function({binding}) {
          if (angular.isDefined(binding)) {
            console.log({
              currentValue: binding.currentValue, 
              isFirstChange: binding.isFirstChange()
            });
          }
        }
      }
    });
</script>
27
Cosmin Ababei

J'ai eu un problème similaire, je l'ai fait pour éviter d'appeler le composant tant que la valeur que je vais envoyer n'est pas prête:

<form-selector ng-if="asyncValue" forms="asyncValue" ></form-selector>
34
Ced

L'affiche originale disait:

la promesse est remplie avant même que la liaison ne soit remplie ... Au moment où je lance la boucle, ctrl.forms n'est toujours pas défini

Depuis AngularJS 1.5.3, nous avons cycles de cycle de vie et pour répondre à la question de l'OP, il vous suffit de déplacer le code dépendant des liaisons satisfaites à l'intérieur de $onInit():

$ onInit () - Appelé sur chaque contrôleur après que tous les contrôleurs d'un élément ont été construits et que leurs liaisons ont été initialisées (et avant les fonctions de liaison préalable et postérieures pour les directives de cet élément). C’est un bon endroit pour mettre le code d’initialisation de votre contrôleur.

Donc dans l'exemple:

app.component('formSelector', {
  bindings: {
    forms: '='
  },
  controller: function(FormSvc) {
    var ctrl = this;
    this.favorites = [];

    this.$onInit = function() {
      // At this point, bindings have been resolved.
      FormSvc
          .GetFavorites()
          .then(function(results) {
            ctrl.favorites = results;
            for (var i = 0; i < ctrl.favorites.length; i++) {
              for (var j = 0; j < ctrl.forms.length; j++) {
                if (ctrl.favorites[i].id == ctrl.newForms[j].id) {
                  ctrl.forms[j].favorite = true;
                }
              }
            }
          });
    }
}

Donc oui, il y a une $onChanges(changesObj), mais $onInit() répond spécifiquement à la question initiale de savoir quand pouvons-nous avoir la garantie que les liaisons ont été résolues.

7
Oli

J'ai eu un problème similaire et j'ai trouvé cet article très utile. http://blog.iblytram.io/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html

J'ai un appel ajax qui frappe le serveur au chargement de la page et mon composant a besoin de la valeur de retour ajax pour se charger correctement. Je l'ai implémenté de cette façon:

this.$onChanges = function (newObj) {
      if (newObj.returnValFromAJAX)
        this.returnValFromAJAX = newObj.returnValFromAJAX;
    };

Maintenant, mon composant fonctionne parfaitement. Pour référence, j'utilise Angular 1.5.6

1
tperdue321