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

Commit

Permalink
feat(messages): support messages validation property
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Nov 24, 2015
1 parent 937226a commit 34564ec
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/directives/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function (Vue) {
each(value, (val, key) => {
if (_.isPlainObject(val)) {
if ('rule' in val) {
this.validation.updateValidate(key, val.rule)
this.validation.updateValidate(key, val.rule, ('message' in val ? val.message : null))
}
} else {
this.validation.updateValidate(key, val)
Expand Down
25 changes: 24 additions & 1 deletion src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ export function warn (msg, err) {
}
}

/**
* empty
*
* @param {Array|Object} target
* @return {Boolean}
*/

export function empty (target) {
if (target === null) { return true }

if (Array.isArray(target)) {
if (target.length > 0) { return false }
if (target.length === 0) { return true }
} else if (exports.Vue.util.isPlainObject(target)) {
for (let key in target) {
if (exports.Vue.util.hasOwn(target, key)) { return false }
}
}

return true
}

/**
* each
*
Expand All @@ -38,8 +60,9 @@ export function each (target, iterator, context) {
iterator.call(context || target[i], target[i], i)
}
} else if (exports.Vue.util.isPlainObject(target)) {
const hasOwn = exports.Vue.util.hasOwn
for (let key in target) {
if (key in target) {
if (hasOwn(target, key)) {
iterator.call(context || target[key], target[key], key)
}
}
Expand Down
22 changes: 17 additions & 5 deletions src/validation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import util, { each, trigger } from './util'
import util, { empty, each, trigger } from './util'


/**
Expand Down Expand Up @@ -35,9 +35,12 @@ export default class Validation {
return ret
}

updateValidate (name, arg, fn) {
updateValidate (name, arg, msg, fn) {
if (this.validates[name]) {
this.validates[name].arg = arg
if (msg) {
this.validates[name].msg = msg
}
if (fn) {
this.validates[name].fn = fn
}
Expand Down Expand Up @@ -65,28 +68,37 @@ export default class Validation {

validate () {
const extend = util.Vue.util.extend
let ret = {}
let ret = Object.create(null)
let messages = Object.create(null)
let valid = true

each(this.validates, (descriptor, name) => {
let res = descriptor.fn(this.el.value, descriptor.arg)
if (!res) {
valid = false
let msg = descriptor.msg
if (msg) {
messages[name] = typeof msg === 'function' ? msg() : msg
}
}
ret[name] = !res
}, this)

trigger(this.el, valid ? 'valid' : 'invalid')

extend(ret, {
let props = {
valid: valid,
invalid: !valid,
touched: this.touched,
untouched: !this.touched,
dirty: this.dirty,
pristine: !this.dirty,
modified: this.modified
})
}
if (!empty(messages)) {
props.messages = messages
}
extend(ret, props)

return ret
}
Expand Down
34 changes: 24 additions & 10 deletions src/validator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import util, { each, pull } from './util'
import util, { empty, each, pull } from './util'


/**
Expand All @@ -9,17 +9,12 @@ export default class Validator {

constructor (name, dir, groups) {
this.name = name
this.scope = {} // TODO: change to Object.create(null)
/*
this.scope = Object.create(null)
this.scope.a = 1
*/

this._dir = dir
this._validations = []
this._groups = groups

this._groupValidations = Object.create(null)

each(groups, (group) => {
this._groupValidations[group] = []
}, this)
Expand All @@ -40,7 +35,7 @@ export default class Validator {
}

removeValidation (validation) {
util.Vue.util.delete(this.scope, validation.model)
util.Vue.util.del(this.scope, validation.model)
pull(this._validations, validation)
}

Expand Down Expand Up @@ -86,7 +81,8 @@ export default class Validator {
untouched: { fn: this._defineUntouched, arg: target },
modified: { fn: this._defineModified, arg: validations },
dirty: { fn: this._defineDirty, arg: validations },
pristine: { fn: this._definePristine, arg: target }
pristine: { fn: this._definePristine, arg: target },
messages: { fn: this._defineMessages, arg: validations }
}, (descriptor, name) => {
Object.defineProperty(target, name, {
enumerable: true,
Expand All @@ -99,11 +95,12 @@ export default class Validator {
}

_walkValidations (validations, property, condition) {
const hasOwn = util.Vue.util.hasOwn
let ret = condition

each(validations, (validation, index) => {
if (ret === !condition) { return }
if (Object.prototype.hasOwnProperty.call(this.scope, validation.model)) {
if (hasOwn(this.scope, validation.model)) {
var target = this.scope[validation.model]
if (target && target[property] === !condition) {
ret = !condition
Expand Down Expand Up @@ -141,4 +138,21 @@ export default class Validator {
_definePristine (scope) {
return !scope.dirty
}

_defineMessages (validations) {
const extend = util.Vue.util.extend
const hasOwn = util.Vue.util.hasOwn
let ret = Object.create(null)

each(validations, (validation, index) => {
if (hasOwn(this.scope, validation.model)) {
let target = this.scope[validation.model]
if (target && !empty(target['messages'])) {
ret[validation.model] = extend(Object.create(null), target['messages'])
}
}
}, this)

return empty(ret) ? undefined : ret
}
}
1 change: 1 addition & 0 deletions test/specs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ require('./invalid')
require('./event')
require('./group')
require('./multiple')
require('./messages')
151 changes: 144 additions & 7 deletions test/specs/messages.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,158 @@
import assert from 'power-assert'
import Vue from 'vue'
import { trigger } from '../../src/util'
import { empty, trigger } from '../../src/util'


describe('async', () => {
describe('messages', () => {
let el, vm

beforeEach((done) => {
el = document.createElement('div')
el.innerHTML =
'<validator :groups="[\'group1\', \'group2\']"name="validation">' +
'<input type="text" group="group1" v-validate:field1.pattern="field1">' +
'<input type="text" group="group1" v-validate:field2.required="field2">' +
'<input type="text" group="group2" v-validate:field3.max="field3">' +
'<input type="text" group="group2" v-validate:field4.maxlength="field4">' +
'<input type="text" group="group1" value="0" v-validate:field5.min="{ min: { rule :1, message: message1 } }">' +
'<input type="text" group="group2" value="h" v-validate:field6.minlength="{ minlength: { rule: 4, message: onMessage2 } }">' +
'<ul><li v-for="msg in $validation.messages">' +
'<div v-for="val in msg"><p>{{$key}}:{{val}}</p></div>' +
'</li></ul>' +
'</validator>'
vm = new Vue({
el: el
el: el,
data: {
field1: { pattern: { rule: '/foo/', message: 'field1 pattern error' } },
field2: { required: { rule: true, message: 'field2 required' } },
field3: { max: { rule: 3, message: 'field3 big too' } },
field4: { maxlength: { rule: 3, message: 'field4 long too' } }
},
computed: {
message1 () {
return 'field5 small too'
}
},
methods: {
onMessage2 () {
return 'field6 short too'
}
}
})
done()
vm.$nextTick(done)
})

it.skip('TODO', () => {
trigger(el, 'input')
assert(vm)
context('invalid', () => {
beforeEach((done) => {
let field3 = el.getElementsByTagName('input')[2]
field3.value = '4'
trigger(field3, 'input')
trigger(field3, 'blur')
vm.$nextTick(() => {
let field4 = el.getElementsByTagName('input')[3]
field4.value = 'hello'
trigger(field4, 'input')
trigger(field4, 'blur')
done()
})
})

describe('fields', () => {
it('should be kept messages', () => {
assert(vm.$validation.field1.messages.pattern === vm.field1.pattern.message)
assert(vm.$validation.field2.messages.required === vm.field2.required.message)
assert(vm.$validation.field3.messages.max === vm.field3.max.message)
assert(vm.$validation.field4.messages.maxlength === vm.field4.maxlength.message)
assert(vm.$validation.field5.messages.min === vm.message1)
assert(vm.$validation.field6.messages.minlength === vm.onMessage2())
})
})

describe('top', () => {
it('should be kept messages', () => {
assert(vm.$validation.messages.field1.pattern === vm.field1.pattern.message)
assert(vm.$validation.messages.field2.required === vm.field2.required.message)
assert(vm.$validation.messages.field3.max === vm.field3.max.message)
assert(vm.$validation.messages.field4.maxlength === vm.field4.maxlength.message)
assert(vm.$validation.messages.field5.min === vm.message1)
assert(vm.$validation.messages.field6.minlength === vm.onMessage2())
})
})

describe('group', () => {
it('should be kept messages', () => {
assert(vm.$validation.group1.messages.field1.pattern === vm.field1.pattern.message)
assert(vm.$validation.group1.messages.field2.required === vm.field2.required.message)
assert(vm.$validation.group1.messages.field5.min === vm.message1)
assert(vm.$validation.group2.messages.field3.max === vm.field3.max.message)
assert(vm.$validation.group2.messages.field4.maxlength === vm.field4.maxlength.message)
assert(vm.$validation.group2.messages.field6.minlength === vm.onMessage2())
})
})
})


context('valid', () => {
beforeEach((done) => {
let field1 = el.getElementsByTagName('input')[0]
field1.value = 'foo'
trigger(field1, 'input')
trigger(field1, 'blur')
vm.$nextTick(() => {
let field2 = el.getElementsByTagName('input')[1]
field2.value = 'hello'
trigger(field2, 'input')
trigger(field2, 'blur')
vm.$nextTick(() => {
let field3 = el.getElementsByTagName('input')[2]
field3.value = '1'
trigger(field3, 'input')
trigger(field3, 'blur')
vm.$nextTick(() => {
let field4 = el.getElementsByTagName('input')[3]
field4.value = 'hi'
trigger(field4, 'input')
trigger(field4, 'blur')
vm.$nextTick(() => {
let field5 = el.getElementsByTagName('input')[4]
field5.value = '10'
trigger(field5, 'input')
trigger(field5, 'blur')
vm.$nextTick(() => {
let field6 = el.getElementsByTagName('input')[5]
field6.value = 'hello'
trigger(field6, 'input')
trigger(field6, 'blur')
done()
})
})
})
})
})
})

describe('fields', () => {
it('should not be kept', () => {
assert(empty(vm.$validation.field1.messages))
assert(empty(vm.$validation.field2.messages))
assert(empty(vm.$validation.field3.messages))
assert(empty(vm.$validation.field4.messages))
assert(empty(vm.$validation.field5.messages))
assert(empty(vm.$validation.field6.messages))
})
})

describe('top', () => {
it('should not be kept', () => {
assert(empty(vm.$validation.messages))
})
})

describe('group', () => {
it('should not be kept', () => {
assert(empty(vm.$validation.group1.messages))
assert(empty(vm.$validation.group2.messages))
})
})
})
})

0 comments on commit 34564ec

Please sign in to comment.