Преглед изворни кода

messy fix for v-if and v-repeat, need more work.

Evan You пре 12 година
родитељ
комит
85496cf77f
4 измењених фајлова са 85 додато и 37 уклоњено
  1. 31 3
      src/compiler.js
  2. 17 10
      src/directives/if.js
  3. 36 18
      src/directives/repeat.js
  4. 1 6
      src/observer.js

+ 31 - 3
src/compiler.js

@@ -217,13 +217,22 @@ CompilerProto.setupObserver = function () {
             // since hooks were merged with child at head,
             // we loop reversely.
             while (i--) {
-                register(hook, fns[i])
+                registerHook(hook, fns[i])
             }
         } else if (fns) {
-            register(hook, fns)
+            registerHook(hook, fns)
         }
     })
 
+    // broadcast attached/detached hooks
+    observer
+        .on('hook:attached', function () {
+            broadcast(1)
+        })
+        .on('hook:detached', function () {
+            broadcast(0)
+        })
+
     function onGet (key) {
         check(key)
         DepsParser.catcher.emit('get', bindings[key])
@@ -235,12 +244,27 @@ CompilerProto.setupObserver = function () {
         bindings[key].update(val)
     }
 
-    function register (hook, fn) {
+    function registerHook (hook, fn) {
         observer.on('hook:' + hook, function () {
             fn.call(compiler.vm, options)
         })
     }
 
+    function broadcast (event) {
+        var children = compiler.childCompilers
+        if (children) {
+            var child, i = children.length
+            while (i--) {
+                child = children[i]
+                if (child.el.parentNode) {
+                    event = 'hook:' + (event ? 'attached' : 'detached')
+                    child.observer.emit(event)
+                    child.emitter.emit(event)
+                }
+            }
+        }
+    }
+
     function check (key) {
         if (!bindings[key]) {
             compiler.createBinding(key)
@@ -625,6 +649,10 @@ CompilerProto.defineMeta = function (key, binding) {
     var vm = this.vm,
         ob = this.observer,
         value = binding.value = vm[key] || this.data[key]
+    // remove initital meta in data, since the same piece
+    // of data can be observed by different VMs, each have
+    // its own associated meta info.
+    delete this.data[key]
     defGetSet(vm, key, {
         get: function () {
             if (Observer.shouldGet) ob.emit('get', key)

+ 17 - 10
src/directives/if.js

@@ -4,28 +4,35 @@ var config = require('../config'),
 module.exports = {
 
     bind: function () {
-        this.parent = this.el.parentNode
+        this.parent = this.el.parentNode || this.el.vue_if_parent
         this.ref = document.createComment(config.prefix + '-if-' + this.key)
-        this.el.vue_ref = this.ref
+        if (this.el.vue_if_ref) {
+            this.parent.insertBefore(this.ref, this.el.vue_if_ref)
+        }
+        this.el.vue_if_ref = this.ref
     },
 
     update: function (value) {
 
         var el = this.el
 
-        if (!this.parent) { // the node was detached when bound
+        // sometimes we need to create a VM on a detached node,
+        // e.g. in v-repeat. In that case, store the desired v-if
+        // state on the node itself so we can deal with it elsewhere.
+        el.vue_if = !!value
+
+        var parent   = this.parent,
+            ref      = this.ref,
+            compiler = this.compiler
+
+        if (!parent) {
             if (!el.parentNode) {
                 return
             } else {
-                this.parent = el.parentNode
+                parent = this.parent = el.parentNode
             }
         }
 
-        // should always have this.parent if we reach here
-        var parent   = this.parent,
-            ref      = this.ref,
-            compiler = this.compiler
-
         if (!value) {
             transition(el, -1, remove, compiler)
         } else {
@@ -52,7 +59,7 @@ module.exports = {
     },
 
     unbind: function () {
-        this.el.vue_ref = null
+        this.el.vue_if_ref = this.el.vue_if_parent = null
         var ref = this.ref
         if (ref.parentNode) {
             ref.parentNode.removeChild(ref)

+ 36 - 18
src/directives/repeat.js

@@ -1,7 +1,6 @@
 var Observer   = require('../observer'),
     utils      = require('../utils'),
     config     = require('../config'),
-    transition = require('../transition'),
     def        = utils.defProtected,
     ViewModel // lazy def to avoid circular dependency
 
@@ -247,17 +246,19 @@ module.exports = {
         var ctn = this.container,
             vms = this.vms,
             col = this.collection,
-            el, i, ref, item, primitive, detached
+            el, i, existing, ref, item, primitive, detached
 
         // append node into DOM first
         // so v-if can get access to parentNode
+        // TODO: logic here is a total mess.
         if (data) {
 
             if (this.old) {
                 i = indexOf(this.old, data)
             }
+            existing = i > -1
 
-            if (i > -1) { // existing, reuse the old VM
+            if (existing) { // existing, reuse the old VM
 
                 item = this.oldVMs[i]
                 // mark, so it won't be destroyed
@@ -285,23 +286,26 @@ module.exports = {
             ref = vms.length > index
                 ? vms[index].$el
                 : this.ref
-            // make sure it works with v-if
-            if (!ref.parentNode) ref = ref.vue_ref
-            if (!detached) {
-                if (i > -1) {
-                    // no need to transition existing node
-                    ctn.insertBefore(el, ref)
-                } else {
-                    // insert new node with transition
-                    transition(el, 1, function () {
-                        ctn.insertBefore(el, ref)
-                    }, this.compiler)
-                }
+            
+            // if ref VM's el is detached by v-if
+            // use its v-if ref node instead
+            if (!ref.parentNode) {
+                ref = ref.vue_if_ref
+            }
+
+            if (existing) {
+                // existing node
+                // if not detached, just re-insert to new location
+                // else re-insert its v-if ref node
+                ctn.insertBefore(detached ? el.vue_if_ref : el, ref)
             } else {
-                // detached by v-if
-                // just move the comment ref node
-                ctn.insertBefore(el.vue_ref, ref)
+                // new node, prepare it for v-if
+                el.vue_if_parent = ctn
+                el.vue_if_ref = ref
             }
+            // set index so vm can init with it
+            // and do not trigger stuff early
+            data.$index = index
         }
 
         item = item || new this.Ctor({
@@ -320,6 +324,7 @@ module.exports = {
             item.$destroy()
         } else {
             vms.splice(index, 0, item)
+
             // for primitive values, listen for value change
             if (primitive) {
                 item.$compiler.observer.on('set', function (key, val) {
@@ -328,6 +333,19 @@ module.exports = {
                     }
                 })
             }
+
+            // new instance and v-if doesn't want it detached
+            // good to insert.
+            if (!existing && el.vue_if !== false) {
+                if (this.compiler.init) {
+                    // do not transition on initial compile.
+                    ctn.insertBefore(item.$el, ref)
+                    item.$compiler.execHook('attached')
+                } else {
+                    // transition in...
+                    item.$before(ref)
+                }
+            }
         }
 
         return item

+ 1 - 6
src/observer.js

@@ -131,12 +131,7 @@ function watchArray (arr) {
  */
 function convert (obj, key) {
     var keyPrefix = key.charAt(0)
-    if (
-        (keyPrefix === '$' || keyPrefix === '_') &&
-        key !== '$index' &&
-        key !== '$key' &&
-        key !== '$value'
-    ) {
+    if (keyPrefix === '$' || keyPrefix === '_') {
         return
     }
     // emit set on bind