Come funziona l'associazione dei dati in AngularJS?


1957

Come funziona l'associazione dei dati nel AngularJSframework?

Non ho trovato i dettagli tecnici sul loro sito . È più o meno chiaro come funziona quando i dati vengono propagati dalla vista al modello. Ma in che modo AngularJS tiene traccia delle modifiche alle proprietà del modello senza setter e getter?

Ho scoperto che ci sono osservatori JavaScript che potrebbero fare questo lavoro. Ma non sono supportati in Internet Explorer 6 e Internet Explorer 7 . Quindi, come fa AngularJS a sapere che ho modificato ad esempio quanto segue e ho riflesso questa modifica in una vista?

myobject.myproperty="new value";

10
Tenere presente che a partire da 1.0.0rc1 angolare è necessario specificare ng-model-instant ( docs-next.angularjs.org/api/… ) per aggiornare il proprio moder in modo coerente. Altrimenti verrà aggiornato in caso di sfocatura.
Sotomajor,

8
Il link di Marcello è apparentemente interrotto, quindi eccolo di nuovo: github.com/mhevery/angular.js/blob/master/docs/content/guide/…
riffraff

6
@orian, quel link è male. aggiornato a (presumo) è lo stesso - docs.angularjs.org/guide/databinding
Kevin Meredith

11
Per coloro che stanno ancora leggendo questa domanda, si noti che Angular 2.0 ha fortemente modificato il modo di eseguire il databinding da Angular 1.x al fine di lavorare con i componenti Web e risolvere molti problemi nelle risposte di seguito.
Agosto

Risposte:


2745

AngularJS ricorda il valore e lo confronta con un valore precedente. Questo è un controllo sporco di base. Se c'è una variazione di valore, quindi genera l'evento change.

Il $apply()metodo, che è quello che chiami quando stai passando da un mondo non AngularJS a un mondo AngularJS, chiama $digest(). Un digest è semplicemente un vecchio controllo sporco. Funziona su tutti i browser ed è totalmente prevedibile.

Per contrastare il controllo sporco (AngularJS) rispetto agli ascoltatori di modifica ( KnockoutJS e Backbone.js ): mentre il controllo sporco può sembrare semplice e persino inefficiente (lo affronterò più avanti), risulta che è semanticamente corretto in ogni momento, mentre gli ascoltatori del cambiamento hanno molti strani casi angolari e hanno bisogno di cose come il monitoraggio delle dipendenze per renderlo più semanticamente corretto. Il rilevamento delle dipendenze di KnockoutJS è una funzione intelligente per un problema che AngularJS non ha.

Problemi con i listener di modifica:

  • La sintassi è atroce, poiché i browser non la supportano in modo nativo. Sì, ci sono proxy, ma non sono semanticamente corretti in tutti i casi e, naturalmente, non ci sono proxy sui vecchi browser. La linea di fondo è che il controllo sporco ti consente di eseguire POJO , mentre KnockoutJS e Backbone.js ti costringono a ereditare dalle loro classi e ad accedere ai tuoi dati attraverso gli accessor.
  • Cambia coalescenza. Supponiamo di avere una serie di articoli. Supponiamo che tu voglia aggiungere elementi in un array, mentre esegui il ciclo continuo per aggiungere, ogni volta che aggiungi attivi eventi in caso di modifica, il che rende l'interfaccia utente. Questo è molto male per le prestazioni. Quello che vuoi è aggiornare l'interfaccia utente solo una volta, alla fine. Gli eventi di modifica sono troppo dettagliati.
  • Gli ascoltatori di modifiche si attivano immediatamente su un setter, il che è un problema, poiché l'ascoltatore di modifiche può ulteriormente modificare i dati, generando più eventi di modifica. Ciò è negativo poiché nel tuo stack potresti avere diversi eventi di modifica in corso contemporaneamente. Supponiamo di avere due array che devono essere mantenuti sincronizzati per qualsiasi motivo. Puoi solo aggiungere l'uno o l'altro, ma ogni volta che aggiungi lanci un evento di cambiamento, che ora ha una visione incoerente del mondo. Questo è un problema molto simile al blocco dei thread, che JavaScript evita poiché ogni callback viene eseguito esclusivamente e fino al completamento. Gli eventi di cambiamento rompono questo dato che i setter possono avere conseguenze di vasta portata che non sono intenzionali e non ovvie, il che crea nuovamente il problema del thread. Si scopre che ciò che si desidera fare è ritardare l'esecuzione dell'ascoltatore e garantire,

