Cómo esperar el enlace en el componente Angular 1.5 (sin scope scope.$reloj)


Estoy escribiendo una directiva Angular 1.5 y me estoy topando con un problema desagradable al tratar de manipular los datos enlazados antes de que existan.

Aquí está mi código:

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
      }
     }
    })
}
...

Como puede ver, estoy haciendo una llamada AJAX para obtener favoritos y luego comprobándolo con mi lista de formularios enlazados.

El problema es que la promesa se está cumpliendo incluso antes de que se rellene el enlace... para cuando ejecute el bucle, ctrl.formas es aún indefinido!

Sin utilizar un $alcance.watch watch (que es parte del atractivo de los componentes 1.5) ¿cómo espero a que se complete el enlace?

Author: gitsitgo, 2016-02-26

4 answers

Puedes usar los nuevos hooks de ciclo de vida , específicamente $onChanges, para detectar el primer cambio de un enlace llamando al método isFirstChange. Lea más sobre esto aquí.

Aquí hay un ejemplo:

<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>
 26
Author: Cosmin Ababei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-15 05:39:56

Tuve un problema similar, lo hice para evitar llamar al componente hasta que el valor que voy a enviar esté listo:

<form-selector ng-if="asyncValue" forms="asyncValue" ></form-selector>
 33
Author: Ced,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-20 06:25:09

El póster original decía:

La promesa se está cumpliendo incluso antes de que se rellene el enlace... sot hat para cuando ejecute el bucle, ctrl.formas es todavía indefinido

Desde AngularJS 1.5.3, tenemos hooks de ciclo de vida y para satisfacer la pregunta del OP, solo necesita mover el código que depende de los enlaces que se satisfagan dentro de $onInit():

On OnInit () - Llamado a cada controlador después de todos los controladores en un elemento han sido construidos y tenían sus enlaces inicializados (y antes de las funciones de enlace pre y post para las directivas sobre este elemento). Este es un buen lugar para poner el código de inicialización para su controlador.

Así que en el ejemplo:

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;
                }
              }
            }
          });
    }
}

Así que sí hay un $onChanges(changesObj), pero $onInit() aborda específicamente la cuestión original de cuándo podemos obtener una garantía de que las consolidaciones se han resuelto.

 8
Author: Oli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-09 01:35:28

Tuve un problema similar y encontré este artículo muy útil. http://blog.thoughtram.io/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html

Tengo una llamada ajax que llega al servidor al cargar la página y mi componente necesita el valor de retorno ajax para cargarse correctamente. Lo implementé de esta manera:

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

Ahora mi componente funciona perfectamente. Como referencia estoy usando Angular 1.5.6

 1
Author: tperdue321,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-05 15:50:44