Come ‘unwatch’ un’espressione

Diciamo che ho una ng-repeat con un grande array.

Quando ng-repeat viene eseguito, aggiunge ogni elemento di tale array a un ambito isolato, oltre ad avere l’array stesso in un ambito. Ciò significa che $ digest controlla l’intero array per le modifiche e, in più, controlla ogni singolo elemento in quell’array per le modifiche.

Vedi questo plunker come esempio di ciò di cui sto parlando.

Nel mio caso d’uso, non cambio mai un singolo elemento del mio array, quindi non ho bisogno di averli guardati. Cambierò solo l’intero array, nel qual caso ng-repeat re-renderà la tabella nella sua interezza. (Se mi sbaglio su questo per favore fammi sapere ..)

In una serie di (diciamo) 1000 righe, ci sono altre 1000 espressioni che non ho bisogno di valutare.

Come posso annullare la registrazione di ciascun elemento dall’osservatore mentre si sta ancora guardando l’array principale?

Forse invece di annullare la registrazione potrei avere più controllo sul mio $ digest e in qualche modo saltare ogni singola riga?

Questo caso specifico è in realtà un esempio di un problema più generale. So che $ watch restituisce una funzione di “deregistrazione” , ma ciò non aiuta quando una direttiva registra gli orologi, il che è il più delle volte.

Per avere un ripetitore con un grande array che non guardi per guardare ogni object.

Dovrai creare una direttiva personalizzata che accetta un argomento e un’espressione nell’array, quindi nella funzione di collegamento devi solo guardare quell’array e la funzione di collegamento aggiornerà a livello di codice l’HTML (anziché utilizzare un ng-repeat)

qualcosa come (psuedo-code):

app.directive('leanRepeat', function() { return { restrict: 'E', scope: { 'data' : '=' }, link: function(scope, elem, attr) { scope.$watch('data', function(value) { elem.empty(); //assuming jquery here. angular.forEach(scope.data, function(d) { //write it however you're going to write it out here. elem.append('
' + d + '
'); }); }); } }; });

… che sembra un dolore nel sedere.

Metodo alternativo di hackish

Potresti essere in grado di passare attraverso $scope.$$watchers ed esaminare $scope.$$watchers[0].exp.exp per vedere se corrisponde all’espressione che desideri rimuovere, quindi rimuoverlo con una semplice splice() chiamata. Il PITA qui, è che cose come Blah {{whatever}} Blah tra tag saranno l’espressione e includeranno anche i ritorni a capo.

Al rialzo, potresti essere in grado di passare in rassegna il $ scope della tua ng-repeat e rimuovere tutto, quindi aggiungere esplicitamente l’orologio che vuoi … Non so.

Ad ogni modo, sembra un hack.

Per rimuovere un osservatore creato da $ scope. $ Watch

Puoi annullare la registrazione di un $watch con la funzione restituita dalla chiamata $watch :

Ad esempio, per fare in modo che un $watch solo una volta:

 var unregister = $scope.$watch('whatever', function(){ alert('once!'); unregister(); }); 

Ovviamente puoi chiamare la funzione di annullamento della registrazione ogni volta che vuoi … è solo un esempio.

Conclusione: non c’è davvero un ottimo modo per fare esattamente quello che stai chiedendo

Ma una cosa da considerare: vale la pena di preoccuparsi? Inoltre è davvero una buona idea avere migliaia di record caricati in dozzine di DOMElement ciascuno? Cibo per la mente.

Spero che aiuti.


EDIT 2 (rimossa ctriggers idea)

$ watch restituisce una funzione che slega l’orologio $ quando viene chiamato. Quindi questo è tutto ciò che serve per “watchOnce”:

 var unwatchValue = scope.$watch('value', function(newValue, oldValue) { // Do your thing unwatchValue(); }); 

Modifica: vedi l’altra risposta che ho postato.

Sono andato e ho implementato l’idea di blesh in un modo separabile. La mia direttiva ngOnce distrugge solo l’ambito figlio che ngRepeat crea su ciascun elemento. Ciò significa che l’ambito non viene raggiunto dall’ambito dei suoi genitori scope.$digest e gli osservatori non vengono mai eseguiti.

Fonte ed esempio su JSFiddle

La stessa direttiva:

 angular.module('transclude', []) .directive('ngOnce', ['$timeout', function($timeout){ return { restrict: 'EA', priority: 500, transclude: true, template: '
', compile: function (tElement, tAttrs, transclude) { return function postLink(scope, iElement, iAttrs, controller) { $timeout(scope.$destroy.bind(scope), 0); } } }; }]);

Usandolo:

  
  • {{item.title}}: {{item.text}}
  • Nota ng-once non crea il proprio ambito, il che significa che può influenzare elementi di pari livello. Tutti fanno la stessa cosa:

      
  • {{item.title}}: {{item.text}}
  • {{item.title}}: {{item.text}}
  • {{item.title}}: {{item.text}}
  • Nota che questa potrebbe essere una pessima idea

    Puoi aggiungere la direttiva bindonce alla tua ng-repeat . Dovrai scaricarlo da https://github.com/pasvaz/bindonce .

    modifica : alcuni avvertimenti:

    Se stai usando l’interpolazione {{}} nel tuo modello, devi sostituirlo con .

    Se stai usando le direttive ng- , devi sostituirle con le giuste direttive.

    Inoltre, se stai mettendo bindonce e ng-repeat sullo stesso elemento, dovresti provare a spostare il bindonce su un elemento genitore (vedi https://github.com/Pasvaz/bindonce/issues/25#issuecomment-25457970 ) o aggiungendo track by al tuo ng-repeat .

    Se si utilizza angularjs 1.3 o versioni successive, è ansible utilizzare la syntax a binding singolo come

     
  • {{item}}
  • Ciò vincolerà il valore e rimuoverà gli osservatori una volta eseguito il primo ciclo di digest e il valore cambierà da undefined defined a defined per la prima volta.

    Un BLOG molto utile su questo.