Che dire delle prestazioni?

Quindi può sembrare che siamo lenti, dal momento che il controllo sporco è inefficiente. È qui che dobbiamo guardare i numeri reali piuttosto che avere solo argomenti teorici, ma prima definiamo alcuni vincoli.

Gli umani sono:

  • Lento - Qualcosa di più veloce di 50 ms è impercettibile per l'uomo e quindi può essere considerato "istantaneo".

  • Limitato - Non puoi davvero mostrare più di circa 2000 informazioni a un essere umano su una singola pagina. Nulla di più è una pessima interfaccia utente e gli umani non possono comunque elaborarlo.

Quindi la vera domanda è questa: quanti confronti puoi fare su un browser in 50 ms? Questa è una domanda difficile a cui rispondere poiché molti fattori entrano in gioco, ma qui è un caso di prova: http://jsperf.com/angularjs-digest/6 che crea 10.000 osservatori. Su un browser moderno ci vogliono poco meno di 6 ms. Su Internet Explorer 8 sono necessari circa 40 ms. Come puoi vedere, al giorno d'oggi questo non è un problema nemmeno sui browser lenti. C'è un avvertimento: i confronti devono essere semplici per adattarsi al limite di tempo ... Sfortunatamente è troppo facile aggiungere un confronto lento in AngularJS, quindi è facile costruire applicazioni lente quando non sai cosa sta facendo. Ma speriamo di avere una risposta fornendo un modulo di strumentazione, che mostrerebbe quali sono i confronti lenti.

Si scopre che i videogiochi e le GPU utilizzano l'approccio del controllo sporco, in particolare perché è coerente. Fintanto che superano la frequenza di aggiornamento del monitor (in genere 50-60 Hz o ogni 16,6-20 ms), qualsiasi prestazione oltre quella è uno spreco, quindi è meglio disegnare più cose, piuttosto che aumentare gli FPS.


32
@Mark - sì, in KO devi solo aggiungere .extend ({throttle: 500}) per attendere 500 millisecondi dopo l'ultimo evento di modifica prima di agire su di esso.
Daniel Earwicker,

