diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 3ae0e75b27a..81c4777c59f 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -21,7 +21,6 @@ export default function model ( const modifiers = dir.modifiers const tag = el.tag const type = el.attrsMap.type - const attrsMap = el.attrsMap if (process.env.NODE_ENV !== 'production') { // inputs with type="file" are read only and setting the input's @@ -32,20 +31,6 @@ export default function model ( `File inputs are read only. Use a v-on:change listener instead.` ) } - - // warn if v-bind:value conflicts with v-model - if ( - (attrsMap['v-bind:value'] || attrsMap[':value']) && - type !== 'checkbox' && - type !== 'radio' && - tag !== 'select' - ) { - const vBindValue = attrsMap['v-bind:value'] ? 'v-bind:value' : ':value' - warn( - `${vBindValue} conflicts with v-model on the same element ` + - 'because the latter already expands to a value binding internally' - ) - } } if (el.component) { @@ -143,6 +128,19 @@ function genDefaultModel ( modifiers: ?ASTModifiers ): ?boolean { const type = el.attrsMap.type + + // warn if v-bind:value conflicts with v-model + if (process.env.NODE_ENV !== 'production') { + const value = el.attrsMap['v-bind:value'] || el.attrsMap[':value'] + if (value) { + const binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value' + warn( + `${binding}="${value}" conflicts with v-model on the same element ` + + 'because the latter already expands to a value binding internally' + ) + } + } + const { lazy, number, trim } = modifiers || {} const needCompositionGuard = !lazy && type !== 'range' const event = lazy diff --git a/test/unit/features/directives/model-text.spec.js b/test/unit/features/directives/model-text.spec.js index 2c186cc16f4..9cc76b852a9 100644 --- a/test/unit/features/directives/model-text.spec.js +++ b/test/unit/features/directives/model-text.spec.js @@ -255,12 +255,9 @@ describe('Directive v-model text', () => { data: { test: 'foo' }, - template: '' + template: '' }).$mount() - expect( - 'v-bind:value conflicts with v-model on the same element because the latter already ' + - 'expands to a value binding internally' - ).toHaveBeenWarned() + expect('v-bind:value="test" conflicts with v-model').toHaveBeenWarned() }) it('warn if v-model and :value conflict', () => { @@ -268,12 +265,30 @@ describe('Directive v-model text', () => { data: { test: 'foo' }, - template: '' + template: '' }).$mount() - expect( - ':value conflicts with v-model on the same element because the latter already ' + - 'expands to a value binding internally' - ).toHaveBeenWarned() + expect(':value="test" conflicts with v-model').toHaveBeenWarned() + }) + + it('should not warn on radio, checkbox, or custom component', () => { + new Vue({ + data: { test: '' }, + components: { + foo: { + props: ['model', 'value'], + model: { prop: 'model', event: 'change' }, + template: `
` + } + }, + template: ` +
+ + + +
+ ` + }).$mount() + expect('conflicts with v-model').not.toHaveBeenWarned() }) if (!isAndroid) {