diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 5f8639cd6b5..3dd2c45ea05 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -2,6 +2,7 @@ import { isIE } from 'core/util/env' import { addHandler, addProp, getBindingAttr } from 'compiler/helpers' +import parseModel from 'web/util/model' let warn @@ -79,7 +80,7 @@ function genRadioModel (el: ASTElement, value: string) { } const valueBinding = getBindingAttr(el, 'value') || 'null' addProp(el, 'checked', `_q(${value},${valueBinding})`) - addHandler(el, 'change', `${value}=${valueBinding}`, null, true) + addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true) } function genDefaultModel ( @@ -114,8 +115,8 @@ function genDefaultModel ( ? `$event.target.value${trim ? '.trim()' : ''}` : `$event` let code = number || type === 'number' - ? `${value}=_n(${valueExpression})` - : `${value}=${valueExpression}` + ? genAssignmentCode(value, `_n(${valueExpression})`) + : genAssignmentCode(value, valueExpression) if (isNative && needCompositionGuard) { code = `if($event.target.composing)return;${code}` } @@ -136,10 +137,13 @@ function genSelect (el: ASTElement, value: string) { if (process.env.NODE_ENV !== 'production') { el.children.some(checkOptionWarning) } - const code = `${value}=Array.prototype.filter` + + + const assignment = `Array.prototype.filter` + `.call($event.target.options,function(o){return o.selected})` + `.map(function(o){return "_value" in o ? o._value : o.value})` + (el.attrsMap.multiple == null ? '[0]' : '') + + const code = genAssignmentCode(value, assignment) addHandler(el, 'change', code, null, true) } @@ -156,3 +160,15 @@ function checkOptionWarning (option: any): boolean { } return false } + +function genAssignmentCode (value: string, assignment: string): string { + const modelRs = parseModel(value) + if (modelRs.idx === null) { + return `${value}=${assignment}` + } else { + return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` + + `if (!Array.isArray($$exp)){` + + `${value}=${assignment}}` + + `else{$$exp.splice($$idx, 1, ${assignment})}` + } +} diff --git a/src/platforms/web/util/model.js b/src/platforms/web/util/model.js new file mode 100644 index 00000000000..fdf6a5ac174 --- /dev/null +++ b/src/platforms/web/util/model.js @@ -0,0 +1,84 @@ +/* @flow */ + +let len, str, chr, index, expressionPos, expressionEndPos + +/** + * parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val) + * + * for loop possible cases: + * + * - test + * - test[idx] + * - test[test1[idx]] + * - test["a"][idx] + * - xxx.test[a[a].test1[idx]] + * - test.xxx.a["asa"][test1[idx]] + * + */ + +export default function parseModel (val: string): Object { + str = val + len = str.length + index = expressionPos = expressionEndPos = 0 + + if (val.indexOf('[') < 0) { + return { + exp: val, + idx: null + } + } + + while (!eof()) { + chr = next() + if (isStringStart(chr)) { + parseString(chr) + } else if (chr === 0x5B) { + parseBracket(chr) + } + } + + return { + exp: val.substring(0, expressionPos), + idx: val.substring(expressionPos + 1, expressionEndPos) + } +} + +function next (): number { + return str.charCodeAt(++index) +} + +function eof (): boolean { + return index >= len +} + +function isStringStart (chr: number): boolean { + return chr === 0x22 || chr === 0x27 +} + +function parseBracket (chr: number): void { + let inBracket = 1 + expressionPos = index + while (!eof()) { + chr = next() + if (isStringStart(chr)) { + parseString(chr) + continue + } + if (chr === 0x5B) inBracket++ + if (chr === 0x5D) inBracket-- + if (inBracket === 0) { + expressionEndPos = index + break + } + } +} + +function parseString (chr: number): void { + const stringQuote = chr + while (!eof()) { + chr = next() + if (chr === stringQuote) { + break + } + } +} diff --git a/test/unit/features/directives/model-component.spec.js b/test/unit/features/directives/model-component.spec.js index 1739e7803b2..622deed74f4 100644 --- a/test/unit/features/directives/model-component.spec.js +++ b/test/unit/features/directives/model-component.spec.js @@ -2,14 +2,18 @@ import Vue from 'vue' describe('Directive v-model component', () => { it('should work', done => { + const spy = jasmine.createSpy() const vm = new Vue({ data: { - msg: 'hello' + msg: ['hello'] + }, + watch: { + msg: spy }, template: `
{{ msg }}
-