158
Tutta questa risposta è eccezionale a parte "Fintanto che ottengono 50 fps, qualsiasi prestazione oltre a ciò è uno spreco, poiché l'occhio umano non può apprezzarlo, quindi è meglio disegnare più cose, piuttosto che aumentare i fps". Tale affermazione è completamente errata a seconda dell'applicazione. L'occhio può sicuramente apprezzare più di 50 fps, e poiché i vari problemi con la realtà virtuale mostrano (leggi uno degli ultimi da John Carmack o Michael Abrash, in particolare il discorso VR di GDC 2013 di quest'ultimo), 50 fps è in realtà troppo lento. A parte questo, la tua risposta è ottima. Non voglio solo diffondere disinformazione.
Nate Bundy,

10
@DavidRivers ci sono µs proprio come in utorrent 1µs =
0,000001s

33
L'affermazione potrebbe essere facilmente pronunciata al contrario come "Il controllo sporco è una funzione intelligente per un problema che non ha knockout". ES6 utilizza osservabili e angolare si sta sbarazzando del controllo sporco. Il mondo reale raggiunse questa risposta e mostrò che era falsa.
conico

17
"Qualcosa di più veloce di 50 ms è impercettibile per l'uomo" non è vero. Durante i test abbiamo scoperto che i nostri clienti possono facilmente distinguere tra latenza di aggiornamento di 50 ms (20 fps) e latenza di aggiornamento di 16,6 ms (60 fps). Le scene girate alla velocità precedente diventano sempre più scarse delle valutazioni complessive "come si sono sentite" anche quando le persone non hanno registrato consapevolmente il framerate.
Crashworks,

323

Misko ha già fornito un'eccellente descrizione del funzionamento dei binding di dati, ma vorrei aggiungere la mia opinione sul problema delle prestazioni con l'associazione dei dati.

Come ha affermato Misko, circa 2000 associazioni sono dove inizi a vedere problemi, ma non dovresti comunque avere più di 2000 informazioni su una pagina. Questo può essere vero, ma non tutti i dati sono visibili all'utente. Una volta che inizi a creare qualsiasi tipo di widget o griglia di dati con associazione bidirezionale, puoi facilmente colpire 2000 associazioni, senza avere una UX scadente.

Si consideri, ad esempio, una casella combinata in cui è possibile digitare il testo per filtrare le opzioni disponibili. Questo tipo di controllo potrebbe avere ~ 150 elementi ed essere ancora altamente utilizzabile. Se ha alcune funzionalità extra (ad esempio una classe specifica sull'opzione attualmente selezionata), inizi a ottenere 3-5 associazioni per opzione. Inserisci tre di questi widget su una pagina (ad esempio, uno per selezionare un paese, l'altro per selezionare una città in detto paese e il terzo per selezionare un hotel) e ti trovi già tra 1000 e 2000 associazioni.

Oppure considera una griglia di dati in un'applicazione web aziendale. 50 righe per pagina non sono irragionevoli, ognuna delle quali potrebbe avere 10-20 colonne. Se lo costruisci con ng-repeats e / o disponi di informazioni in alcune celle che usano alcuni binding, potresti avvicinarti a 2000 binding solo con questa griglia.

Trovo che questo sia un grosso problema quando lavoro con AngularJS e l'unica soluzione che sono stato in grado di trovare finora è quella di costruire widget senza usare l'associazione bidirezionale, invece di usare ngOnce, annullare la registrazione di watcher e trucchi simili o costruire direttive che costruiscono il DOM con manipolazione di jQuery e DOM. Sento che questo sconfigge lo scopo di usare Angular in primo luogo.

Mi piacerebbe ricevere suggerimenti su altri modi per gestirlo, ma forse dovrei scrivere la mia domanda. Volevo inserire questo in un commento, ma si è rivelato essere troppo lungo per quello ...

TL; DR
L'associazione dei dati può causare problemi di prestazioni su pagine complesse.


26
Sì, secondo questo. La responsabilità principale della nostra app è quella di visualizzare le connessioni tra entità diverse. Una determinata pagina potrebbe avere 10 sezioni. Ogni sezione ha una tabella. Ogni tabella ha 2-5 filtri typeahead. Ogni tabella ha 2-5 colonne, ognuna con 10 righe. Molto rapidamente ci imbattiamo in problemi di perf, e andando con le opzioni "trucchi simili".
Scott Silvi,

10
È corretto affermare che Angular non riguarda solo l'associazione dei dati e alcune app potrebbero non voler utilizzare questa funzione per i motivi citati da altri? Penso che valga molto l'approccio di DI e modularità; avere un'auto-associazione magica è bello, ma in ogni implementazione esistente ci sono compromessi in termini di prestazioni. Il modo di Angular è senza dubbio superiore per la maggior parte delle app Web CRUD e le persone stanno solo colpendo un muro cercando di portarlo agli estremi. Sarebbe bello avere un metodo alternativo di ascolto degli eventi supportato, ma forse è fondamentalmente troppo complesso per un singolo framework?
Jason Boyd,

8
Angular ora ha un modo e il databinding bind-once per aiutare con questo problema. Inoltre ora ha indici per la fonte del ripetitore, che consente di modificare l'elenco senza ricostruire la dom per l'intero contenuto.
Gaute Løken

6
@MW. Onestamente pensavo che il bind-once fosse al centro. Ma sembra che non lo sia. È solo qualcosa che puoi fare quando scrivi le tue direttive, fondamentalmente collegando cose senza guardarle. Tuttavia c'è una mod ux per questo: github.com/pasvaz/bindonce
Gaute Løken

9
Un grido dal futuro per chiunque legga questo: una volta il binding è ora una caratteristica fondamentale di Angular v1.3, leggi di più qui: docs.angularjs.org/guide/expression
Nobita

158

Controllando sporco l' $scopeoggetto

Angolare mantiene un semplice arrayosservatore negli $scopeoggetti. Se ispezionate qualcuno $scope, troverete che contiene un arraychiamato $$watchers.

Ogni osservatore è uno objectche contiene tra l'altro

  1. Un'espressione che l'osservatore sta monitorando. Questo potrebbe essere solo un attributenome o qualcosa di più complicato.
  2. Un ultimo valore noto dell'espressione. Questo può essere verificato rispetto al valore calcolato corrente dell'espressione. Se i valori differiscono, l'osservatore attiverà la funzione e contrassegnerà $scopecome sporco.
  3. Una funzione che verrà eseguita se l'osservatore è sporco.

Come sono definiti gli osservatori

Esistono molti modi diversi per definire un osservatore in AngularJS.

  • Puoi esplicitamente $watchun attributeon $scope.

    $scope.$watch('person.username', validateUnique);
  • Puoi inserire {{}}un'interpolazione nel tuo modello (un watcher verrà creato per te sull'attuale $scope).

    <p>username: {{person.username}}</p>
  • Puoi chiedere una direttiva tale da ng-modeldefinire l'osservatore per te.

    <input ng-model="person.username" />

