Просмотр исходного кода

fix delegation, and invoke updates during binding

Evan You 12 лет назад
Родитель
Сommit
01fae38fe0
3 измененных файлов с 82 добавлено и 92 удалено
  1. 10 9
      src/directives/each.js
  2. 1 1
      src/directives/on.js
  3. 71 82
      src/seed.js

+ 10 - 9
src/directives/each.js

@@ -101,7 +101,8 @@ module.exports = {
                 eachPrefixRE: new RegExp('^' + this.arg + '.'),
                 parentSeed: this.seed,
                 index: index,
-                data: data
+                data: data,
+                delegator: this.delegator
             })
         this.collection[index] = spore.scope
         return spore
@@ -113,21 +114,21 @@ module.exports = {
         })
     },
 
-    unbind: function (rm) {
+    unbind: function (reset) {
         if (this.collection && this.collection.length) {
-            var fn = rm ? '_destroy' : '_unbind'
+            var fn = reset ? '_destroy' : '_unbind'
             this.collection.forEach(function (scope) {
                 scope.$seed[fn]()
             })
             this.collection = null
         }
         var delegator = this.delegator
-        if (!delegator) return
-        var handlers = delegator.sdDelegationHandlers
-        for (var key in handlers) {
-            console.log('remove: ' + key)
-            delegator.removeEventListener(handlers[key].event, handlers[key])
+        if (delegator) {
+            var handlers = delegator.sdDelegationHandlers
+            for (var key in handlers) {
+                delegator.removeEventListener(handlers[key].event, handlers[key])
+            }
+            delete delegator.sdDelegationHandlers
         }
-        delete delegator.sdDelegationHandlers
     }
 }

+ 1 - 1
src/directives/on.js

@@ -38,7 +38,7 @@ module.exports = {
         if (this.seed.each && event !== 'blur') {
             // for each blocks, delegate for better performance
             // blur events dont bubble so exclude them
-            var delegator = this.seed.el.parentNode
+            var delegator = this.seed.delegator
             if (!delegator) return
             var marker    = this.expression,
                 dHandler  = delegator.sdDelegationHandlers[marker]

+ 71 - 82
src/seed.js

@@ -34,12 +34,12 @@ function Seed (el, options) {
     // check if there's passed in data
     var dataAttr = config.prefix + '-data',
         dataId = el.getAttribute(dataAttr),
-        data = (options && options.data) || config.datum[dataId]
+        data = (options && options.data) || config.datum[dataId] || {}
     el.removeAttribute(dataAttr)
 
     // if the passed in data is the scope of a Seed instance,
     // make a copy from it
-    if (data && data.$seed instanceof Seed) {
+    if (data.$seed instanceof Seed) {
         data = data.$dump()
     }
 
@@ -52,21 +52,12 @@ function Seed (el, options) {
     scope.$index    = options.index
     scope.$parent   = options.parentSeed && options.parentSeed.scope
 
-    // add event listener to update corresponding binding
-    // when a property is set
-    this.on('set', this._updateBinding.bind(this))
-
-    // now parse the DOM
-    this._compileNode(el, true)
-
     // copy data
-    if (data) {
-        for (var key in data) {
-            scope[key] = data[key]
-        }
+    for (var key in data) {
+        scope[key] = data[key]
     }
 
-    // if has controller function, apply it
+    // if has controller function, apply it so we have all the user definitions
     var ctrlID = el.getAttribute(ctrlAttr)
     if (ctrlID) {
         el.removeAttribute(ctrlAttr)
@@ -78,9 +69,19 @@ function Seed (el, options) {
         }
     }
 
+    // add event listener to update corresponding binding
+    // when a property is set
+    var self = this
+    this.on('set', function (key, value) {
+        self._bindings[key].update(value)
+    })
+
+    // now parse the DOM
+    this._compileNode(el, true)
+
     // extract dependencies for computed properties
     parsingDeps = true
-    this._computed.forEach(this._parseDeps.bind(this))
+    this._computed.forEach(parseDeps)
     delete this._computed
     parsingDeps = false
 }
@@ -156,10 +157,8 @@ Seed.prototype._compileTextNode = function (node) {
  */
 Seed.prototype._bind = function (directive) {
 
-    directive.seed = this
-
     var key = directive.key,
-        seed = this
+        seed = directive.seed = this
 
     if (this.each) {
         if (this.eachPrefixRE.test(key)) {
@@ -169,8 +168,8 @@ Seed.prototype._bind = function (directive) {
         }
     }
 
-    var ownerScope = determinScope(directive, seed),
-        binding = ownerScope._bindings[key] || ownerScope._createBinding(key)
+    seed = getScopeOwner(directive, seed)
+    var binding = seed._bindings[key] || seed._createBinding(key)
 
     // add directive to this binding
     binding.instances.push(directive)
@@ -180,6 +179,10 @@ Seed.prototype._bind = function (directive) {
     if (directive.bind) {
         directive.bind(binding.value)
     }
+
+    // set initial value
+    directive.update(binding.value)
+
 }
 
 /*
@@ -188,7 +191,9 @@ Seed.prototype._bind = function (directive) {
 Seed.prototype._createBinding = function (key) {
 
     var binding = new Binding()
+    binding.update(this.scope[key])
     this._bindings[key] = binding
+    if (binding.isComputed) this._computed.push(binding)
 
     var seed = this
     Object.defineProperty(this.scope, key, {
@@ -210,48 +215,6 @@ Seed.prototype._createBinding = function (key) {
     return binding
 }
 
-/*
- *  Update a binding with a new value.
- *  Triggered in the binding's setter.
- */
-Seed.prototype._updateBinding = function (key, value) {
-
-    var binding = this._bindings[key],
-        type = binding.type = typeOf(value)
-
-    // preprocess the value depending on its type
-    if (type === 'Object') {
-        if (value.get) { // computed property
-            this._computed.push(binding)
-            binding.isComputed = true
-        } else { // normal object
-            // TODO watchObject
-        }
-    } else if (type === 'Array') {
-        watchArray(value)
-        value.on('mutate', function () {
-            binding.emitChange()
-        })
-    }
-
-    binding.update(value)
-}
-
-/*
- *  Auto-extract the dependencies of a computed property
- *  by recording the getters triggered when evaluating it
- */
-Seed.prototype._parseDeps = function (binding) {
-    depsObserver.on('get', function (dep) {
-        if (!dep.dependents) {
-            dep.dependents = []
-        }
-        dep.dependents.push.apply(dep.dependents, binding.instances)
-    })
-    binding.value.get()
-    depsObserver.off('get')
-}
-
 /*
  *  Call unbind() of all directive instances
  *  to remove event listeners, destroy child seeds, etc.
@@ -301,36 +264,62 @@ Seed.prototype._dump = function () {
 /*
  *  Binding class
  */
- function Binding () {
-    this.value = undefined
+function Binding (value) {
+    this.value = value
     this.instances = []
     this.dependents = []
- }
+}
 
- Binding.prototype.update = function (value) {
-     if (value === undefined) {
-        value = this.value
-    } else {
-        this.value = value
+Binding.prototype.update = function (value) {
+    var type = typeOf(value),
+        self = this
+    // preprocess the value depending on its type
+    if (type === 'Object') {
+        if (value.get) { // computed property
+            this.isComputed = true
+        } else { // normal object
+            // TODO watchObject
+        }
+    } else if (type === 'Array') {
+        watchArray(value)
+        value.on('mutate', function () {
+            self.emitChange()
+        })
     }
-     this.instances.forEach(function (instance) {
-         instance.update(value)
-     })
-     this.emitChange()
- }
-
- Binding.prototype.emitChange = function () {
-     this.dependents.forEach(function (dept) {
-         dept.refresh()
-     })
- }
+    this.value = value
+    this.instances.forEach(function (instance) {
+        instance.update(value)
+    })
+    this.emitChange()
+}
+
+Binding.prototype.emitChange = function () {
+    this.dependents.forEach(function (dept) {
+        dept.refresh()
+    })
+}
 
 // Helpers --------------------------------------------------------------------
 
+/*
+ *  Auto-extract the dependencies of a computed property
+ *  by recording the getters triggered when evaluating it
+ */
+function parseDeps (binding) {
+    depsObserver.on('get', function (dep) {
+        if (!dep.dependents) {
+            dep.dependents = []
+        }
+        dep.dependents.push.apply(dep.dependents, binding.instances)
+    })
+    binding.value.get()
+    depsObserver.off('get')
+}
+
 /*
  *  determine which scope a key belongs to based on nesting symbols
  */
-function determinScope (key, seed) {
+function getScopeOwner (key, seed) {
     if (key.nesting) {
         var levels = key.nesting
         while (seed.parentSeed && levels--) {