diff --git a/src/directives/public/for.js b/src/directives/public/for.js index d8959c7ff52..8e3167b5d0c 100644 --- a/src/directives/public/for.js +++ b/src/directives/public/for.js @@ -473,6 +473,7 @@ module.exports = { */ _postProcess: function (value) { + this.processedValue = value if (_.isArray(value)) { return value } else if (_.isPlainObject(value)) { @@ -513,6 +514,23 @@ module.exports = { frag.destroy() } } + }, + + _syncChanges: function (scope, value) { + // two-way sync for v-for alias + var subjectValue = this.filters ? this.processedValue + : this.rawValue + if (scope.$key) { // original is an object + subjectValue[scope.$key] = value + } else { + subjectValue.$set(scope.$index, value) + } + if (this.filters) { + var vm = this._scope || this.vm + var reverseValue = vm._applyFilters(subjectValue, + subjectValue, this.filters, true) + vm.$set(this.expression, reverseValue) + } } } diff --git a/src/instance/misc.js b/src/instance/misc.js index 7eee2e717f4..9c6381fdd2e 100644 --- a/src/instance/misc.js +++ b/src/instance/misc.js @@ -39,6 +39,14 @@ exports._applyFilters = function (value, oldValue, filters, write) { return value } +exports._syncChanges = function (value, expression) { + // two-way sync for v-for alias + var forContext = this.$forContext + if (forContext && forContext.alias === expression) { + forContext._syncChanges(this, value) + } +} + /** * Resolve a component, depending on whether the component * is defined normally or using an async factory function. diff --git a/src/watcher.js b/src/watcher.js index 2f6806005fb..9e0a344ecc9 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -143,25 +143,7 @@ Watcher.prototype.set = function (value) { ) } } - // two-way sync for v-for alias - var forContext = scope.$forContext - if (forContext && forContext.alias === this.expression) { - if (forContext.filters) { - process.env.NODE_ENV !== 'production' && _.warn( - 'It seems you are using two-way binding on ' + - 'a v-for alias, and the v-for has filters. ' + - 'This will not work properly. Either remove the ' + - 'filters or use an array of objects and bind to ' + - 'object properties instead.' - ) - return - } - if (scope.$key) { // original is an object - forContext.rawValue[scope.$key] = value - } else { - forContext.rawValue.$set(scope.$index, value) - } - } + scope._syncChanges(value, this.expression) } /** diff --git a/test/unit/specs/directives/public/for/for_spec.js b/test/unit/specs/directives/public/for/for_spec.js index b51489da1fa..4ea94d37f66 100644 --- a/test/unit/specs/directives/public/for/for_spec.js +++ b/test/unit/specs/directives/public/for/for_spec.js @@ -678,19 +678,32 @@ if (_.inBrowser) { } }) - it('warn v-model on alias with filters', function () { + it('v-model on alias with filters', function (done) { var vm = new Vue({ el: el, template: - '
' + - '' + + '
' + + '{{item}}' + '
', data: { - items: ['a', 'b'] + items: 'a, b' + }, + filters: { + filter: { + read: function (value) { return value.split(', ') }, + write: function (value) { + return value.join(', ') + } + } } }) - trigger(vm.$el.querySelector('input'), 'input') - expect(hasWarned(_, 'It seems you are using two-way binding')).toBe(true) + var input = vm.$el.querySelector('input') + input.value = 'c' + trigger(input, 'input') + _.nextTick(function () { + expect(vm.items).toBe('c, b') + done() + }) }) it('nested track by', function (done) {