Il $digestciclo controlla tutti gli osservatori rispetto al loro ultimo valore

Quando interagiamo con AngularJS attraverso i normali canali (ng-model, ng-repeat, ecc.) Un ciclo digest verrà attivato dalla direttiva.

Un ciclo digest è un attraversamento in profondità di $scopee di tutti i suoi figli . Per ognuno $scope object, ripetiamo il suo $$watchers arraye valutiamo tutte le espressioni. Se il nuovo valore di espressione è diverso dall'ultimo valore noto, viene chiamata la funzione di watcher. Questa funzione potrebbe ricompilare parte del DOM, ricalcolare un valore $scope, attivare un AJAX request, tutto ciò che è necessario per farlo.

Ogni ambito viene attraversato e ogni espressione di controllo viene valutata e verificata rispetto all'ultimo valore.

Se viene attivato un osservatore, $scopeè sporco

Se viene attivato un osservatore, l'app sa che qualcosa è cambiato e che $scopeè contrassegnato come sporco.

Le funzioni di watcher possono modificare altri attributi su $scopeo su un genitore $scope. Se una $watcherfunzione è stata attivata, non possiamo garantire che le altre $scopesiano ancora pulite e quindi eseguiamo nuovamente l'intero ciclo di digest.

Questo perché AngularJS ha un'associazione a due vie, quindi i dati possono essere trasferiti $scopesull'albero. Potremmo modificare un valore su un valore superiore $scopeche è già stato digerito. Forse cambiamo un valore su $rootScope.

Se $digestè sporco, eseguiamo $digestnuovamente l'intero ciclo

Facciamo un ciclo continuo attraverso il $digestciclo fino a quando il ciclo digest non viene pulito (tutte le $watchespressioni hanno lo stesso valore che avevano nel ciclo precedente) o raggiungiamo il limite digest. Per impostazione predefinita, questo limite è impostato su 10.

Se raggiungiamo il limite digest, AngularJS genererà un errore nella console:

10 $digest() iterations reached. Aborting!

Il digest è difficile sulla macchina ma facile per lo sviluppatore

Come puoi vedere, ogni volta che qualcosa cambia in un'app AngularJS, AngularJS controllerà ogni singolo osservatore nella $scopegerarchia per vedere come rispondere. Per uno sviluppatore questo è un grande vantaggio per la produttività, poiché ora è necessario scrivere quasi nessun codice di cablaggio, AngularJS noterà solo se un valore è cambiato e renderà il resto dell'app coerente con la modifica.

