2013-10-17 11 views
11

Mi chiedo se sia possibile eseguire alcune richiamate solo una volta dopo la valutazione di tutti (o solo alcuni) attributi di direttiva (senza ambito isolato). Gli attributi sono davvero grandi per passare la configurazione alla direttiva. Il fatto è che è possibile osservare ogni attributo separatamente e richiamare più volte la richiamata.

Nell'esempio abbiamo una direttiva senza ambito isolato che osserva due attributi: nome e cognome. Dopo ogni modifica action richiamata viene licenziato:

html

<button ng-click="name='John';surname='Brown'">Change all params</button> 
<div person name="{{name}}" surname="{{surname}}"></div> 

js

angular.module('app', []). 

directive('person', function() { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     $attrs.$observe('name', action); 
     $attrs.$observe('surname', action); 
    } 
} 
}); 

Plunker here.

Così l'effetto è che dopo aver cambiato nome e cognome durante uno scatto, action callback viene attivato due volte:

name: 
surname: Brown 

name: John 
surname: Brown 

Quindi la domanda è: può action essere licenziato solo una volta sia con nome e valori cognome cambiato?

risposta

11

È possibile utilizzare $watch per valutare una funzione personalizzata anziché un modello specifico.

cioè

$scope.$watch(function() { 
    return [$attrs.name, $attrs.surname]; 
}, action, true); 

che verrà eseguito su tutti i $digest cicli, e se $watch rileva la matrice di ritorno (o comunque si vuole strutturare il valore di ritorno della vostra funzione) non corrisponde al vecchio valore, la richiamata l'argomento $watch verrà attivato. Se si utilizza un oggetto come valore di ritorno, assicurarsi di lasciare il valore true per l'ultimo argomento su $watch in modo che lo $watch effettui un confronto approfondito.

+0

grazie mille! – iamtankist

0

Underscore (o lo-dash) ha una funzione once. Se si avvolge la propria funzione all'interno di once, è possibile assicurarsi che la funzione venga richiamata una sola volta.

angular.module('app', []). 

directive('person', function() { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     var once = _.once(action); 
     $attrs.$observe('name', once); 
     $attrs.$observe('surname', once); 
    } 
} 
}); 
+2

Sfortunatamente '_.once' attiverà la richiamata solo una volta ma solo con il primo attributo cambiato. Il mio punto è che il callback dovrebbe essere attivato dopo tutte le modifiche durante la singola fase '$ digest '. – kseb

0

Così, ho finito con la mia propria implementazione di observeAll metodo, che può attendere per diversi cambiamenti di attributi durante uno stack di chiamate. Funziona, tuttavia, non sono sicuro delle prestazioni.

La soluzione di @cmw sembra essere più semplice ma le prestazioni possono risentire di un numero elevato di parametri e di più esecuzioni di fasi $ digest, quando l'uguaglianza dell'oggetto viene valutata molte volte. Tuttavia ho deciso di accettare la sua risposta.

Qui di seguito potete trovare il mio approccio:

angular.module('utils.observeAll', []). 

factory('observeAll', ['$rootScope', function($rootScope) { 
    return function($attrs, callback) { 
     var o = {}, 
      callQueued = false, 
      args = arguments, 

      observe = function(attr) { 
       $attrs.$observe(attr, function(value) { 
        o[attr] = value; 
        if (!callQueued) { 
         callQueued = true; 
         $rootScope.$evalAsync(function() { 
          var argArr = []; 
          for(var i = 2, max = args.length; i < max; i++) { 
           var attr = args[i]; 
           argArr.push(o[attr]); 
          } 
          callback.apply(null, argArr); 
          callQueued = false; 
         }); 
        } 
       }); 
      }; 

     for(var i = 2, max = args.length; i < max; i++) { 
      var attr = args[i]; 
      if ($attrs.$attr[attr]) 
       observe(attr); 
     } 
    }; 
}]); 

Ed è possibile utilizzare nel vostro direttiva:

angular.module('app', ['utils.observeAll']). 

directive('person', ['observeAll', function(observeAll) { 
    return { 
    restrict: 'A', 
    link: function($scope, $elem, $attrs) { 
     var action = function() { 
      $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>'); 
     } 
     observeAll($attrs, action, 'name', 'surname'); 
    } 
} 
}]); 

Plunker here

0

ho risolto lo stesso problema che ho avuto con un altro approccio, anche se cercavo idee diverse. Mentre i suggerimenti di cmw stanno funzionando, ho confrontato la sua prestazione con la mia e ho visto che il metodo $ watch è chiamato troppe volte, quindi ho deciso di mantenere le cose come avevo implementato.

Ho aggiunto $ osservare le chiamate per entrambe le variabili che volevo tracciare e associarle a una chiamata antirimbalzo. Dal momento che entrambi sono modificati con pochissima differenza di tempo, entrambi i metodi $ osservare innescano la stessa chiamata di funzione, che viene eseguito dopo un breve ritardo:

var debounceUpdate = _.debounce(function() { 
    setMinAndMaxValue(attrs['minFieldName'], attrs['maxFieldName']); 
}, 100); 

attrs.$observe('minFieldName', function() { 
    debounceUpdate(); 
}); 

attrs.$observe('maxFieldName', function() { 
    debounceUpdate(); 
}); 
0

Ci sono diversi modi presentati per risolvere questo problema. Mi è piaciuta molto la soluzione antirimbalzo. Tuttavia, ecco la mia soluzione a questo problema. Questo combina tutti gli attributi in un singolo attributo e crea una rappresentazione JSON degli attributi a cui sei interessato. Ora, devi solo $ osservare un attributo e avere un buon risultato!

Ecco un fork del plunkr originale con l'attuazione: linkhttp://plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ

Problemi correlati