Explorar o código

Refactor and comment x-for code

Caleb Porzio %!s(int64=5) %!d(string=hai) anos
pai
achega
e3fd0f2ab3
Modificáronse 1 ficheiros con 53 adicións e 33 borrados
  1. 53 33
      src/directives/for.js

+ 53 - 33
src/directives/for.js

@@ -3,37 +3,32 @@ import { transitionIn, transitionOut, getXAttrs } from '../utils'
 export function handleForDirective(component, el, expression, initialUpdate) {
     const { single, bunch, iterator1, iterator2 } = parseFor(expression)
 
-    var output = component.evaluateReturnExpression(bunch)
+    var items = component.evaluateReturnExpression(bunch)
 
+    // As we walk the array, we'll also walk the DOM (updating/creating as we go).
     var previousEl = el
-    output.forEach((i, index, group) => {
-        const nextEl = previousEl.nextElementSibling
-        let currentEl = nextEl
-        const keyAttr = getXAttrs(el, 'bind').filter(attr => attr.value === 'key')[0]
-
-        let keyAliases = { [single]: i }
-        if (iterator1) keyAliases[iterator1] = index
-        if (iterator2) keyAliases[iterator2] = group
-
-        const currentKey = keyAttr
-            ? component.evaluateReturnExpression(keyAttr.expression, keyAliases)
-            : index
-
-        if (nextEl && nextEl.__x_for_key !== undefined) {
-            // The key is not the same as the item in the dom.
-            if (nextEl.__x_for_key !== currentKey) {
-                // Let's see if it's somewhere else.
+    items.forEach((i, index, group) => {
+        const currentKey = getThisIterationsKeyFromTemplateTag(component, el, single, iterator1, iterator2, i, index, group)
+        let currentEl = previousEl.nextElementSibling
+
+        // Let's check and see if the x-for has already generated an element last time it ran.
+        if (currentEl && currentEl.__x_for_key !== undefined) {
+            // If the the key's don't match.
+            if (currentEl.__x_for_key !== currentKey) {
+                // We'll look ahead to see if we can find it further down.
                 var tmpCurrentEl = currentEl
                 while(tmpCurrentEl) {
+                    // If we found it later in the DOM.
                     if (tmpCurrentEl.__x_for_key === currentKey) {
+                        // Move it to where it's supposed to be in the DOM.
                         el.parentElement.insertBefore(tmpCurrentEl, currentEl)
+                        // And set it as the current element as if we just created it.
                         currentEl = tmpCurrentEl
                         break
                     }
 
                     tmpCurrentEl = (tmpCurrentEl.nextElementSibling && tmpCurrentEl.nextElementSibling.__x_for_key !== undefined) ? tmpCurrentEl.nextElementSibling : false
                 }
-
             }
 
             // Temporarily remove the key indicator to allow the normal "updateElements" to work
@@ -44,13 +39,22 @@ export function handleForDirective(component, el, expression, initialUpdate) {
             // Reset it for next time around.
             currentEl.__x_for_key = currentKey
         } else {
+            // There are no more .__x_for_key elements, meaning the page is first loading, OR, there are
+            // extra items in the array that need to be added as new elements.
+
+            // Let's create a clone from the template.
             const clone = document.importNode(el.content, true);
-            el.parentElement.insertBefore(clone, nextEl)
+            // Insert it where we are in the DOM.
+            el.parentElement.insertBefore(clone, currentEl)
 
+            // Set it as the current element.
             currentEl = previousEl.nextElementSibling
 
+            // And transition it in if it's not the first page load.
             transitionIn(currentEl, () => {}, initialUpdate)
 
+            // Now, let's walk the new DOM node and initialize everything,
+            // including new nested components.
             component.initializeElements(currentEl, {[single]: i})
 
             currentEl.__x_for_key = currentKey
@@ -59,23 +63,27 @@ export function handleForDirective(component, el, expression, initialUpdate) {
         previousEl = currentEl
     })
 
-    // Clean up oldies
-    var thing = (previousEl.nextElementSibling && previousEl.nextElementSibling.__x_for_key !== undefined) ? previousEl.nextElementSibling : false
+    // Now that we've added/updated/moved all the elements for the current state of the loop.
+    // Anything left over, we can get rid of.
+    var nextElementFromOldLoop = (previousEl.nextElementSibling && previousEl.nextElementSibling.__x_for_key !== undefined) ? previousEl.nextElementSibling : false
+
+    while(nextElementFromOldLoop) {
+        const nextElementFromOldLoopImmutable = nextElementFromOldLoop
 
-    while(thing) {
-        const thingImmutable = thing
-        transitionOut(thing, () => {
-            thingImmutable.remove()
+        transitionOut(nextElementFromOldLoop, () => {
+            nextElementFromOldLoopImmutable.remove()
         })
 
-        thing = (thing.nextElementSibling && thing.nextElementSibling.__x_for_key !== undefined) ? thing.nextElementSibling : false
+        nextElementFromOldLoop = (nextElementFromOldLoop.nextElementSibling && nextElementFromOldLoop.nextElementSibling.__x_for_key !== undefined) ? nextElementFromOldLoop.nextElementSibling : false
     }
 }
 
+// This was taken from VueJS 2.* core. Thanks Vue!
 function parseFor (expression) {
     const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
     const stripParensRE = /^\(|\)$/g
     const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
+
     const inMatch = expression.match(forAliasRE)
     if (! inMatch) return
     const res = {}
@@ -83,13 +91,25 @@ function parseFor (expression) {
     const single = inMatch[1].trim().replace(stripParensRE, '')
     const iteratorMatch = single.match(forIteratorRE)
     if (iteratorMatch) {
-      res.single = single.replace(forIteratorRE, '').trim()
-      res.iterator1 = iteratorMatch[1].trim()
-      if (iteratorMatch[2]) {
-        res.iterator2 = iteratorMatch[2].trim()
-      }
+        res.single = single.replace(forIteratorRE, '').trim()
+        res.iterator1 = iteratorMatch[1].trim()
+        if (iteratorMatch[2]) {
+            res.iterator2 = iteratorMatch[2].trim()
+        }
     } else {
-      res.single = single
+        res.single = single
     }
     return res
   }
+
+function getThisIterationsKeyFromTemplateTag(component, el, single, iterator1, iterator2, i, index, group) {
+    const keyAttr = getXAttrs(el, 'bind').filter(attr => attr.value === 'key')[0]
+
+    let keyAliases = { [single]: i }
+    if (iterator1) keyAliases[iterator1] = index
+    if (iterator2) keyAliases[iterator2] = group
+
+    return keyAttr
+        ? component.evaluateReturnExpression(keyAttr.expression, keyAliases)
+        : index
+}