Dal punto di vista della macchina, tuttavia, questo è estremamente inefficiente e rallenterà la nostra app se creiamo troppi osservatori. Misko ha citato una cifra di circa 4000 osservatori prima che la tua app sembrerà lenta sui browser più vecchi.

Questo limite è facile da raggiungere, ad esempio se si ng-repeatsupera un grande JSON array. È possibile attenuare ciò utilizzando funzionalità come l'associazione una tantum per compilare un modello senza creare osservatori.

Come evitare di creare troppi osservatori

Ogni volta che l'utente interagisce con la tua app, ogni singolo watcher nella tua app verrà valutato almeno una volta. Una parte importante dell'ottimizzazione di un'app AngularJS è la riduzione del numero di osservatori nel tuo $scopealbero. Un modo semplice per farlo è con l' associazione una volta .

Se disponi di dati che cambieranno raramente, puoi collegarli solo una volta usando la sintassi ::, in questo modo:

<p>{{::person.username}}</p>

o

<p ng-bind="::person.username"></p>

L'associazione verrà attivata solo quando viene eseguito il rendering del modello contenitore e vengono caricati i dati $scope.

Ciò è particolarmente importante quando si dispone di un ng-repeatcon molti articoli.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

Grazie @ user2864740 - anche se è giusto che la risposta di Misko sia la migliore. Conosce il framework meglio di chiunque altro, ed è piuttosto bello che si impegni con Stack Overflow ..
superluminario

4
Non sono d'accordo sul fatto che detta risposta dovrebbe essere in cima; c'è una differenza tra sapere qualcosa e scrivere una risposta pertinente / dettagliata per una domanda specifica. Ci sono modi migliori per ottenere riconoscimenti. Comunque ..
user2864740

1
Non dubito che ciò sia vero, ma domande domande e risposte risposte :)
user2864740

3
Bella risposta che copre il comportamento del controllo sporco e ciò che sta effettivamente valutando, una cosa non era troppo chiara nella risposta di Misko.
strider

3
Risposta superba e dettagliata. @superluminary, grazie per tale risposta. Inoltre, dopo aver letto questa risposta, arrivo al punto che non dobbiamo aggiungere un'espressione non idempotente come espressione osservata.
Mangu Singh Rajpurohit,

81

