diff --git a/component.json b/component.json
index b60fe6763..5bdf4ab27 100644
--- a/component.json
+++ b/component.json
@@ -23,6 +23,7 @@
"src/transition.js",
"src/batcher.js",
"src/directives/index.js",
+ "src/directives/component.js",
"src/directives/if.js",
"src/directives/repeat.js",
"src/directives/on.js",
diff --git a/src/batcher.js b/src/batcher.js
index 7b4ac2c0e..1082ba1cb 100644
--- a/src/batcher.js
+++ b/src/batcher.js
@@ -29,9 +29,8 @@ BatcherProto.flush = function () {
// as we execute existing jobs
for (var i = 0; i < this.queue.length; i++) {
var job = this.queue[i]
- if (job.cancelled) continue
- if (job.execute() !== false) {
- this.has[job.id] = false
+ if (!job.cancelled) {
+ job.execute()
}
}
this.reset()
diff --git a/src/binding.js b/src/binding.js
index c105e19a9..8861645f2 100644
--- a/src/binding.js
+++ b/src/binding.js
@@ -39,8 +39,6 @@ BindingProto.update = function (value) {
execute: function () {
if (!self.unbound) {
self._update()
- } else {
- return false
}
}
})
diff --git a/src/compiler.js b/src/compiler.js
index f77d1d7e0..3eb7bee80 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -325,6 +325,8 @@ CompilerProto.observeData = function (data) {
*/
CompilerProto.compile = function (node, root) {
+ /* jshint boss: true */
+
var compiler = this,
nodeType = node.nodeType,
tagName = node.tagName
@@ -335,22 +337,15 @@ CompilerProto.compile = function (node, root) {
if (utils.attr(node, 'pre') !== null) return
// special attributes to check
- var repeatExp,
- viewExp,
- withExp,
- directive,
- // resolve a standalone child component with no inherited data
- hasComponent = this.resolveComponent(node, undefined, true)
+ var directive, repeatExp, viewExp, Component
// It is important that we access these attributes
// procedurally because the order matters.
- //
// `utils.attr` removes the attribute once it gets the
// value, so we should not access them all at once.
// v-repeat has the highest priority
// and we need to preserve all other attributes for it.
- /* jshint boss: true */
if (repeatExp = utils.attr(node, 'repeat')) {
// repeat block cannot have v-id at the same time.
@@ -370,18 +365,26 @@ CompilerProto.compile = function (node, root) {
}
// Child component has 2nd highest priority
- } else if (root !== true && ((withExp = utils.attr(node, 'with')) || hasComponent)) {
+ } else if (root !== true && (Component = this.resolveComponent(node, undefined, true))) {
- withExp = Directive.split(withExp || '')
- withExp.forEach(function (exp, i) {
- var directive = Directive.parse('with', exp, compiler, node)
- if (directive) {
- // notify the directive that this is the
- // last expression in the group
- directive.last = i === withExp.length - 1
- compiler.deferred.push(directive)
- }
- })
+ directive = Directive.parse('component', '', compiler, node)
+ if (directive) {
+ directive.Ctor = Component
+ compiler.deferred.push(directive)
+ }
+
+ // should build component
+
+ // withExp = Directive.split(withExp || '')
+ // withExp.forEach(function (exp, i) {
+ // var directive = Directive.parse('with', exp, compiler, node)
+ // if (directive) {
+ // // notify the directive that this is the
+ // // last expression in the group
+ // directive.last = i === withExp.length - 1
+ // compiler.deferred.push(directive)
+ // }
+ // })
} else {
@@ -432,7 +435,13 @@ CompilerProto.compileNode = function (node) {
exp = exps[j]
dirname = attr.name.slice(prefix.length)
directive = Directive.parse(dirname, exp, this, node)
- this.bindDirective(directive)
+
+ if (dirname === 'with') {
+ this.bindDirective(directive, this.parent)
+ } else {
+ this.bindDirective(directive)
+ }
+
}
} else if (config.interpolate) {
// non directive attribute, check interpolation tags
@@ -811,7 +820,10 @@ CompilerProto.eval = function (exp, data) {
*/
CompilerProto.resolveComponent = function (node, data, test) {
- var exp = utils.attr(node, 'component', test),
+ // late require to avoid circular deps
+ ViewModel = ViewModel || require('./viewmodel')
+
+ var exp = utils.attr(node, 'component'),
tagName = node.tagName,
id = this.eval(exp, data),
tagId = (tagName.indexOf('-') > 0 && tagName.toLowerCase()),
@@ -822,8 +834,10 @@ CompilerProto.resolveComponent = function (node, data, test) {
}
return test
- ? Ctor
- : Ctor || (ViewModel || (ViewModel = require('./viewmodel')))
+ ? exp === ''
+ ? ViewModel
+ : Ctor
+ : Ctor || ViewModel
}
/**
diff --git a/src/directives/component.js b/src/directives/component.js
new file mode 100644
index 000000000..8d0b57b83
--- /dev/null
+++ b/src/directives/component.js
@@ -0,0 +1,20 @@
+module.exports = {
+
+ isLiteral: true,
+
+ bind: function () {
+ if (!this.el.vue_vm) {
+ this.component = new this.Ctor({
+ el: this.el,
+ parent: this.vm
+ })
+ }
+ },
+
+ unbind: function () {
+ if (this.component) {
+ this.component.$destroy()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/directives/index.js b/src/directives/index.js
index aa170adbd..598d40923 100644
--- a/src/directives/index.js
+++ b/src/directives/index.js
@@ -13,6 +13,7 @@ module.exports = {
style : require('./style'),
partial : require('./partial'),
view : require('./view'),
+ component : require('./component'),
attr: {
bind: function () {
diff --git a/src/directives/with.js b/src/directives/with.js
index 17071fc4b..1c0d8faad 100644
--- a/src/directives/with.js
+++ b/src/directives/with.js
@@ -3,88 +3,136 @@ var utils = require('../utils')
module.exports = {
bind: function () {
- if (this.el.vue_vm) {
- this.subVM = this.el.vue_vm
- var compiler = this.subVM.$compiler
- if (this.arg && !compiler.bindings[this.arg]) {
- compiler.createBinding(this.arg)
+
+ var self = this,
+ childKey = self.arg,
+ parentKey = self.key,
+ compiler = self.compiler,
+ owner = self.binding.compiler
+
+ if (compiler === owner) {
+ this.alone = true
+ return
+ }
+
+ if (childKey) {
+ if (!compiler.bindings[childKey]) {
+ compiler.createBinding(childKey)
}
- } else if (this.isEmpty) {
- this.build()
+ // sync changes on child back to parent
+ compiler.observer.on('change:' + childKey, function (val) {
+ if (compiler.init) return
+ if (!self.lock) {
+ self.lock = true
+ utils.nextTick(function () {
+ self.lock = false
+ })
+ }
+ owner.vm.$set(parentKey, val)
+ })
}
},
- update: function (value, init) {
- var vm = this.subVM,
- key = this.arg || '$data'
- if (!vm) {
- this.build(value)
- } else if (!this.lock && vm[key] !== value) {
- vm[key] = value
- }
- if (init) {
- // watch after first set
- this.watch()
- // The v-with directive can have multiple expressions,
- // and we want to make sure when the ready hook is called
- // on the subVM, all these clauses have been properly set up.
- // So this is a hack that sniffs whether we have reached
- // the last expression. We hold off the subVM's ready hook
- // until we are actually ready.
- if (this.last) {
- this.subVM.$compiler.execHook('ready')
+ update: function (value) {
+ // sync from parent
+ if (!this.alone && !this.lock) {
+ if (this.arg) {
+ this.vm.$set(this.arg, value)
+ } else {
+ this.vm.$data = value
}
}
- },
-
- build: function (value) {
- var data = value
- if (this.arg) {
- data = {}
- data[this.arg] = value
- }
- var Ctor = this.compiler.resolveComponent(this.el, data)
- this.subVM = new Ctor({
- el : this.el,
- data : data,
- parent : this.vm,
- compilerOptions: {
- // it is important to delay the ready hook
- // so that when it's called, all `v-with` wathcers
- // would have been set up.
- delayReady: !this.last
- }
- })
- // mark that this VM is created by v-with
- utils.defProtected(this.subVM, '$with', true)
- },
-
- /**
- * For inhertied keys, need to watch
- * and sync back to the parent
- */
- watch: function () {
- if (!this.arg) return
- var self = this,
- key = self.key,
- ownerVM = self.binding.compiler.vm
- this.subVM.$compiler.observer.on('change:' + this.arg, function (val) {
- if (!self.lock) {
- self.lock = true
- utils.nextTick(function () {
- self.lock = false
- })
- }
- ownerVM.$set(key, val)
- })
- },
-
- unbind: function () {
- // all watchers are turned off during destroy
- // so no need to worry about it
- if (this.subVM.$with) {
- this.subVM.$destroy()
- }
}
-}
\ No newline at end of file
+}
+
+// var utils = require('../utils')
+
+// module.exports = {
+
+// bind: function () {
+// if (this.el.vue_vm) {
+// this.subVM = this.el.vue_vm
+// var compiler = this.subVM.$compiler
+// if (this.arg && !compiler.bindings[this.arg]) {
+// compiler.createBinding(this.arg)
+// }
+// } else if (this.isEmpty) {
+// this.build()
+// }
+// },
+
+// update: function (value, init) {
+// var vm = this.subVM,
+// key = this.arg || '$data'
+// if (!vm) {
+// this.build(value)
+// } else if (!this.lock && vm[key] !== value) {
+// vm[key] = value
+// }
+// if (init) {
+// // watch after first set
+// this.watch()
+// // The v-with directive can have multiple expressions,
+// // and we want to make sure when the ready hook is called
+// // on the subVM, all these clauses have been properly set up.
+// // So this is a hack that sniffs whether we have reached
+// // the last expression. We hold off the subVM's ready hook
+// // until we are actually ready.
+// if (this.last) {
+// this.subVM.$compiler.execHook('ready')
+// }
+// }
+// },
+
+// build: function (value) {
+// var data = value
+// if (this.arg) {
+// data = {}
+// data[this.arg] = value
+// }
+// var Ctor = this.compiler.resolveComponent(this.el, data)
+// this.subVM = new Ctor({
+// el : this.el,
+// data : data,
+// parent : this.vm,
+// compilerOptions: {
+// // it is important to delay the ready hook
+// // so that when it's called, all `v-with` wathcers
+// // would have been set up.
+// delayReady: !this.last
+// }
+// })
+// // mark that this VM is created by v-with
+// utils.defProtected(this.subVM, '$with', true)
+// },
+
+// /**
+// * For inhertied keys, need to watch
+// * and sync back to the parent
+// */
+// watch: function () {
+// if (!this.arg) return
+// var self = this,
+// key = self.key,
+// ownerVM = self.binding.compiler.vm
+// this.subVM.$compiler.observer.on('change:' + this.arg, function (val) {
+// if (!self.lock) {
+// self.lock = true
+// utils.nextTick(function () {
+// self.lock = false
+// })
+// }
+// ownerVM.$set(key, val)
+// })
+// },
+
+// unbind: function () {
+// // all watchers are turned off during destroy
+// // so no need to worry about it
+// if (this.subVM.$with) {
+// this.subVM.$destroy()
+// }
+// }
+
+// }
\ No newline at end of file
diff --git a/src/utils.js b/src/utils.js
index 4d9441389..5ec21b1f0 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -63,10 +63,10 @@ var utils = module.exports = {
/**
* get an attribute and remove it.
*/
- attr: function (el, type, preserve) {
+ attr: function (el, type) {
var attr = config.prefix + '-' + type,
val = el.getAttribute(attr)
- if (!preserve && val !== null) {
+ if (val !== null) {
el.removeAttribute(attr)
}
return val
diff --git a/test/functional/fixtures/component.html b/test/functional/fixtures/component.html
index 27b52dddd..413eb51e0 100644
--- a/test/functional/fixtures/component.html
+++ b/test/functional/fixtures/component.html
@@ -5,8 +5,8 @@