2013-11-21 18 views
8

Ho sezionato ng-repeat ed estratto i blocchi di codice allegati, visto che questi comprendono la logica che gestisce l'algoritmo che ripete (che voglio capire come funziona).Come funziona ng-repeat?

Ho alcune domande, ma dal momento che riguardano tutti gli aspetti interni di ng-repeat, ho scelto di chiedere loro tutti qui. Non vedo alcun motivo per separarli in domande SO diverse. Ho segnato in linea a quale riga (s) di codice fa riferimento ogni domanda.

  1. Perché hanno bisogno per assicurarsi che trackById non è la funzione nativa hasOwnProperty? (Questo è quello che la funzione assertNotHasOwnProperty fa, parte di API interna di angolare)
  2. Per quanto riguarda la mia intuizione andare, questo codice viene eseguito su elementi già nel ripetitore, quando si deve aggiornare la collezione - è appena li raccoglie e li spinge nella lista per l'elaborazione, giusto?
  3. Questo blocco di codice cerca ovviamente i duplicati nella raccolta ripetitori. Ma come esattamente ciò è al di là di me. Spiega per favore.
  4. Perché Angular deve memorizzare l'oggetto del blocco sia nextBlockMape in nextBlockOrder?
  5. Cosa sono block.endNode e block.startNode?
  6. Suppongo che la risposta alla domanda sopra chiarirà come funziona questo algoritmo, ma per favore spiegate perché deve verificare se lo nextNode è stato (stato) '$$NG_REMOVED'?
  7. Cosa succede qui? Di nuovo, presumo che la domanda 6 fornirà già una risposta a questo. Ma continui a farlo notare.

Come ho detto, ho scavato attraverso ng-repeat per trovare il codice che ritenevo rilevante per il meccanismo di ripetizione. Inoltre, capisco il resto della direttiva. Quindi, senza ulteriori indugi, ecco il codice (da v1.2.0):

 length = nextBlockOrder.length = collectionKeys.length; 
     for (index = 0; index < length; index++) { 
     key = (collection === collectionKeys) ? index : collectionKeys[index]; 
     value = collection[key]; 
     trackById = trackByIdFn(key, value, index); 

     // question #1 
     assertNotHasOwnProperty(trackById, '`track by` id'); 

     // question #2 
     if (lastBlockMap.hasOwnProperty(trackById)) { 
     block = lastBlockMap[trackById]; 
     delete lastBlockMap[trackById]; 
     nextBlockMap[trackById] = block; 
     nextBlockOrder[index] = block; 

     // question #3 
     } else if (nextBlockMap.hasOwnProperty(trackById)) { 
     // restore lastBlockMap 
     forEach(nextBlockOrder, function(block) { 
      if (block && block.startNode) lastBlockMap[block.id] = block; 
     }); 
     // This is a duplicate and we need to throw an error 
     throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}", 
                                       expression,  trackById); 

     // question #4 
     } else { 
     // new never before seen block 
     nextBlockOrder[index] = { id: trackById }; 
     nextBlockMap[trackById] = false; 
     } 
    } 


     for (index = 0, length = collectionKeys.length; index < length; index++) { 
     key = (collection === collectionKeys) ? index : collectionKeys[index]; 
     value = collection[key]; 
     block = nextBlockOrder[index]; 


     // question #5 
     if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode; 

     if (block.startNode) { 
      // if we have already seen this object, then we need to reuse the 
      // associated scope/element 
      childScope = block.scope; 

      // question #6 
      nextNode = previousNode; 
      do { 
      nextNode = nextNode.nextSibling; 
      } while(nextNode && nextNode[NG_REMOVED]); 
      if (block.startNode != nextNode) { 
      // existing item which got moved 
      $animate.move(getBlockElements(block), null, jqLite(previousNode)); 
      } 
      previousNode = block.endNode; 

     } else { 
      // new item which we don't know about 
      childScope = $scope.$new(); 
     } 

     // question #7 
     if (!block.startNode) { 
      linker(childScope, function(clone) { 
      clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); 
      $animate.enter(clone, null, jqLite(previousNode)); 
      previousNode = clone; 
      block.scope = childScope; 
      block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0]; 
      block.endNode = clone[clone.length - 1]; 
      nextBlockMap[block.id] = block; 
      }); 
     } 
     } 
     lastBlockMap = nextBlockMap; 

risposta

10

Dopo un po 'armeggiare con la direttiva, sono diventato familiarità con il codice ng-repeater s, e sono riuscito a rispondere ad alcune delle mie domande. Ho evidenziato in grassetto le cose che non riuscivo ancora a capire per conto mio, e apprezzerei se qualcuno potesse fare luce sulle grassetto parti:

  1. L'ID viene testato per hasOwnProperty, perché utilizzare questo metodo per verificare se l'ID è presente negli oggetti di iterazione (lastBlockMap, nextBlockMap) (questa procedura è illustrata di seguito). Non sono riuscito a scoprire in quale scenario questo può effettivamente accadere, comunque.
  2. Avevo ragione nella mia ipotesi. nextBlockMap contiene tutti gli elementi che verranno inclusi nella modifica del modello corrente. lastBlockMap contiene tutto dal precedente aggiornamento del modello. Utilizzato per trovare duplicati nella collezione.
  3. OK, questo è piuttosto semplice in realtà. In questo ciclo for, lo ng-repeat riempie lo nextBlockMap con gli articoli da lastBlockMap.Osservando l'ordine di if s, è facile vedere che se l'elemento non può essere trovato in lastBlockMap, ma è già presente in nextBlockMap (nel senso, era già copiato da lastBlockMap, e quindi il suo trackById appare due volte nella raccolta) - è un duplicato. Che cosa fa il forEach è semplicemente eseguire tutti gli articoli inizializzati in nextBlockMap (block s che hanno una proprietà startNode) e inviare l'ID nell'lastBlockMap. Non riesco comunque a capire perché sia ​​necessario.
  4. L'unico motivo ho trovato per separare nextBlockOrder (tutti trackById s di un array) da nextBlockMap (tutti block oggetti in un trackById hash), è questa linea, che lavorando con un array rende un funzionamento facile e semplice: if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;. Viene spiegato nelle risposte alle domande 5 e 6:
  5. block.startNode e block.endNode sono i primi e gli ultimi nodi DOM nel blocco che appartiene a un elemento nella raccolta ripetuto. Pertanto, questa riga qui imposta previousNode per fare riferimento all'ultimo nodo DOM dell'elemento precedente nel ripetitore.
  6. previousNode viene quindi utilizzato come primo nodo, in un ciclo che controlla come il DOM è cambiato quando gli oggetti sono stati spostati o rimossi dalla raccolta di ripetitori - di nuovo, solo nel caso in cui non stiamo lavorando con il primo blocco nell'array .
  7. Questo è facile - inizializza il blocco - assegnando $scope e startNode e endNode per un successivo riferimento e salva tutto in nextBlockMap. Il commento creato subito dopo l'elemento clonato, è lì per garantire che abbiamo sempre un endNode.
Problemi correlati