Questa è la mia comprensione di base. Potrebbe anche essere sbagliato!

  1. Gli oggetti vengono guardati passando una funzione (restituendo l'oggetto da guardare) al $watchmetodo.
  2. Le modifiche agli elementi controllati devono essere effettuate all'interno di un blocco di codice racchiuso dal $applymetodo.
  3. Alla fine del metodo viene invocato $applyil $digestmetodo che passa attraverso ciascuno degli orologi e controlla se sono cambiati dall'ultima volta che è stato $digesteseguito.
  4. Se vengono rilevate modifiche, il digest viene richiamato nuovamente fino a quando tutte le modifiche non si stabilizzano.

Nello sviluppo normale, la sintassi di associazione dei dati nell'HTML dice al compilatore AngularJS di creare gli orologi per te e i metodi del controller sono $applygià eseguiti all'interno . Quindi per lo sviluppatore dell'applicazione è tutto trasparente.


4
quando viene attivato il metodo apply?
numan salati

3
@EliseuMonar Il ciclo digest viene eseguito come risultato di un evento o chiamando $ apply (), non viene chiamato periodicamente in base a un timer. vedi Come funziona la funzione $ watch di AngularJS? e come funziona l'associazione e la digestione in AngularJS?
adl

1
@remi, non sono preoccupato per l'ultima versione di AngularJS. Stanno già utilizzando proxy o Object.observe? In caso contrario, sono ancora nell'era del controllo sporco, che crea un ciclo a tempo per vedere se gli attributi del modello sono cambiati.
Eliseu Monar dos Santos

1
ho letto che digest funzionerà un massimo di dieci volte sitepoint.com/understanding-angulars-apply-digest
user137717

62

Mi sono chiesto questo per un po '. Senza setter come si AngularJSnotano le modifiche $scopeall'oggetto? Li interroga?

Ciò che effettivamente fa è questo: qualsiasi posto "normale" in cui modifichi il modello è già stato chiamato dalle viscere di AngularJS, quindi ti chiama automaticamente $applydopo l'esecuzione del codice. Supponiamo che il tuo controller abbia un metodo collegato a ng-clickqualche elemento. Poiché AngularJScollega la chiamata di quel metodo insieme per te, ha la possibilità di farlo $applynel posto appropriato. Allo stesso modo, per le espressioni che appaiono proprio nelle viste, quelle vengono eseguite da AngularJScosì fa il $apply.

Quando la documentazione parla della necessità di chiamare $applymanualmente il codice al di fuori diAngularJS esso, si tratta di codice che, quando eseguito, non deriva da AngularJSse stesso nello stack di chiamate.


32

Spiegare con le immagini:

Il Data Binding richiede una mappatura

Il riferimento nell'ambito non è esattamente il riferimento nel modello. Quando si collegano i dati a due oggetti, è necessario un terzo che ascolti il ​​primo e modifichi l'altro.

inserisci qui la descrizione dell'immagine

Qui, quando si modifica il <input>, si tocca il data-ref3 . E il classico meccanismo di associazione dei dati cambierà i dati-ref4 . Quindi come {{data}}si sposteranno le altre espressioni?

Gli eventi portano a $ digest ()

inserisci qui la descrizione dell'immagine

Angolare mantiene un oldValuee newValuedi ogni legame. E dopo ogni evento angolare , il famoso $digest()loop controllerà la WatchList per vedere se qualcosa è cambiato. Questi eventi angolari sono ng-click, ng-change, $httpoperazioni ... Il $digest()ciclo volontà purchè ogni oldValuedifferisce dalla newValue.

Nell'immagine precedente, noterà che i dati-ref1 e data-ref2 sono cambiati.

conclusioni

È un po 'come l'uovo e il pollo. Non si sa mai chi inizia, ma si spera che funzioni quasi sempre come previsto.

L'altro punto è che puoi capire facilmente l'impatto profondo di un semplice binding sulla memoria e sulla CPU. Spero che i desktop siano abbastanza grassi per gestirlo. I telefoni cellulari non sono così forti.


22

Ovviamente non vi è alcun controllo periodico Scopese vi siano cambiamenti negli Oggetti ad esso associati. Non tutti gli oggetti collegati all'ambito vengono guardati. Scope mantiene prototipicamente un osservatore $$ . Scopescorre solo attraverso questo $$watchersquando $digestviene chiamato.

Angular aggiunge un osservatore agli osservatori $$ per ognuno di questi

  1. {{expression}} - Nei tuoi modelli (e in qualsiasi altro luogo in cui sia presente un'espressione) o quando definiamo ng-model.
  2. $ scope. $ watch ('espressione / funzione') - Nel tuo JavaScript possiamo semplicemente collegare un oggetto scope da angolare da guardare.

La funzione $ watch accetta tre parametri:

  1. La prima è una funzione watcher che restituisce semplicemente l'oggetto o possiamo semplicemente aggiungere un'espressione.

  2. La seconda è una funzione di ascolto che verrà chiamata in caso di modifica dell'oggetto. Tutte le cose come le modifiche al DOM saranno implementate in questa funzione.

  3. Il terzo è un parametro facoltativo che accetta un valore booleano. Se la sua profondità angolare vera osserva l'oggetto e se la sua angolazione falsa fa solo un riferimento a guardare l'oggetto. L'implementazione approssimativa di $ watch è simile a questa

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

C'è una cosa interessante in Angular chiamata Digest Cycle. Il ciclo $ digest inizia come risultato di una chiamata a $ scope. $ Digest (). Si supponga di modificare un modello $ scope in una funzione del gestore tramite la direttiva ng-click. In tal caso AngularJS avvia automaticamente un ciclo $ digest chiamando $ digest (). Oltre a ng-click, ci sono molte altre direttive / servizi integrati che ti consentono di cambiare modello (ad esempio ng-model, $ timeout, ecc.) e attiva automaticamente un ciclo $ digest. L'implementazione approssimativa di $ digest appare così.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

Se utilizziamo la funzione setTimeout () di JavaScript per aggiornare un modello di ambito, Angular non ha modo di sapere cosa potresti cambiare. In questo caso è nostra responsabilità chiamare $ apply () manualmente, che innesca un ciclo $ digest. Allo stesso modo, se si dispone di una direttiva che imposta un listener di eventi DOM e modifica alcuni modelli all'interno della funzione del gestore, è necessario chiamare $ apply () per assicurarsi che le modifiche abbiano effetto. La grande idea di $ apply è che possiamo eseguire del codice che non è a conoscenza di Angular, che può ancora cambiare le cose nell'ambito. Se racchiudiamo quel codice in $ apply, ci occuperemo di chiamare $ digest (). Implementazione approssimativa di $ apply ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

15

AngularJS gestisce il meccanismo di associazione dei dati con l'aiuto di tre potenti funzioni: $ watch () , $ digest () e $ apply () . Il più delle volte AngularJS chiamerà $ scope. $ Watch () e $ scope. $ Digest (), ma in alcuni casi potrebbe essere necessario chiamare queste funzioni manualmente per aggiornare con nuovi valori.

$ watch () : -

Questa funzione viene utilizzata per osservare le modifiche in una variabile nell'ambito $ scope. Accetta tre parametri: espressione, listener e oggetto di uguaglianza, dove listener e oggetto di uguaglianza sono parametri opzionali.

$ digest () -

Questa funzione scorre attraverso tutti gli orologi nell'oggetto $ scope e i suoi oggetti $ scope $
(se presenti). Quando $ digest () scorre sugli orologi, controlla se il valore dell'espressione è cambiato. Se il valore è cambiato, AngularJS chiama il listener con nuovo valore e vecchio valore. La funzione $ digest () viene chiamata ogni volta che AngularJS lo ritiene necessario. Ad esempio, dopo aver fatto clic su un pulsante o dopo una chiamata AJAX. Potresti avere alcuni casi in cui AngularJS non chiama la funzione $ digest () per te. In tal caso devi chiamarlo da solo.

$ apply () -

Angular aggiorna automaticamente solo le modifiche al modello che si trovano all'interno del contesto AngularJS. Quando si cambia in qualsiasi modello al di fuori del contesto angolare (come eventi DOM del browser, setTimeout, XHR o librerie di terze parti), è necessario informare Angular delle modifiche chiamando $ apply () manualmente. Quando la chiamata della funzione $ apply () termina internamente, AngularJS chiama $ digest () internamente, quindi tutti i collegamenti ai dati vengono aggiornati.


7

È successo che avevo bisogno di collegare un modello di dati di una persona con un modulo, quello che ho fatto è stato un mapping diretto dei dati con il modulo.

Ad esempio se il modello avesse qualcosa del tipo:

$scope.model.people.name

L'ingresso di controllo del modulo:

<input type="text" name="namePeople" model="model.people.name">

In questo modo se si modifica il valore del controller degli oggetti, questo si rifletterà automaticamente nella vista.

Un esempio in cui ho superato il modello viene aggiornato dai dati del server è quando chiedi un codice postale e un codice postale basato sui carichi scritti un elenco di colonie e città associate a quella vista e, per impostazione predefinita, imposta il primo valore con l'utente. E questo ho funzionato molto bene, quello che succede è che a angularJSvolte ci vogliono alcuni secondi per aggiornare il modello, per fare questo puoi mettere un filatore mentre visualizzi i dati.


14
Ho letto questa risposta 5 volte e ancora non capisco cosa significhi qui.
sbedulin,

1
La risposta mi sembra un enigma
Aman,

6
  1. L'associazione dati unidirezionale è un approccio in cui un valore viene preso dal modello di dati e inserito in un elemento HTML. Non è possibile aggiornare il modello dalla vista. È utilizzato nei sistemi di template classici. Questi sistemi associano i dati in una sola direzione.

  2. L'associazione di dati nelle app angolari è la sincronizzazione automatica dei dati tra il modello e i componenti di visualizzazione.

L'associazione dei dati consente di trattare il modello come unica fonte di verità nella propria applicazione. La vista è una proiezione del modello in ogni momento. Se il modello viene modificato, la vista riflette la modifica e viceversa.


5

Ecco un esempio di associazione dei dati con AngularJS, utilizzando un campo di input. Spiegherò più avanti

Codice HTML

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

Codice AngularJS

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

Come puoi vedere nell'esempio sopra, AngularJS usa ng-modelper ascoltare e guardare cosa succede sugli elementi HTML, specialmente sui inputcampi. Quando succede qualcosa, fai qualcosa. Nel nostro caso, ng-modelè vincolato alla nostra vista, usando la notazione dei baffi {{}}. Tutto ciò che viene digitato all'interno del campo di input viene immediatamente visualizzato sullo schermo. E questa è la bellezza dell'associazione dei dati, usando AngularJS nella sua forma più semplice.

Spero che sia di aiuto.

Guarda un esempio funzionante qui su Codepen


5

AngularJs supporta l'associazione dati bidirezionale .
Significa che puoi accedere ai dati Visualizza -> Controller e controller -> Visualizza

Per es.

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

OPERAZIONE

Peter

Puoi associare i dati in ng-modelLike: -
2)

<input ng-model="name" />

<div> {{ name }} </div>

Qui nell'esempio sopra, qualunque sia l'input fornito dall'utente, sarà visibile nel <div>tag.

Se si desidera associare l'input da HTML al controller: -
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

Qui se si desidera utilizzare l'input namenel controller,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-modellega la nostra visione e la rende in espressione {{ }}.
ng-modelsono i dati mostrati all'utente nella vista e con i quali l'utente interagisce.
Quindi è facile associare i dati in AngularJs.


4

Angular.js crea un watcher per ogni modello che creiamo in vista. Ogni volta che un modello viene cambiato, una classe "ng-dirty" viene aggiunta al modello, quindi l'osservatore osserverà tutti i modelli che hanno la classe "ng-dirty" e aggiornerà i loro valori nel controller e viceversa.


3

associazione dati:

Che cos'è l'associazione dei dati?

Ogni volta che l'utente modifica i dati nella vista, si verifica un aggiornamento di tale modifica nel modello di ambito e viceversa.

Come è possibile?

Risposta breve: con l'aiuto del ciclo digest.

Descrizione: Angular js imposta l'osservatore sul modello di ambito, che attiva la funzione di ascolto in caso di modifica del modello.

$scope.$watch('modelVar' , function(newValue,oldValue){

// Codice di aggiornamento Dom con nuovo valore

});

Quindi quando e come viene chiamata la funzione watcher?

La funzione Watcher viene chiamata come parte del ciclo digest.

Il ciclo digest viene chiamato automaticamente attivato come parte di j angolari incorporati in direttive / servizi come ng-model, ng-bind, $ timeout, ng-click e altri .. che consentono di attivare il ciclo digest.

Funzione del ciclo del digest:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

vale a dire$rootScope.$apply()

Nota: $ apply () è uguale a $ rootScope. $ Digest () significa che il controllo sporco inizia direttamente dalla radice o dall'alto o dall'ambito padre fino a tutti gli ambiti $ figlio nell'applicazione js angolare.

Le funzionalità di cui sopra funzionano nel browser IE per le versioni menzionate anche solo assicurandosi che l'applicazione sia angolare js, il che significa che si sta utilizzando il file di script del framework angularjs a cui si fa riferimento nel tag dello script.

Grazie.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.