Skip to content
This repository has been archived by the owner on Dec 25, 2017. It is now read-only.

Commit

Permalink
⚡ improvement(component): fix component validation [ci skip]
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Nov 28, 2016
1 parent 3c9cb5e commit 53258ea
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/components/validity/computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 7 additions & 6 deletions src/components/validity/lifecycles.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default function (Vue: GlobalAPI): Object {
: null
}

function mapValidatorProps (validators: any) {
function getValidatorProps (validators: any): Array<string> {
const normalized = typeof validators === 'string' ? [validators] : validators
const ret: Dictionary<Array<string>> = {}
const targets: Array<string> = []
if (isPlainObject(normalized)) {
Object.keys(normalized).forEach((validator: string) => {
const props: ?Object = (normalized[validator]
Expand All @@ -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 {
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/components/validity/methods-validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 17 additions & 9 deletions src/elements/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,32 @@ 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()
}

attachValidity (): void {
this._vm.$el.$validity = this._vm
}

getValidatorProps (): Array<string> {
const vm = this._vm
return vm._validatorProps(vm._uid.toString(), vm.validators)
}

getValue (): any {
return this._vnode.child.value
const value: Dictionary<string> = {}
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 {
Expand All @@ -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 {
Expand Down
29 changes: 22 additions & 7 deletions test/unit/components/validity-functional.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,15 @@ describe('validity functional component', () => {
h('validity', {
props: {
field: 'field1',
validators: ['required']
validators: {
required: {
props: {
value: {
rule: true
}
}
}
}
},
ref: 'validity'
}, [
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -625,7 +634,9 @@ describe('validity functional component', () => {
h('validity', {
props: {
field: 'field2',
validators: ['required']
validators: {
required: { props: { value: { rule: true }}}
}
},
ref: 'validity2'
}, [
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -922,7 +935,9 @@ describe('validity functional component', () => {
h('validity', {
props: {
field: 'field2',
validators: ['required']
validators: {
required: { props: { value: { rule: true }}}
}
},
ref: 'validity2'
}, [
Expand Down
3 changes: 1 addition & 2 deletions test/unit/components/validity/progresses.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(), '')
Expand Down
2 changes: 1 addition & 1 deletion test/unit/components/validity/props.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
59 changes: 41 additions & 18 deletions test/unit/elements/component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})
Expand All @@ -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)
Expand All @@ -71,23 +86,27 @@ 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' }})
}
}
},
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()
Expand All @@ -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)
})
})
Expand Down

0 comments on commit 53258ea

Please sign in to comment.