From 0cd9353fd073fc1e5a3a4eda609147ed9e92d28d Mon Sep 17 00:00:00 2001 From: defcc Date: Mon, 24 Oct 2016 08:46:52 +0800 Subject: [PATCH 1/5] support number modifier in select, radio, checkbox --- .../web/compiler/directives/model.js | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 3dd2c45ea05..9ca9f6a15e9 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -26,11 +26,11 @@ export default function model ( } } if (tag === 'select') { - genSelect(el, value) + genSelect(el, value, modifiers) } else if (tag === 'input' && type === 'checkbox') { - genCheckboxModel(el, value) + genCheckboxModel(el, value, modifiers) } else if (tag === 'input' && type === 'radio') { - genRadioModel(el, value) + genRadioModel(el, value, modifiers) } else { genDefaultModel(el, value, modifiers) } @@ -38,7 +38,11 @@ export default function model ( return true } -function genCheckboxModel (el: ASTElement, value: string) { +function genCheckboxModel ( + el: ASTElement, + value: string, + modifiers: ?Object +) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { warn( @@ -47,6 +51,7 @@ function genCheckboxModel (el: ASTElement, value: string) { 'Declare initial values in the component\'s data option instead.' ) } + const number = modifiers && modifiers.number const valueBinding = getBindingAttr(el, 'value') || 'null' const trueValueBinding = getBindingAttr(el, 'true-value') || 'true' const falseValueBinding = getBindingAttr(el, 'false-value') || 'false' @@ -60,7 +65,7 @@ function genCheckboxModel (el: ASTElement, value: string) { '$$el=$event.target,' + `$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` + 'if(Array.isArray($$a)){' + - `var $$v=${valueBinding},` + + `var $$v=${number ? '_n(' + valueBinding + ')' : valueBinding},` + '$$i=_i($$a,$$v);' + `if($$c){$$i<0&&(${value}=$$a.concat($$v))}` + `else{$$i>-1&&(${value}=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}` + @@ -69,7 +74,11 @@ function genCheckboxModel (el: ASTElement, value: string) { ) } -function genRadioModel (el: ASTElement, value: string) { +function genRadioModel ( + el: ASTElement, + value: string, + modifiers: ?Object +) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { warn( @@ -78,7 +87,11 @@ function genRadioModel (el: ASTElement, value: string) { 'Declare initial values in the component\'s data option instead.' ) } - const valueBinding = getBindingAttr(el, 'value') || 'null' + const number = modifiers && modifiers.number + let valueBinding = getBindingAttr(el, 'value') || 'null' + if (number) { + valueBinding = `_n(${valueBinding})` + } addProp(el, 'checked', `_q(${value},${valueBinding})`) addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true) } @@ -133,14 +146,20 @@ function genDefaultModel ( addHandler(el, event, code, null, true) } -function genSelect (el: ASTElement, value: string) { +function genSelect ( + el: ASTElement, + value: string, + modifiers: ?Object +) { if (process.env.NODE_ENV !== 'production') { el.children.some(checkOptionWarning) } + const number = modifiers && modifiers.number 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})` + + `.map(function(o){var val = "_value" in o ? o._value : o.value;` + + `return ${number ? '_n(val)' : 'val'}})` + (el.attrsMap.multiple == null ? '[0]' : '') const code = genAssignmentCode(value, assignment) From 90d258b6a9ec9409c13a88653893d2b9ab636b9f Mon Sep 17 00:00:00 2001 From: defcc Date: Mon, 24 Oct 2016 22:12:20 +0800 Subject: [PATCH 2/5] add test case --- .../directives/model-checkbox.spec.js | 26 +++++++++++++++++++ .../features/directives/model-radio.spec.js | 21 +++++++++++++++ .../features/directives/model-select.spec.js | 18 +++++++++++++ 3 files changed, 65 insertions(+) diff --git a/test/unit/features/directives/model-checkbox.spec.js b/test/unit/features/directives/model-checkbox.spec.js index 9fc92d32bab..b4f300c70c2 100644 --- a/test/unit/features/directives/model-checkbox.spec.js +++ b/test/unit/features/directives/model-checkbox.spec.js @@ -133,6 +133,32 @@ describe('Directive v-model checkbox', () => { }).then(done) }) + it('.number modifier', () => { + const vm = new Vue({ + data: { + test: [], + check: true + }, + template: ` +
+ + + +
+ ` + }).$mount() + document.body.appendChild(vm.$el) + var checkboxInputs = vm.$el.getElementsByTagName('input') + expect(checkboxInputs[0].checked).toBe(false) + expect(checkboxInputs[1].checked).toBe(false) + expect(checkboxInputs[2].checked).toBe(true) + checkboxInputs[0].click() + checkboxInputs[1].click() + checkboxInputs[2].click() + expect(vm.test).toEqual([1, '2']) + expect(vm.check).toEqual(false) + }) + it('warn inline checked', () => { const vm = new Vue({ template: ``, diff --git a/test/unit/features/directives/model-radio.spec.js b/test/unit/features/directives/model-radio.spec.js index b52456d4e6b..26e6ffed119 100644 --- a/test/unit/features/directives/model-radio.spec.js +++ b/test/unit/features/directives/model-radio.spec.js @@ -125,6 +125,27 @@ describe('Directive v-model radio', () => { }).then(done) }) + it('.number modifier', () => { + const vm = new Vue({ + data: { + test: 1 + }, + template: ` +
+ + +
+ ` + }).$mount() + document.body.appendChild(vm.$el) + expect(vm.$el.children[0].checked).toBe(true) + expect(vm.$el.children[1].checked).toBe(false) + vm.$el.children[1].click() + expect(vm.$el.children[0].checked).toBe(false) + expect(vm.$el.children[1].checked).toBe(true) + expect(vm.test).toBe(2) + }) + it('warn inline checked', () => { const vm = new Vue({ template: ``, diff --git a/test/unit/features/directives/model-select.spec.js b/test/unit/features/directives/model-select.spec.js index 7de1f0d3fd2..49aa9018d22 100644 --- a/test/unit/features/directives/model-select.spec.js +++ b/test/unit/features/directives/model-select.spec.js @@ -301,6 +301,24 @@ describe('Directive v-model select', () => { }).then(done) }) + it('.number modifier', () => { + const vm = new Vue({ + data: { + test: 2 + }, + template: + '' + }).$mount() + document.body.appendChild(vm.$el) + updateSelect(vm.$el, '1') + triggerEvent(vm.$el, 'change') + expect(vm.test).toBe(1) + }) + it('should warn inline selected', () => { const vm = new Vue({ data: { From f8f053d3f4c0203df35fe1dabbbe67e40b835908 Mon Sep 17 00:00:00 2001 From: defcc Date: Wed, 26 Oct 2016 13:40:01 +0800 Subject: [PATCH 3/5] add ASTModifier type to specify modifiers type --- flow/compiler.js | 6 ++++-- flow/vnode.js | 2 +- src/compiler/helpers.js | 4 ++-- src/platforms/web/compiler/directives/model.js | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index a1a77f68d60..e21e4f60a10 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -41,9 +41,11 @@ declare type ModuleOptions = { staticKeys?: Array; // AST properties to be considered static } +declare type ASTModifier = { [key: string]: boolean } + declare type ASTElementHandler = { value: string; - modifiers: ?{ [key: string]: true }; + modifiers: ?ASTModifier; } declare type ASTElementHandlers = { @@ -55,7 +57,7 @@ declare type ASTDirective = { rawName: string; value: string; arg: ?string; - modifiers: ?{ [key: string]: true }; + modifiers: ?ASTModifier; } declare type ASTNode = ASTElement | ASTText | ASTExpression diff --git a/flow/vnode.js b/flow/vnode.js index 39d90f78649..66b3ce47ebe 100644 --- a/flow/vnode.js +++ b/flow/vnode.js @@ -60,6 +60,6 @@ declare type VNodeDirective = { value?: any; oldValue?: any; arg?: string; - modifiers?: { [key: string]: boolean }; + modifiers?: ASTModifier; def?: Object; } diff --git a/src/compiler/helpers.js b/src/compiler/helpers.js index 652ea97481a..733ffb0aaa2 100644 --- a/src/compiler/helpers.js +++ b/src/compiler/helpers.js @@ -27,7 +27,7 @@ export function addDirective ( rawName: string, value: string, arg: ?string, - modifiers: ?{ [key: string]: true } + modifiers: ?ASTModifier ) { (el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers }) } @@ -36,7 +36,7 @@ export function addHandler ( el: ASTElement, name: string, value: string, - modifiers: ?{ [key: string]: true }, + modifiers: ?ASTModifier, important: ?boolean ) { // check capture modifier diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 9ca9f6a15e9..b3a094c32fb 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -41,7 +41,7 @@ export default function model ( function genCheckboxModel ( el: ASTElement, value: string, - modifiers: ?Object + modifiers: ?ASTModifier ) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { @@ -77,7 +77,7 @@ function genCheckboxModel ( function genRadioModel ( el: ASTElement, value: string, - modifiers: ?Object + modifiers: ?ASTModifier ) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { @@ -99,7 +99,7 @@ function genRadioModel ( function genDefaultModel ( el: ASTElement, value: string, - modifiers: ?Object + modifiers: ?ASTModifier ): ?boolean { if (process.env.NODE_ENV !== 'production') { if (el.tag === 'input' && el.attrsMap.value) { @@ -149,7 +149,7 @@ function genDefaultModel ( function genSelect ( el: ASTElement, value: string, - modifiers: ?Object + modifiers: ?ASTModifier ) { if (process.env.NODE_ENV !== 'production') { el.children.some(checkOptionWarning) From dafad747990a04fe547719527ac4e5ab64c22bdf Mon Sep 17 00:00:00 2001 From: defcc Date: Wed, 26 Oct 2016 14:28:11 +0800 Subject: [PATCH 4/5] fix typo --- flow/compiler.js | 6 +++--- flow/vnode.js | 2 +- src/compiler/helpers.js | 4 ++-- src/platforms/web/compiler/directives/model.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index e21e4f60a10..186f38576dc 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -41,11 +41,11 @@ declare type ModuleOptions = { staticKeys?: Array; // AST properties to be considered static } -declare type ASTModifier = { [key: string]: boolean } +declare type ASTModifiers = { [key: string]: boolean } declare type ASTElementHandler = { value: string; - modifiers: ?ASTModifier; + modifiers: ?ASTModifiers; } declare type ASTElementHandlers = { @@ -57,7 +57,7 @@ declare type ASTDirective = { rawName: string; value: string; arg: ?string; - modifiers: ?ASTModifier; + modifiers: ?ASTModifiers; } declare type ASTNode = ASTElement | ASTText | ASTExpression diff --git a/flow/vnode.js b/flow/vnode.js index 66b3ce47ebe..c58efa2f9be 100644 --- a/flow/vnode.js +++ b/flow/vnode.js @@ -60,6 +60,6 @@ declare type VNodeDirective = { value?: any; oldValue?: any; arg?: string; - modifiers?: ASTModifier; + modifiers?: ASTModifiers; def?: Object; } diff --git a/src/compiler/helpers.js b/src/compiler/helpers.js index 733ffb0aaa2..2772ebefe60 100644 --- a/src/compiler/helpers.js +++ b/src/compiler/helpers.js @@ -27,7 +27,7 @@ export function addDirective ( rawName: string, value: string, arg: ?string, - modifiers: ?ASTModifier + modifiers: ?ASTModifiers ) { (el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers }) } @@ -36,7 +36,7 @@ export function addHandler ( el: ASTElement, name: string, value: string, - modifiers: ?ASTModifier, + modifiers: ?ASTModifiers, important: ?boolean ) { // check capture modifier diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index b3a094c32fb..2cfe17b6ce1 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -41,7 +41,7 @@ export default function model ( function genCheckboxModel ( el: ASTElement, value: string, - modifiers: ?ASTModifier + modifiers: ?ASTModifiers ) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { @@ -77,7 +77,7 @@ function genCheckboxModel ( function genRadioModel ( el: ASTElement, value: string, - modifiers: ?ASTModifier + modifiers: ?ASTModifiers ) { if (process.env.NODE_ENV !== 'production' && el.attrsMap.checked != null) { @@ -99,7 +99,7 @@ function genRadioModel ( function genDefaultModel ( el: ASTElement, value: string, - modifiers: ?ASTModifier + modifiers: ?ASTModifiers ): ?boolean { if (process.env.NODE_ENV !== 'production') { if (el.tag === 'input' && el.attrsMap.value) { @@ -149,7 +149,7 @@ function genDefaultModel ( function genSelect ( el: ASTElement, value: string, - modifiers: ?ASTModifier + modifiers: ?ASTModifiers ) { if (process.env.NODE_ENV !== 'production') { el.children.some(checkOptionWarning) From cce5e716817c4def72646e3173d6da0d4f4779df Mon Sep 17 00:00:00 2001 From: defcc Date: Fri, 28 Oct 2016 21:58:22 +0800 Subject: [PATCH 5/5] keep code consistent --- src/platforms/web/compiler/directives/model.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 2cfe17b6ce1..2ae6b711452 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -89,9 +89,7 @@ function genRadioModel ( } const number = modifiers && modifiers.number let valueBinding = getBindingAttr(el, 'value') || 'null' - if (number) { - valueBinding = `_n(${valueBinding})` - } + valueBinding = number ? `_n(${valueBinding})` : valueBinding addProp(el, 'checked', `_q(${value},${valueBinding})`) addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true) } @@ -124,12 +122,13 @@ function genDefaultModel ( const needCompositionGuard = !lazy && type !== 'range' const isNative = el.tag === 'input' || el.tag === 'textarea' - const valueExpression = isNative + let valueExpression = isNative ? `$event.target.value${trim ? '.trim()' : ''}` : `$event` - let code = number || type === 'number' - ? genAssignmentCode(value, `_n(${valueExpression})`) - : genAssignmentCode(value, valueExpression) + valueExpression = number || type === 'number' + ? `_n(${valueExpression})` + : valueExpression + let code = genAssignmentCode(value, valueExpression) if (isNative && needCompositionGuard) { code = `if($event.target.composing)return;${code}` }