From 46d153e95c7e6d18860a7b093eae73ef06c5f96d Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Sat, 27 Feb 2016 17:10:55 +0900 Subject: [PATCH] feat(trigger): support validation trigger Related #58 #150 --- src/directives/validate.js | 29 ++++- src/validations/base.js | 46 +++++++- src/validations/checkbox.js | 10 +- src/validations/radio.js | 7 +- src/validations/select.js | 7 +- src/validator.js | 40 ++++--- test/specs/directives/validate.js | 181 ++++++++++++++++++++++++++++++ 7 files changed, 295 insertions(+), 25 deletions(-) diff --git a/src/directives/validate.js b/src/directives/validate.js index 9f8166b..8ce2b90 100644 --- a/src/directives/validate.js +++ b/src/directives/validate.js @@ -16,7 +16,19 @@ export default function (Vue) { Vue.directive('validate', { priority: vIf.priority + 1, - params: ['group', 'field'], + params: ['group', 'field', 'detect-blur', 'detect-change'], + + paramWatchers: { + detectBlur (val, old) { + this.validation.detectBlur = this.isDetectBlur(val) + this.validator.validate() + }, + + detectChange (val, old) { + this.validation.detectChange = this.isDetectChange(val) + this.validator.validate() + } + }, bind () { if (this.el.__vue__) { @@ -54,7 +66,7 @@ export default function (Vue) { this.handleArray(value) } - this.validator.validate(this.validation) + this.validator.validate() }, unbind () { @@ -72,7 +84,9 @@ export default function (Vue) { this.field = _.camelize(this.arg ? this.arg : params.field) this.validation = validator.manageValidation( - this.field, model, this.vm, this.frag.node, this._scope + this.field, model, this.vm, this.frag.node, this._scope, + this.isDetectBlur(this.params.detectBlur), + this.isDetectChange(this.params.detectChange) ) if (params.group) { @@ -91,7 +105,6 @@ export default function (Vue) { || el.tagName === 'SELECT') && !model) { this.onChange = _.bind(validation.listener, validation) _.on(el, 'change', this.onChange) - } else if (el.type === 'checkbox') { if (!model) { this.onChange = _.bind(validation.listener, validation) @@ -185,6 +198,14 @@ export default function (Vue) { this.validation.setValidation(key, val) } }, this) + }, + + isDetectBlur (detectBlur) { + return detectBlur === undefined || detectBlur === 'on' || detectBlur === true + }, + + isDetectChange (detectChange) { + return detectChange === undefined || detectChange === 'on' || detectChange === true } }) } diff --git a/src/validations/base.js b/src/validations/base.js index 9905ab6..2242594 100644 --- a/src/validations/base.js +++ b/src/validations/base.js @@ -7,7 +7,7 @@ import util, { empty, each, trigger } from '../util' export default class BaseValidation { - constructor (field, model, vm, el, scope, validator) { + constructor (field, model, vm, el, scope, validator, detectBlur, detectChange) { this.field = field this.touched = false this.dirty = false @@ -21,6 +21,24 @@ export default class BaseValidation { this._forScope = scope this._init = this._getValue(el) this._validators = {} + this._detectBlur = detectBlur + this._detectChange = detectChange + } + + get detectChange () { + return this._detectChange + } + + set detectChange (val) { + this._detectChange = val + } + + get detectBlur () { + return this._detectBlur + } + + set detectBlur (val) { + this._detectBlur = val } manageElement (el) { @@ -30,6 +48,9 @@ export default class BaseValidation { el.value = scope.$get(model) || '' this._unwatch = scope.$watch(model, (val, old) => { if (val !== old) { + if (this.guardValidate(el, 'input')) { + return + } this.handleValidate(el) } }, { deep: true }) @@ -88,6 +109,9 @@ export default class BaseValidation { return } + if (this.guardValidate(e.target, e.type)) { + return + } this.handleValidate(e.target, e.type) } @@ -177,6 +201,26 @@ export default class BaseValidation { this._init = this._getValue(this._el) } + guardValidate (el, type) { + if (type && type === 'blur' && !this.detectBlur) { + return true + } + + if (type && type === 'input' && !this.detectChange) { + return true + } + + if (type && type === 'change' && !this.detectChange) { + return true + } + + if (type && type === 'click' && !this.detectChange) { + return true + } + + return false + } + _getValue (el) { return el.value } diff --git a/src/validations/checkbox.js b/src/validations/checkbox.js index 1fa7e61..f1e99e2 100644 --- a/src/validations/checkbox.js +++ b/src/validations/checkbox.js @@ -8,8 +8,8 @@ import BaseValidation from './base' export default class CheckboxValidation extends BaseValidation { - constructor (field, model, vm, el, scope, validator) { - super(field, model, vm, el, scope, validator) + constructor (field, model, vm, el, scope, validator, detectBlur, detectChange) { + super(field, model, vm, el, scope, validator, detectBlur, detectChange) this._inits = [] } @@ -24,6 +24,9 @@ export default class CheckboxValidation extends BaseValidation { this._setChecked(value, item.el) item.unwatch = scope.$watch(model, (val, old) => { if (val !== old) { + if (this.guardValidate(item.el, 'change')) { + return + } this.handleValidate(item.el) } }) @@ -34,6 +37,9 @@ export default class CheckboxValidation extends BaseValidation { item.value = el.value item.unwatch = scope.$watch(model, (val, old) => { if (val !== old) { + if (this.guardValidate(el, 'change')) { + return + } this.handleValidate(el) } }) diff --git a/src/validations/radio.js b/src/validations/radio.js index 09edd16..1fb3dc1 100644 --- a/src/validations/radio.js +++ b/src/validations/radio.js @@ -8,8 +8,8 @@ import BaseValidation from './base' export default class RadioValidation extends BaseValidation { - constructor (field, model, vm, el, scope, validator) { - super(field, model, vm, el, scope, validator) + constructor (field, model, vm, el, scope, validator, detectBlur, detectChange) { + super(field, model, vm, el, scope, validator, detectBlur, detectChange) this._inits = [] } @@ -23,6 +23,9 @@ export default class RadioValidation extends BaseValidation { this._setChecked(value, el, item) item.unwatch = scope.$watch(model, (val, old) => { if (val !== old) { + if (this.guardValidate(item.el, 'change')) { + return + } this.handleValidate(el) } }) diff --git a/src/validations/select.js b/src/validations/select.js index e86fa89..1279567 100644 --- a/src/validations/select.js +++ b/src/validations/select.js @@ -7,8 +7,8 @@ import BaseValidation from './base' export default class SelectValidation extends BaseValidation { - constructor (field, model, vm, el, scope, validator) { - super(field, model, vm, el, scope, validator) + constructor (field, model, vm, el, scope, validator, detectBlur, detectChange) { + super(field, model, vm, el, scope, validator, detectBlur, detectChange) this._multiple = this._el.hasAttribute('multiple') } @@ -24,6 +24,9 @@ export default class SelectValidation extends BaseValidation { let values1 = !Array.isArray(val) ? [val] : val let values2 = !Array.isArray(old) ? [old] : old if (values1.slice().sort().toString() !== values2.slice().sort().toString()) { + if (this.guardValidate(el, 'change')) { + return + } this.handleValidate(el) } }) diff --git a/src/validator.js b/src/validator.js index fb7267c..f5ba96b 100644 --- a/src/validator.js +++ b/src/validator.js @@ -169,17 +169,25 @@ export default class Validator { return ret } - manageValidation (field, model, vm, el, scope) { + manageValidation (field, model, vm, el, scope, detectBlur, detectChange) { let validation = null if (el.tagName === 'SELECT') { - validation = this._manageSelectValidation(field, model, vm, el, scope) + validation = this._manageSelectValidation( + field, model, vm, el, scope, detectBlur, detectChange + ) } else if (el.type === 'checkbox') { - validation = this._manageCheckboxValidation(field, model, vm, el, scope) + validation = this._manageCheckboxValidation( + field, model, vm, el, scope, detectBlur, detectChange + ) } else if (el.type === 'radio') { - validation = this._manageRadioValidation(field, model, vm, el, scope) + validation = this._manageRadioValidation( + field, model, vm, el, scope, detectBlur, detectChange + ) } else { - validation = this._manageBaseValidation(field, model, vm, el, scope) + validation = this._manageBaseValidation( + field, model, vm, el, scope, detectBlur, detectChange + ) } return validation @@ -197,8 +205,10 @@ export default class Validator { } } - _manageBaseValidation (field, model, vm, el, scope) { - let validation = this._validations[field] = new BaseValidation(field, model, vm, el, scope, this) + _manageBaseValidation (field, model, vm, el, scope, detectBlur, detectChange) { + let validation = this._validations[field] = new BaseValidation( + field, model, vm, el, scope, this, detectBlur, detectChange + ) validation.manageElement(el) return validation } @@ -213,10 +223,10 @@ export default class Validator { } } - _manageCheckboxValidation (field, model, vm, el, scope) { + _manageCheckboxValidation (field, model, vm, el, scope, detectBlur, detectChange) { let validationSet = this._checkboxValidations[field] if (!validationSet) { - let validation = new CheckboxValidation(field, model, vm, el, scope, this) + let validation = new CheckboxValidation(field, model, vm, el, scope, this, detectBlur, detectChange) validationSet = { validation: validation, elements: 0 } this._checkboxValidations[field] = validationSet } @@ -239,10 +249,10 @@ export default class Validator { } } - _manageRadioValidation (field, model, vm, el, scope) { + _manageRadioValidation (field, model, vm, el, scope, detectBlur, detectChange) { let validationSet = this._radioValidations[field] if (!validationSet) { - let validation = new RadioValidation(field, model, vm, el, scope, this) + let validation = new RadioValidation(field, model, vm, el, scope, this, detectBlur, detectChange) validationSet = { validation: validation, elements: 0 } this._radioValidations[field] = validationSet } @@ -265,8 +275,10 @@ export default class Validator { } } - _manageSelectValidation (field, model, vm, el, scope) { - let validation = this._validations[field] = new SelectValidation(field, model, vm, el, scope, this) + _manageSelectValidation (field, model, vm, el, scope, detectBlur, detectChange) { + let validation = this._validations[field] = new SelectValidation( + field, model, vm, el, scope, this, detectBlur, detectChange + ) validation.manageElement(el) return validation } @@ -305,7 +317,7 @@ export default class Validator { } } - validate (validation) { + validate () { each(this._validations, (validation, key) => { let res = validation.validate() util.Vue.set(this._scope, key, res) diff --git a/test/specs/directives/validate.js b/test/specs/directives/validate.js index cda65fb..4fc4f0b 100644 --- a/test/specs/directives/validate.js +++ b/test/specs/directives/validate.js @@ -810,4 +810,185 @@ describe('validate directive', () => { }) }) }) + + + describe('params', () => { + describe('detect-blur', () => { + beforeEach((done) => { + el.innerHTML = '' + + '
' + + '' + + '
' + + '
' + vm = new Vue({ + el: el, + data: { + msg: 'hello', blur: 'off' + } + }) + vm.$nextTick(done) + }) + + it('should be validated', (done) => { + let field = el.getElementsByTagName('input')[0] + + // default + assert(vm.$validator1.field1.required === true) + assert(vm.$validator1.field1.minlength === true) + assert(vm.$validator1.field1.valid === false) + assert(vm.$validator1.field1.touched === false) + assert(vm.$validator1.field1.modified === false) + assert(vm.$validator1.field1.dirty === false) + + // occured blur + field.value = 'helloworld' + trigger(field, 'blur') + vm.$nextTick(() => { + assert(vm.$validator1.field1.required === true) + assert(vm.$validator1.field1.minlength === true) + assert(vm.$validator1.field1.valid === false) + assert(vm.$validator1.field1.touched === false) + assert(vm.$validator1.field1.modified === false) + assert(vm.$validator1.field1.dirty === false) + + vm.$set('blur', 'on') + vm.$nextTick(() => { + field.value = 'helloworld!!' + trigger(field, 'blur') + vm.$nextTick(() => { + assert(vm.$validator1.field1.required === false) + assert(vm.$validator1.field1.minlength === false) + assert(vm.$validator1.field1.valid === true) + assert(vm.$validator1.field1.touched === true) + assert(vm.$validator1.field1.modified === true) + assert(vm.$validator1.field1.dirty === true) + + done() + }) + }) + }) + }) + }) + + + describe('detect-change', () => { + describe('normal', () => { + beforeEach((done) => { + el.innerHTML = '' + + '
' + + '' + + '
' + + '
' + vm = new Vue({ + el: el, + data: { change: 'off' } + }) + vm.$nextTick(done) + }) + + it('should be valided', (done) => { + // default + assert(vm.$validator1.lang.required === true) + assert(vm.$validator1.lang.valid === false) + assert(vm.$validator1.lang.touched === false) + assert(vm.$validator1.lang.modified === false) + assert(vm.$validator1.lang.dirty === false) + + let select = el.getElementsByTagName('select')[0] + + // select a language + let option1 = el.getElementsByTagName('option')[1] + option1.selected = true + trigger(select, 'change') + vm.$nextTick(() => { + assert(vm.$validator1.lang.required === true) + assert(vm.$validator1.lang.valid === false) + assert(vm.$validator1.lang.touched === false) + assert(vm.$validator1.lang.modified === false) + assert(vm.$validator1.lang.dirty === false) + + vm.change = 'on' + vm.$nextTick(() => { + let option2 = el.getElementsByTagName('option')[2] + option2.selected = true + trigger(select, 'change') + vm.$nextTick(() => { + assert(vm.$validator1.lang.required === false) + assert(vm.$validator1.lang.valid === true) + assert(vm.$validator1.lang.touched === false) + assert(vm.$validator1.lang.dirty === true) + assert(vm.$validator1.lang.modified === true) + done() + }) + }) + }) + }) + }) + + describe('v-model', () => { + beforeEach((done) => { + el.innerHTML = + '' + + '
' + + '' + + '' + + '' + + '
' + + '
' + vm = new Vue({ + el: el, + data: { checkedNames: [], change: 'off' } + }) + vm.$nextTick(done) + }) + + it('should be validated', (done) => { + let foo = el.getElementsByTagName('input')[0] + let bar = el.getElementsByTagName('input')[1] + + // default + assert(vm.$validator1.field1.required === true) + assert(vm.$validator1.field1.minlength === true) + assert(vm.$validator1.field1.valid === false) + assert(vm.$validator1.field1.touched === false) + assert(vm.$validator1.field1.dirty === false) + assert(vm.$validator1.field1.modified === false) + + // checked foo + foo.checked = true + trigger(foo, 'click') + vm.$nextTick(() => { + assert(vm.$validator1.field1.required === true) + assert(vm.$validator1.field1.minlength === true) + assert(vm.$validator1.field1.valid === false) + assert(vm.$validator1.field1.touched === false) + assert(vm.$validator1.field1.dirty === false) + assert(vm.$validator1.field1.modified === false) + + vm.$set('change', 'on') + vm.$nextTick(() => { + // checked bar + bar.checked = true + trigger(bar, 'click') + vm.$nextTick(() => { + assert(vm.$validator1.field1.required === false) + assert(vm.$validator1.field1.minlength === false) + assert(vm.$validator1.field1.valid === true) + assert(vm.$validator1.field1.touched === false) + assert(vm.$validator1.field1.dirty === true) + assert(vm.$validator1.field1.modified === true) + + done() + }) + }) + }) + }) + }) + }) + }) })