From 53258eace6e447a5708766f7fdf3f4ce64115d93 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Tue, 29 Nov 2016 01:01:27 +0900 Subject: [PATCH] :zap: improvement(component): fix component validation [ci skip] --- src/components/validity/computed.js | 2 +- src/components/validity/lifecycles.js | 13 ++-- src/components/validity/methods-validate.js | 6 +- src/elements/component.js | 26 +++++--- .../components/validity-functional.test.js | 29 ++++++--- .../components/validity/progresses.test.js | 3 +- test/unit/components/validity/props.test.js | 2 +- test/unit/elements/component.test.js | 59 +++++++++++++------ 8 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/components/validity/computed.js b/src/components/validity/computed.js index fccdd46..de70f17 100644 --- a/src/components/validity/computed.js +++ b/src/components/validity/computed.js @@ -63,7 +63,7 @@ export default function (Vue: GlobalAPI): Object { const propRet: boolean | string | void = result[prop] ret[prop] = ret[prop] || {} if (typeof propRet === 'boolean') { - if (result) { + if (propRet) { ret[prop][validator] = false } else { _setError(ret, this.field, validator, undefined, prop) diff --git a/src/components/validity/lifecycles.js b/src/components/validity/lifecycles.js index 34b79a3..4de8a0e 100644 --- a/src/components/validity/lifecycles.js +++ b/src/components/validity/lifecycles.js @@ -16,9 +16,9 @@ export default function (Vue: GlobalAPI): Object { : null } - function mapValidatorProps (validators: any) { + function getValidatorProps (validators: any): Array { const normalized = typeof validators === 'string' ? [validators] : validators - const ret: Dictionary> = {} + const targets: Array = [] if (isPlainObject(normalized)) { Object.keys(normalized).forEach((validator: string) => { const props: ?Object = (normalized[validator] @@ -28,13 +28,14 @@ export default function (Vue: GlobalAPI): Object { : null if (props) { Object.keys(props).forEach((prop: string) => { - if (!ret[prop]) { ret[prop] = [] } - ret[prop].push(validator) + if (!~targets.indexOf(prop)) { + targets.push(prop) + } }) } }) } - return ret + return targets } function created (): void { @@ -44,7 +45,7 @@ export default function (Vue: GlobalAPI): Object { return Object.keys(results) }) - this._validatorPropMap = memoize(mapValidatorProps) + this._validatorProps = memoize(getValidatorProps) // for event control flags this._modified = false diff --git a/src/components/validity/methods-validate.js b/src/components/validity/methods-validate.js index 1b9da0c..5716b6d 100644 --- a/src/components/validity/methods-validate.js +++ b/src/components/validity/methods-validate.js @@ -149,10 +149,11 @@ export default function (Vue: GlobalAPI): Object { propsKeys.forEach((prop: string) => { if (this.progresses[validator][prop]) { return } this.progresses[validator][prop] = 'running' + const values = descriptor.value const propDescriptor = { fn: descriptor.fn, - value: descriptor.value, - filed: descriptor.filed + value: values[prop], + field: descriptor.field } if (descriptor.props[prop].rule) { propDescriptor.rule = descriptor.props[prop].rule @@ -164,6 +165,7 @@ export default function (Vue: GlobalAPI): Object { this._invokeValidator(propDescriptor, propDescriptor.value, (result: boolean, msg: ?string) => { this.progresses[validator][prop] = '' this.results[validator][prop] = msg || result + console.log('prop_invoi', result, msg, validator, prop, values, propDescriptor) const e: Object = { prop, result } if (msg) { e['msg'] = msg diff --git a/src/elements/component.js b/src/elements/component.js index 6c376f9..7a1f21a 100644 --- a/src/elements/component.js +++ b/src/elements/component.js @@ -13,10 +13,10 @@ export default function (Vue: GlobalAPI): any { initValue: any constructor (vm: ValidityComponent, vnode: any) { - console.log('ComponentElement#constructor') this._vm = vm this._vnode = vnode this.initValue = this.getValue() + this._watchers = [] this.attachValidity() } @@ -24,12 +24,21 @@ export default function (Vue: GlobalAPI): any { this._vm.$el.$validity = this._vm } + getValidatorProps (): Array { + const vm = this._vm + return vm._validatorProps(vm._uid.toString(), vm.validators) + } + getValue (): any { - return this._vnode.child.value + const value: Dictionary = {} + this.getValidatorProps().forEach((prop: string) => { + value[prop] = this._vnode.child[prop] + }) + return value } checkModified (): boolean { - return !looseEqual(this.initValue, this._vnode.child.value) + return !looseEqual(this.initValue, this.getValue()) } listenToucheableEvent (): void { @@ -41,15 +50,14 @@ export default function (Vue: GlobalAPI): any { } listenInputableEvent (): void { - this._unwatchInputable = this._vnode.child.$watch('value', this._vm.watchInputable) + this.getValidatorProps().forEach((prop: string) => { + this._watchers.push(this._vnode.child.$watch(prop, this._vm.watchInputable)) + }) } unlistenInputableEvent (): void { - if (this._unwatchInputable) { - this._unwatchInputable() - this._unwatchInputable = undefined - delete this._unwatchInputable - } + this._watchers.forEach(watcher => { watcher() }) + this._watchers = [] } fireInputableEvent (): void { diff --git a/test/unit/components/validity-functional.test.js b/test/unit/components/validity-functional.test.js index d773613..90acf76 100644 --- a/test/unit/components/validity-functional.test.js +++ b/test/unit/components/validity-functional.test.js @@ -379,7 +379,15 @@ describe('validity functional component', () => { h('validity', { props: { field: 'field1', - validators: ['required'] + validators: { + required: { + props: { + value: { + rule: true + } + } + } + } }, ref: 'validity' }, [ @@ -396,10 +404,11 @@ describe('validity functional component', () => { validity.validate() // validate !! }).thenWaitFor(1).then(() => { result = validity.result - assert(result.required === true) + assert(result.value.required === true) assert.deepEqual(result.errors, [{ field: 'field1', - validator: 'required' + validator: 'required', + prop: 'value' }]) assert(validity.valid === false) assert(validity.invalid === true) @@ -411,7 +420,7 @@ describe('validity functional component', () => { validity.validate() // validate !! }).thenWaitFor(1).then(() => { result = validity.result - assert(result.required === false) + assert(result.value.required === false) assert(result.errors === undefined) assert(validity.valid === true) assert(validity.invalid === false) @@ -625,7 +634,9 @@ describe('validity functional component', () => { h('validity', { props: { field: 'field2', - validators: ['required'] + validators: { + required: { props: { value: { rule: true }}} + } }, ref: 'validity2' }, [ @@ -808,7 +819,9 @@ describe('validity functional component', () => { h('validity', { props: { field: 'field2', - validators: ['required'], + validators: { + required: { props: { value: { rule: true }}} + }, classes: classesProp2 }, ref: 'validity2' @@ -922,7 +935,9 @@ describe('validity functional component', () => { h('validity', { props: { field: 'field2', - validators: ['required'] + validators: { + required: { props: { value: { rule: true }}} + } }, ref: 'validity2' }, [ diff --git a/test/unit/components/validity/progresses.test.js b/test/unit/components/validity/progresses.test.js index 5162a1f..441d950 100644 --- a/test/unit/components/validity/progresses.test.js +++ b/test/unit/components/validity/progresses.test.js @@ -60,13 +60,12 @@ describe('validity component: progresses', () => { const vm = new Vue(baseOptions) const progresses = [] const unwatch = vm.$watch('progresses', (val) => { - console.log('progresses#watch', JSON.stringify(val)) progresses.push(val.max.prop1) }, { deep: true }) // initial assert.equal(vm.progresses.max.prop1, '') waitForUpdate(() => { - vm.validate('max', '') + vm.validate('max', { prop1: '' }, () => {}) }).thenWaitFor(1).then(() => { assert.equal(progresses.shift(), 'running') assert.equal(progresses.shift(), '') diff --git a/test/unit/components/validity/props.test.js b/test/unit/components/validity/props.test.js index 55dca34..325b6ff 100644 --- a/test/unit/components/validity/props.test.js +++ b/test/unit/components/validity/props.test.js @@ -61,7 +61,7 @@ describe('validity component: props', () => { const handler = jasmine.createSpy() vm.$on('validate', handler) waitForUpdate(() => { - vm.validate('max', 512, () => {}) + vm.validate('max', { prop1: 512 }, () => {}) }).thenWaitFor(6).then(() => { const calls = handler.calls assert(calls.count() === 1) diff --git a/test/unit/elements/component.test.js b/test/unit/elements/component.test.js index b0a4564..df71957 100644 --- a/test/unit/elements/component.test.js +++ b/test/unit/elements/component.test.js @@ -9,26 +9,33 @@ describe('ComponentElement class', () => { const vm = new Vue({ data: { child: null, - value: 'hello' + value: 'hello', + prop1: 'foo' }, components: { comp: { - props: ['value'], + props: ['value', 'prop1'], render (h) { return h('input', { attrs: { type: 'text' }}) } } }, render (h) { - return (this.child = h('comp', { props: { value: this.value }})) + return (this.child = h('comp', { props: { value: this.value, prop1: this.prop1 }})) + }, + methods: { + _validatorProps () { + return ['value', 'prop1'] + } } }).$mount() const component = new ComponentElement(vm, vm.child) - assert.equal(component.getValue(), 'hello') + assert.deepEqual(component.getValue(), { value: 'hello', prop1: 'foo' }) waitForUpdate(() => { vm.value = 'world' + vm.prop1 = 'bar' }).thenWaitFor(1).then(() => { - assert.equal(component.getValue(), 'world') + assert.deepEqual(component.getValue(), { value: 'world', prop1: 'bar'}) }).then(done) }) }) @@ -40,27 +47,35 @@ describe('ComponentElement class', () => { const vm = new Vue({ data: { child: null, - value: 'hello' + value: 'hello', + prop1: 'foo' }, components: { comp: { - props: ['value'], + props: ['value', 'prop1'], render (h) { return h('input', { attrs: { type: 'text' }}) } } }, render (h) { - return (this.child = h('comp', { props: { value: this.value }})) + return (this.child = h('comp', { props: { value: this.value, prop1: this.prop1 }})) + }, + methods: { + _validatorProps () { + return ['value', 'prop1'] + } } }).$mount() const component = new ComponentElement(vm, vm.child) assert(component.checkModified() === false) waitForUpdate(() => { vm.value = 'world' + vm.prop1 = 'bar' }).thenWaitFor(1).then(() => { assert(component.checkModified() === true) vm.value = 'hello' + vm.prop1 = 'foo' }).thenWaitFor(1).then(() => { assert(component.checkModified() === false) }).then(done) @@ -71,15 +86,15 @@ describe('ComponentElement class', () => { describe('#listenToucheableEvent / #unlistenToucheableEvent', () => { it('should be work', done => { const handleFocusout = jasmine.createSpy() + const _validatorProps = function () { return ['value', 'prop1'] } const vm = new Vue({ - methods: { willUpdateTouched: handleFocusout }, data: { child: null, value: 'hello' }, components: { comp: { - props: ['value'], + props: ['value', 'prop1'], render (h) { return h('input', { attrs: { type: 'text' }}) } @@ -87,7 +102,11 @@ describe('ComponentElement class', () => { }, render (h) { return (this.child = h('comp', { props: { value: this.value }})) - } + }, + methods: { + willUpdateTouched: handleFocusout, + _validatorProps + }, }).$mount() const component = new ComponentElement(vm, vm.child) component.listenToucheableEvent() @@ -107,34 +126,38 @@ describe('ComponentElement class', () => { describe('component', () => { it('should be work', done => { const watchInputable = jasmine.createSpy() + const _validatorProps = function () { return ['value', 'prop1'] } const vm = new Vue({ data: { child: null, - value: 'hello' + value: 'hello', + prop1: 'foo' }, - methods: { watchInputable }, components: { comp: { - props: ['value'], + props: ['value', 'prop1'], render (h) { return h('input', { attrs: { type: 'text' }}) } } }, render (h) { - return (this.child = h('comp', { props: { value: this.value }})) - } + return (this.child = h('comp', { props: { value: this.value, prop1: this.prop1 }})) + }, + methods: { watchInputable, _validatorProps } }).$mount() const component = new ComponentElement(vm, vm.child) component.listenInputableEvent() waitForUpdate(() => { vm.value = 'world' + vm.prop1 = 'bar' }).thenWaitFor(1).then(() => { - assert(watchInputable.calls.count() === 1) + assert(watchInputable.calls.count() === 2) component.unlistenInputableEvent() vm.value = 'hello' + vm.prop1 = 'foo' }).thenWaitFor(1).then(() => { - assert(watchInputable.calls.count() === 1) + assert(watchInputable.calls.count() === 2) }).then(done) }) })