diff --git a/src/compiler.js b/src/compiler.js index f13f1268629..127a50c9812 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -124,9 +124,7 @@ function Compiler (vm, options) { } // extract dependencies for computed properties - if (compiler.computed.length) { - DepsParser.parse(compiler.computed) - } + compiler.parseDeps() // done! compiler.init = false @@ -600,6 +598,14 @@ CompilerProto.hasKey = function (key) { hasOwn.call(this.vm, baseKey) } +/** + * Collect dependencies for computed properties + */ +CompilerProto.parseDeps = function () { + if (!this.computed.length) return + DepsParser.parse(this.computed) +} + /** * Unbind and remove element */ diff --git a/src/deps-parser.js b/src/deps-parser.js index b505fe2a78f..1ddda62aecc 100644 --- a/src/deps-parser.js +++ b/src/deps-parser.js @@ -11,6 +11,7 @@ function catchDeps (binding) { if (binding.isFn) return utils.log('\n- ' + binding.key) var got = utils.hash() + binding.deps = [] catcher.on('get', function (dep) { var has = got[dep.key] if (has && has.compiler === dep.compiler) return @@ -36,7 +37,8 @@ module.exports = { parse: function (bindings) { utils.log('\nparsing dependencies...') Observer.shouldGet = true - bindings.forEach(catchDeps) + var i = bindings.length + while (i--) { catchDeps(bindings[i]) } Observer.shouldGet = false utils.log('\ndone.') } diff --git a/src/directives/repeat.js b/src/directives/repeat.js index 46b97280999..a4d8d548d05 100644 --- a/src/directives/repeat.js +++ b/src/directives/repeat.js @@ -110,38 +110,59 @@ module.exports = { if (method !== 'push' && method !== 'pop') { self.updateIndexes() } + if (method === 'push' || method === 'unshift' || method === 'splice') { + self.changed() + } } }, - update: function (collection) { + update: function (collection, init) { - this.unbind(true) + var self = this + self.unbind(true) // attach an object to container to hold handlers - this.container.vue_dHandlers = utils.hash() + self.container.vue_dHandlers = utils.hash() // if initiating with an empty collection, we need to // force a compile so that we get all the bindings for // dependency extraction. - if (!this.initiated && (!collection || !collection.length)) { - this.buildItem() - this.initiated = true + if (!self.initiated && (!collection || !collection.length)) { + self.buildItem() + self.initiated = true } - collection = this.collection = collection || [] - this.vms = [] + collection = self.collection = collection || [] + self.vms = [] // listen for collection mutation events // the collection has been augmented during Binding.set() if (!collection.__observer__) Observer.watchArray(collection, null, new Emitter()) - collection.__observer__.on('mutate', this.mutationListener) + collection.__observer__.on('mutate', self.mutationListener) // create child-vms and append to DOM if (collection.length) { for (var i = 0, l = collection.length; i < l; i++) { - this.buildItem(collection[i], i) + self.buildItem(collection[i], i) } + if (!init) self.changed() } }, + /** + * Notify parent compiler that new items + * have been added to the collection, it needs + * to re-calculate computed property dependencies. + * Batched to ensure it's called only once every event loop. + */ + changed: function () { + var self = this + if (self.queued) return + self.queued = true + setTimeout(function () { + self.compiler.parseDeps() + self.queued = false + }, 0) + }, + /** * Create a new child VM from a data object * passing along compiler options indicating this diff --git a/test/functional/fixtures/computed-repeat.html b/test/functional/fixtures/computed-repeat.html new file mode 100644 index 00000000000..e177052c72a --- /dev/null +++ b/test/functional/fixtures/computed-repeat.html @@ -0,0 +1,41 @@ + + + + Repeated form elements + + + + +
+

+ +

+ +

{{texts}}

+
+ + + \ No newline at end of file diff --git a/test/functional/specs/computed-repeat.js b/test/functional/specs/computed-repeat.js new file mode 100644 index 00000000000..d2420c78cb1 --- /dev/null +++ b/test/functional/specs/computed-repeat.js @@ -0,0 +1,29 @@ +casper.test.begin('Computed property depending on repeated items', 4, function (test) { + + casper + .start('./fixtures/computed-repeat.html') + .then(function () { + test.assertSelectorHasText('#texts', 'a,b') + }) + .thenClick('#add', function () { + test.assertSelectorHasText('#texts', 'a,b,c') + }) + .then(function () { + this.fill('#form', { + "text0": 'd', + "text1": 'e', + "text2": 'f', + }) + }) + .then(function () { + this.sendKeys('input[name="text2"]', 'fff') + }) + .then(function () { + test.assertField('text0', 'd') + test.assertSelectorHasText('#texts', 'd,e,ffff') + }) + .run(function () { + test.done() + }) + +}) \ No newline at end of file