diff --git a/docs/en/installation.md b/docs/en/installation.md
index 1e58203..72859b0 100644
--- a/docs/en/installation.md
+++ b/docs/en/installation.md
@@ -9,7 +9,7 @@ See [dist folder](https://github.com/vuejs/vue-validator/tree/dev/dist). Note th
### jsdelivr
```html
-
+
```
## NPM
@@ -25,7 +25,10 @@ See [dist folder](https://github.com/vuejs/vue-validator/tree/dev/dist). Note th
$ npm install
$ npm run build
-When used in CommonJS, you must explicitly install the router via `Vue.use()`:
+When used in CommonJS, you must explicitly install the validator via `Vue.use()`:
+
+> :warning: if you are using `vue-router`, you must install with `Vue.use()` in advance of `router#start`.
+
```javascript
var Vue = require('vue')
var VueValidator = require('vue-validator')
diff --git a/docs/en/syntax.md b/docs/en/syntax.md
index b71f845..20653d8 100644
--- a/docs/en/syntax.md
+++ b/docs/en/syntax.md
@@ -32,7 +32,7 @@ v2.0-alpha later:
```
-### Caml-case property
+### Camel-case property
As well as [Vue.js](http://vuejs.org/guide/components.html#camelCase_vs-_kebab-case), you can use the kebab-case for `v-validate` models:
```html
diff --git a/example/classes/basic/index.html b/example/classes/basic/index.html
new file mode 100644
index 0000000..82a4d21
--- /dev/null
+++ b/example/classes/basic/index.html
@@ -0,0 +1,41 @@
+
+
+
+
+
validation classes basic example
+
+
+
+
+
+
+
+
+
+ {{usernameClasses}}
+
+
+
+
+
+
diff --git a/example/classes/custom/index.html b/example/classes/custom/index.html
new file mode 100644
index 0000000..073a1d2
--- /dev/null
+++ b/example/classes/custom/index.html
@@ -0,0 +1,46 @@
+
+
+
+
+
validation custom classes example
+
+
+
+
+
+
+
+
+
+ {{usernameClasses}}
+
+
+
+
+
+
diff --git a/example/classes/element/index.html b/example/classes/element/index.html
new file mode 100644
index 0000000..35328b4
--- /dev/null
+++ b/example/classes/element/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
validation classes to another element example
+
+
+
+
+
+
+
+
+
+ {{usernameClasses}}
+
+
+
+
+
+
diff --git a/example/errors/focus/index.html b/example/errors/focus/index.html
index 1d837ad..94e2d97 100644
--- a/example/errors/focus/index.html
+++ b/example/errors/focus/index.html
@@ -18,7 +18,7 @@
diff --git a/package.json b/package.json
index 3fb2f78..7ed4667 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vue-validator",
"description": "Validator component for Vue.js",
- "version": "2.0.2",
+ "version": "2.1.0",
"author": {
"name": "kazuya kawaguchi",
"email": "kawakazu80@gmail.com"
@@ -19,6 +19,7 @@
"babel-preset-es2015": "^6.5.0",
"babel-preset-es2015-loose": "^7.0.0",
"babel-preset-es2015-loose-rollup": "git+https://github.com/kazupon/babel-preset-es2015-loose-rollup.git#master",
+ "component-classes": "^1.2.6",
"eslint": "^2.7.0",
"eslint-config-standard": "^5.1.0",
"eslint-loader": "^1.3.0",
diff --git a/src/const.js b/src/const.js
new file mode 100644
index 0000000..6b6a92e
--- /dev/null
+++ b/src/const.js
@@ -0,0 +1,5 @@
+export const VALIDATE_UPDATE = '__vue-validator-validate-update__'
+export const PRIORITY_VALIDATE = 16
+export const PRIORITY_VALIDATE_CLASS = 32
+export const REGEX_FILTER = /[^|]\|[^|]/
+export const REGEX_VALIDATE_DIRECTIVE = /^v-validate(?:$|:(.*)$)/
diff --git a/src/directives/validate-class.js b/src/directives/validate-class.js
new file mode 100644
index 0000000..b5d3910
--- /dev/null
+++ b/src/directives/validate-class.js
@@ -0,0 +1,82 @@
+import { VALIDATE_UPDATE, PRIORITY_VALIDATE_CLASS, REGEX_VALIDATE_DIRECTIVE } from '../const'
+
+
+let classId = 0 // ID for validation class
+
+
+export default function (Vue) {
+ const vIf = Vue.directive('if')
+ const FragmentFactory = Vue.FragmentFactory
+ const { toArray, replace, createAnchor } = Vue.util
+
+
+ /**
+ * `v-validate-class` directive
+ */
+
+ Vue.directive('validate-class', {
+ terminal: true,
+ priority: vIf.priority + PRIORITY_VALIDATE_CLASS,
+
+ bind () {
+ const id = String(classId++)
+ this.setClassIds(this.el, id)
+
+ this.vm.$on(VALIDATE_UPDATE, this.cb = (classIds, validation, results) => {
+ if (classIds.indexOf(id) > -1) {
+ validation.updateClasses(results, this.frag.node)
+ }
+ })
+
+ this.setupFragment()
+ },
+
+ unbind () {
+ this.vm.$off(VALIDATE_UPDATE, this.cb)
+ this.teardownFragment()
+ },
+
+ setClassIds (el, id) {
+ let childNodes = toArray(el.childNodes)
+ for (let i = 0, l = childNodes.length; i < l; i++) {
+ let element = childNodes[i]
+ if (element.nodeType === 1) {
+ let hasAttrs = element.hasAttributes()
+ let attrs = hasAttrs && toArray(element.attributes)
+ for (let k = 0, l = attrs.length; k < l; k++) {
+ let attr = attrs[k]
+ if (attr.name.match(REGEX_VALIDATE_DIRECTIVE)) {
+ let existingId = element.getAttribute(VALIDATE_UPDATE)
+ let value = existingId ? (existingId + ',' + id) : id
+ element.setAttribute(VALIDATE_UPDATE, value)
+ }
+ }
+ }
+
+ if (element.hasChildNodes()) {
+ this.setClassIds(element, id)
+ }
+ }
+ },
+
+ setupFragment () {
+ this.anchor = createAnchor('v-validate-class')
+ replace(this.el, this.anchor)
+
+ this.factory = new FragmentFactory(this.vm, this.el)
+ this.frag = this.factory.create(this._host, this._scope, this._frag)
+ this.frag.before(this.anchor)
+ },
+
+ teardownFragment () {
+ if (this.frag) {
+ this.frag.remove()
+ this.frag = null
+ this.factory = null
+ }
+
+ replace(this.anchor, this.el)
+ this.anchor = null
+ }
+ })
+}
diff --git a/src/directives/validate.js b/src/directives/validate.js
index 259c735..6b08d8f 100644
--- a/src/directives/validate.js
+++ b/src/directives/validate.js
@@ -1,16 +1,19 @@
+import { PRIORITY_VALIDATE, REGEX_FILTER } from '../const'
import { warn, each } from '../util'
export default function (Vue) {
- const _ = Vue.util
const vIf = Vue.directive('if')
const FragmentFactory = Vue.FragmentFactory
const parseDirective = Vue.parsers.directive.parseDirective
- const REGEX_FILTER = /[^|]\|[^|]/
+ const {
+ inBrowser, bind, on, off, createAnchor,
+ replace, camelize, isPlainObject
+ } = Vue.util
// Test for IE10/11 textarea placeholder clone bug
function checkTextareaCloneBug () {
- if (_.inBrowser) {
+ if (inBrowser) {
let t = document.createElement('textarea')
t.placeholder = 't'
return t.cloneNode(true).value === 't'
@@ -27,8 +30,8 @@ export default function (Vue) {
Vue.directive('validate', {
terminal: true,
- priority: vIf.priority + 16,
- params: ['group', 'field', 'detect-blur', 'detect-change', 'initial'],
+ priority: vIf.priority + PRIORITY_VALIDATE,
+ params: ['group', 'field', 'detect-blur', 'detect-change', 'initial', 'classes'],
paramWatchers: {
detectBlur (val, old) {
@@ -67,6 +70,13 @@ export default function (Vue) {
return
}
+ if ((process.env.NODE_ENV !== 'production')
+ && !(this.arg || this.params.field)) {
+ warn('you need specify field name for v-validate directive.')
+ this._invalid = true
+ return
+ }
+
let validatorName = this.vm.$options._validator
if ((process.env.NODE_ENV !== 'production') && !validatorName) {
warn('v-validate need to use into validator element directive: '
@@ -89,13 +99,18 @@ export default function (Vue) {
update (value, old) {
if (!value || this._invalid) { return }
- if (_.isPlainObject(value)) {
+ if (isPlainObject(value)) {
this.handleObject(value)
} else if (Array.isArray(value)) {
this.handleArray(value)
}
- this.validator.validate({ field: this.field, noopable: this._initialNoopValidation })
+ let options = { field: this.field, noopable: this._initialNoopValidation }
+ if (this.frag) {
+ options.el = this.frag.node
+ }
+ this.validator.validate(options)
+
if (this._initialNoopValidation) {
this._initialNoopValidation = null
}
@@ -124,14 +139,18 @@ export default function (Vue) {
const params = this.params
let validator = this.validator = this.vm._validatorMaps[name]
- this.field = _.camelize(this.arg ? this.arg : params.field)
+ this.field = camelize(this.arg ? this.arg : params.field)
this.validation = validator.manageValidation(
- this.field, model, this.vm, this.frag.node, this._scope, filters,
+ this.field, model, this.vm, this.frag.node,
+ this._scope, filters, params.initial,
this.isDetectBlur(params.detectBlur),
this.isDetectChange(params.detectChange)
)
+ isPlainObject(params.classes)
+ && this.validation.setValidationClasses(params.classes)
+
params.group
&& validator.addGroupValidation(params.group, this.field)
@@ -143,24 +162,24 @@ export default function (Vue) {
const validation = this.validation
const el = this.frag.node
- this.onBlur = _.bind(validation.listener, validation)
- _.on(el, 'blur', this.onBlur)
+ this.onBlur = bind(validation.listener, validation)
+ on(el, 'blur', this.onBlur)
if ((el.type === 'radio'
|| el.tagName === 'SELECT') && !model) {
- this.onChange = _.bind(validation.listener, validation)
- _.on(el, 'change', this.onChange)
+ this.onChange = bind(validation.listener, validation)
+ on(el, 'change', this.onChange)
} else if (el.type === 'checkbox') {
if (!model) {
- this.onChange = _.bind(validation.listener, validation)
- _.on(el, 'change', this.onChange)
+ this.onChange = bind(validation.listener, validation)
+ on(el, 'change', this.onChange)
} else {
- this.onClick = _.bind(validation.listener, validation)
- _.on(el, 'click', this.onClick)
+ this.onClick = bind(validation.listener, validation)
+ on(el, 'click', this.onClick)
}
} else {
if (!model) {
- this.onInput = _.bind(validation.listener, validation)
- _.on(el, 'input', this.onInput)
+ this.onInput = bind(validation.listener, validation)
+ on(el, 'input', this.onInput)
}
}
},
@@ -169,22 +188,22 @@ export default function (Vue) {
const el = this.frag.node
if (this.onInput) {
- _.off(el, 'input', this.onInput)
+ off(el, 'input', this.onInput)
this.onInput = null
}
if (this.onClick) {
- _.off(el, 'click', this.onClick)
+ off(el, 'click', this.onClick)
this.onClick = null
}
if (this.onChange) {
- _.off(el, 'change', this.onChange)
+ off(el, 'change', this.onChange)
this.onChange = null
}
if (this.onBlur) {
- _.off(el, 'blur', this.onBlur)
+ off(el, 'blur', this.onBlur)
this.onBlur = null
}
},
@@ -205,8 +224,8 @@ export default function (Vue) {
},
setupFragment () {
- this.anchor = _.createAnchor('v-validate')
- _.replace(this.el, this.anchor)
+ this.anchor = createAnchor('v-validate')
+ replace(this.el, this.anchor)
this.factory = new FragmentFactory(this.vm, this.shimNode(this.el))
this.frag = this.factory.create(this._host, this._scope, this._frag)
@@ -220,7 +239,7 @@ export default function (Vue) {
this.factory = null
}
- _.replace(this.anchor, this.el)
+ replace(this.anchor, this.el)
this.anchor = null
},
@@ -232,7 +251,7 @@ export default function (Vue) {
handleObject (value) {
each(value, (val, key) => {
- if (_.isPlainObject(val)) {
+ if (isPlainObject(val)) {
if ('rule' in val) {
let msg = 'message' in val ? val.message : null
let initial = 'initial' in val ? val.initial : null
diff --git a/src/directives/validator.js b/src/directives/validator.js
index 450ebda..c62178f 100644
--- a/src/directives/validator.js
+++ b/src/directives/validator.js
@@ -3,10 +3,12 @@ import Validator from '../validator'
export default function (Vue) {
- const _ = Vue.util
const FragmentFactory = Vue.FragmentFactory
const vIf = Vue.directive('if')
- const camelize = Vue.util.camelize
+ const {
+ isArray, isPlainObject, createAnchor,
+ replace, extend, camelize
+ } = Vue.util
/**
@@ -14,7 +16,7 @@ export default function (Vue) {
*/
Vue.elementDirective('validator', {
- params: ['name', 'groups', 'lazy'],
+ params: ['name', 'groups', 'lazy', 'classes'],
bind () {
const params = this.params
@@ -31,7 +33,12 @@ export default function (Vue) {
throw new Error('Invalid validator management error')
}
- this.setupValidator()
+ let classes = {}
+ if (isPlainObject(this.params.classes)) {
+ classes = this.params.classes
+ }
+
+ this.setupValidator(classes)
this.setupFragment(params.lazy)
},
@@ -45,9 +52,9 @@ export default function (Vue) {
let groups = []
if (params.groups) {
- if (_.isArray(params.groups)) {
+ if (isArray(params.groups)) {
groups = params.groups
- } else if (!_.isPlainObject(params.groups)
+ } else if (!isPlainObject(params.groups)
&& typeof params.groups === 'string') {
groups.push(params.groups)
}
@@ -56,10 +63,10 @@ export default function (Vue) {
return groups
},
- setupValidator () {
+ setupValidator (classes) {
const validator
= this.validator
- = new Validator(this.validatorName, this, this.getGroups())
+ = new Validator(this.validatorName, this, this.getGroups(), classes)
validator.enableReactive()
validator.setupScope()
validator.registerEvents()
@@ -79,9 +86,9 @@ export default function (Vue) {
const vm = this.vm
this.validator.waitFor(() => {
- this.anchor = _.createAnchor('vue-validator')
- _.replace(this.el, this.anchor)
- _.extend(vm.$options, { _validator: this.validatorName })
+ this.anchor = createAnchor('vue-validator')
+ replace(this.el, this.anchor)
+ extend(vm.$options, { _validator: this.validatorName })
this.factory = new FragmentFactory(vm, this.el.innerHTML)
vIf.insert.call(this)
})
diff --git a/src/index.js b/src/index.js
index a93f04c..b3db277 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,7 @@
import util, { warn } from './util'
import Asset from './asset'
import Override from './override'
+import ValidateClass from './directives/validate-class'
import Validate from './directives/validate'
import Validator from './directives/validator'
import Errors from './components/errors'
@@ -25,10 +26,11 @@ function plugin (Vue, options = {}) {
Override(Vue)
Validator(Vue)
+ ValidateClass(Vue)
Validate(Vue)
}
-plugin.version = '2.0.2'
+plugin.version = '2.1.0'
export default plugin
diff --git a/src/util.js b/src/util.js
index 3e4ed08..adce225 100644
--- a/src/util.js
+++ b/src/util.js
@@ -127,3 +127,24 @@ export function trigger (el, event, args) {
export function isPromise (p) {
return p && typeof p.then === 'function'
}
+
+/**
+ * Togging classes
+ *
+ * @param {Element} el
+ * @param {String} key
+ * @param {Function} fn
+ */
+
+export function toggleClasses (el, key, fn) {
+ key = key.trim()
+ if (key.indexOf(' ') === -1) {
+ fn(el, key)
+ return
+ }
+
+ let keys = key.split(/\s+/)
+ for (let i = 0, l = keys.length; i < l; i++) {
+ fn(el, keys[i])
+ }
+}
diff --git a/src/validations/base.js b/src/validations/base.js
index a6f9d8d..0bd0dbc 100644
--- a/src/validations/base.js
+++ b/src/validations/base.js
@@ -1,4 +1,5 @@
-import util, { empty, each, trigger, isPromise } from '../util'
+import { VALIDATE_UPDATE } from '../const'
+import util, { empty, each, trigger, isPromise, toggleClasses } from '../util'
/**
@@ -24,6 +25,7 @@ export default class BaseValidation {
this._validators = {}
this._detectBlur = detectBlur
this._detectChange = detectChange
+ this._classes = {}
}
get vm () { return this._vm }
@@ -36,9 +38,18 @@ export default class BaseValidation {
get detectBlur () { return this._detectBlur }
set detectBlur (val) { this._detectBlur = val }
- manageElement (el) {
+ manageElement (el, initial) {
const scope = this._getScope()
const model = this._model
+
+ this._initial = initial
+
+ const classIds = el.getAttribute(VALIDATE_UPDATE)
+ if (classIds) {
+ el.removeAttribute(VALIDATE_UPDATE)
+ this._classIds = classIds.split(',')
+ }
+
if (model) {
el.value = this._evalModel(model, this._filters)
this._unwatch = scope.$watch(model, (val, old) => {
@@ -46,7 +57,11 @@ export default class BaseValidation {
if (this.guardValidate(el, 'input')) {
return
}
- this.handleValidate(el)
+
+ this.handleValidate(el, this._initial)
+ if (this._initial) {
+ this._initial = null
+ }
}
}, { deep: true })
}
@@ -74,6 +89,12 @@ export default class BaseValidation {
}
}
+ setValidationClasses (classes) {
+ each(classes, (value, key) => {
+ this._classes[key] = value
+ })
+ }
+
willUpdateFlags (touched = false) {
touched && this.willUpdateTouched(this._el, 'blur')
this.willUpdateDirty(this._el)
@@ -107,18 +128,18 @@ export default class BaseValidation {
return
}
- this.handleValidate(e.target, e.type)
+ this.handleValidate(e.target, { type: e.type })
}
- handleValidate (el, type) {
+ handleValidate (el, { type = null, noopable = false } = {}) {
this.willUpdateTouched(el, type)
this.willUpdateDirty(el)
this.willUpdateModified(el)
- this._validator.validate({ field: this.field })
+ this._validator.validate({ field: this.field, el: el, noopable: noopable })
}
- validate (cb, noopable = false) {
+ validate (cb, noopable = false, el = null) {
const _ = util.Vue.util
let results = {}
@@ -200,6 +221,8 @@ export default class BaseValidation {
}
_.extend(results, props)
+ this.willUpdateClasses(results, el)
+
cb(results)
})
}
@@ -221,6 +244,21 @@ export default class BaseValidation {
this._init = this._getValue(this._el)
}
+ willUpdateClasses (results, el = null) {
+ if (this._checkClassIds(el)) {
+ const classIds = this._getClassIds(el)
+ this.vm.$nextTick(() => {
+ this.vm.$emit(VALIDATE_UPDATE, classIds, this, results)
+ })
+ } else {
+ this.updateClasses(results)
+ }
+ }
+
+ updateClasses (results, el = null) {
+ this._updateClasses(el || this._el, results)
+ }
+
guardValidate (el, type) {
if (type && type === 'blur' && !this.detectBlur) {
return true
@@ -249,10 +287,18 @@ export default class BaseValidation {
return this._forScope || this._vm
}
+ _getClassIds (el) {
+ return this._classIds
+ }
+
_checkModified (target) {
return this._init !== this._getValue(target)
}
+ _checkClassIds (el) {
+ return this._getClassIds(el)
+ }
+
_fireEvent (el, type, args) {
trigger(el, type, args)
}
@@ -270,6 +316,67 @@ export default class BaseValidation {
}
}
+ _updateClasses (el, results) {
+ this._toggleValid(el, results.valid)
+ this._toggleTouched(el, results.touched)
+ this._togglePristine(el, results.pristine)
+ this._toggleModfied(el, results.modified)
+ }
+
+ _toggleValid (el, valid) {
+ const { addClass, removeClass } = util.Vue.util
+ const validClass = this._classes.valid || 'valid'
+ const invalidClass = this._classes.invalid || 'invalid'
+
+ if (valid) {
+ toggleClasses(el, validClass, addClass)
+ toggleClasses(el, invalidClass, removeClass)
+ } else {
+ toggleClasses(el, validClass, removeClass)
+ toggleClasses(el, invalidClass, addClass)
+ }
+ }
+
+ _toggleTouched (el, touched) {
+ const { addClass, removeClass } = util.Vue.util
+ const touchedClass = this._classes.touched || 'touched'
+ const untouchedClass = this._classes.untouched || 'untouched'
+
+ if (touched) {
+ toggleClasses(el, touchedClass, addClass)
+ toggleClasses(el, untouchedClass, removeClass)
+ } else {
+ toggleClasses(el, touchedClass, removeClass)
+ toggleClasses(el, untouchedClass, addClass)
+ }
+ }
+
+ _togglePristine (el, pristine) {
+ const { addClass, removeClass } = util.Vue.util
+ const pristineClass = this._classes.pristine || 'pristine'
+ const dirtyClass = this._classes.dirty || 'dirty'
+
+ if (pristine) {
+ toggleClasses(el, pristineClass, addClass)
+ toggleClasses(el, dirtyClass, removeClass)
+ } else {
+ toggleClasses(el, pristineClass, removeClass)
+ toggleClasses(el, dirtyClass, addClass)
+ }
+ }
+
+ _toggleModfied (el, modified) {
+ const { addClass, removeClass } = util.Vue.util
+ const modifiedClass = this._classes.modified || 'modified'
+
+ if (modified) {
+ toggleClasses(el, modifiedClass, addClass)
+ } else {
+ toggleClasses(el, modifiedClass, removeClass)
+ }
+ }
+
+
_applyFilters (value, oldValue, filters, write) {
const resolveAsset = util.Vue.util.resolveAsset
const scope = this._getScope()
diff --git a/src/validations/checkbox.js b/src/validations/checkbox.js
index 4299a98..f35638c 100644
--- a/src/validations/checkbox.js
+++ b/src/validations/checkbox.js
@@ -1,3 +1,4 @@
+import { VALIDATE_UPDATE } from '../const'
import { each } from '../util'
import BaseValidation from './base'
@@ -14,9 +15,10 @@ export default class CheckboxValidation extends BaseValidation {
this._inits = []
}
- manageElement (el) {
+ manageElement (el, initial) {
const scope = this._getScope()
- let item = this._addItem(el)
+ let item = this._addItem(el, initial)
+
const model = item.model = this._model
if (model) {
let value = this._evalModel(model, this._filters)
@@ -27,7 +29,11 @@ export default class CheckboxValidation extends BaseValidation {
if (this.guardValidate(item.el, 'change')) {
return
}
- this.handleValidate(item.el)
+
+ this.handleValidate(item.el, { noopable: item.initial })
+ if (item.initial) {
+ item.initial = null
+ }
}
})
} else {
@@ -40,12 +46,20 @@ export default class CheckboxValidation extends BaseValidation {
if (this.guardValidate(el, 'change')) {
return
}
- this.handleValidate(el)
+
+ this.handleValidate(el, { noopable: item.initial })
+ if (item.initial) {
+ item.initial = null
+ }
}
})
}
} else {
- this._validator.validate({ field: this.field })
+ let options = { field: this.field, noopable: initial }
+ if (this._checkClassIds(el)) {
+ options.el = el
+ }
+ this._validator.validate(options)
}
}
@@ -67,8 +81,9 @@ export default class CheckboxValidation extends BaseValidation {
this._validator.validate({ field: this.field })
}
- willUpdateFlags () {
+ willUpdateFlags (touched = false) {
each(this._inits, (item, index) => {
+ touched && this.willUpdateTouched(item.el, 'blur')
this.willUpdateDirty(item.el)
this.willUpdateModified(item.el)
})
@@ -82,12 +97,30 @@ export default class CheckboxValidation extends BaseValidation {
})
}
- _addItem (el) {
- const item = {
+ updateClasses (results, el = null) {
+ if (el) { // for another element
+ this._updateClasses(el, results)
+ } else {
+ each(this._inits, (item, index) => {
+ this._updateClasses(item.el, results)
+ })
+ }
+ }
+
+ _addItem (el, initial) {
+ let item = {
el: el,
init: el.checked,
- value: el.value
+ value: el.value,
+ initial: initial
+ }
+
+ const classIds = el.getAttribute(VALIDATE_UPDATE)
+ if (classIds) {
+ el.removeAttribute(VALIDATE_UPDATE)
+ item.classIds = classIds.split(',')
}
+
this._inits.push(item)
return item
}
@@ -113,6 +146,16 @@ export default class CheckboxValidation extends BaseValidation {
}
}
+ _getClassIds (el) {
+ let classIds
+ each(this._inits, (item, index) => {
+ if (item.el === el) {
+ classIds = item.classIds
+ }
+ })
+ return classIds
+ }
+
_checkModified (target) {
if (this._inits.length === 0) {
return this._init !== target.checked
diff --git a/src/validations/radio.js b/src/validations/radio.js
index 6b8da1e..c673a80 100644
--- a/src/validations/radio.js
+++ b/src/validations/radio.js
@@ -1,3 +1,4 @@
+import { VALIDATE_UPDATE } from '../const'
import { each } from '../util'
import BaseValidation from './base'
@@ -14,9 +15,10 @@ export default class RadioValidation extends BaseValidation {
this._inits = []
}
- manageElement (el) {
+ manageElement (el, initial) {
const scope = this._getScope()
- let item = this._addItem(el)
+ let item = this._addItem(el, initial)
+
const model = item.model = this._model
if (model) {
let value = this._evalModel(model, this._filters)
@@ -26,11 +28,19 @@ export default class RadioValidation extends BaseValidation {
if (this.guardValidate(item.el, 'change')) {
return
}
- this.handleValidate(el)
+
+ this.handleValidate(el, { noopable: item.initial })
+ if (item.initial) {
+ item.initial = null
+ }
}
})
} else {
- this._validator.validate({ field: this.field })
+ let options = { field: this.field, noopable: initial }
+ if (this._checkClassIds(el)) {
+ options.el = el
+ }
+ this._validator.validate(options)
}
}
@@ -47,8 +57,9 @@ export default class RadioValidation extends BaseValidation {
this._validator.validate({ field: this.field })
}
- willUpdateFlags () {
+ willUpdateFlags (touched = false) {
each(this._inits, (item, index) => {
+ touched && this.willUpdateTouched(item.el, 'blur')
this.willUpdateDirty(item.el)
this.willUpdateModified(item.el)
})
@@ -62,12 +73,30 @@ export default class RadioValidation extends BaseValidation {
})
}
- _addItem (el) {
- const item = {
+ updateClasses (results, el = null) {
+ if (el) { // for another element
+ this._updateClasses(el, results)
+ } else {
+ each(this._inits, (item, index) => {
+ this._updateClasses(item.el, results)
+ })
+ }
+ }
+
+ _addItem (el, initial) {
+ let item = {
el: el,
init: el.checked,
- value: el.value
+ value: el.value,
+ initial: initial
}
+
+ const classIds = el.getAttribute(VALIDATE_UPDATE)
+ if (classIds) {
+ el.removeAttribute(VALIDATE_UPDATE)
+ item.classIds = classIds.split(',')
+ }
+
this._inits.push(item)
return item
}
@@ -93,6 +122,16 @@ export default class RadioValidation extends BaseValidation {
}
}
+ _getClassIds (el) {
+ let classIds
+ each(this._inits, (item, index) => {
+ if (item.el === el) {
+ classIds = item.classIds
+ }
+ })
+ return classIds
+ }
+
_checkModified (target) {
if (this._inits.length === 0) {
return this._init !== target.checked
diff --git a/src/validations/select.js b/src/validations/select.js
index f7996af..669d0e7 100644
--- a/src/validations/select.js
+++ b/src/validations/select.js
@@ -1,3 +1,4 @@
+import { VALIDATE_UPDATE } from '../const'
import BaseValidation from './base'
@@ -13,9 +14,18 @@ export default class SelectValidation extends BaseValidation {
this._multiple = this._el.hasAttribute('multiple')
}
- manageElement (el) {
+ manageElement (el, initial) {
const scope = this._getScope()
const model = this._model
+
+ this._initial = initial
+
+ const classIds = el.getAttribute(VALIDATE_UPDATE)
+ if (classIds) {
+ el.removeAttribute(VALIDATE_UPDATE)
+ this._classIds = classIds.split(',')
+ }
+
if (model) {
const value = this._evalModel(model, this._filters)
const values = !Array.isArray(value) ? [value] : value
@@ -27,7 +37,11 @@ export default class SelectValidation extends BaseValidation {
if (this.guardValidate(el, 'change')) {
return
}
- this.handleValidate(el)
+
+ this.handleValidate(el, this._initial)
+ if (this._initial) {
+ this._initial = null
+ }
}
})
}
diff --git a/src/validator.js b/src/validator.js
index 18fcd30..d7776a4 100644
--- a/src/validator.js
+++ b/src/validator.js
@@ -14,7 +14,7 @@ const eventRE = /^v-on:|^@/
export default class Validator {
- constructor (name, dir, groups) {
+ constructor (name, dir, groups, classes) {
this.name = name
this._scope = {}
@@ -26,6 +26,7 @@ export default class Validator {
this._groupValidations = {}
this._events = {}
this._modified = false
+ this._classes = classes
each(groups, (group) => {
this._groupValidations[group] = []
@@ -93,27 +94,29 @@ export default class Validator {
return ret
}
- manageValidation (field, model, vm, el, scope, filters, detectBlur, detectChange) {
+ manageValidation (field, model, vm, el, scope, filters, initial, detectBlur, detectChange) {
let validation = null
if (el.tagName === 'SELECT') {
validation = this._manageSelectValidation(
- field, model, vm, el, scope, filters, detectBlur, detectChange
+ field, model, vm, el, scope, filters, initial, detectBlur, detectChange
)
} else if (el.type === 'checkbox') {
validation = this._manageCheckboxValidation(
- field, model, vm, el, scope, filters, detectBlur, detectChange
+ field, model, vm, el, scope, filters, initial, detectBlur, detectChange
)
} else if (el.type === 'radio') {
validation = this._manageRadioValidation(
- field, model, vm, el, scope, filters, detectBlur, detectChange
+ field, model, vm, el, scope, filters, initial, detectBlur, detectChange
)
} else {
validation = this._manageBaseValidation(
- field, model, vm, el, scope, filters, detectBlur, detectChange
+ field, model, vm, el, scope, filters, initial, detectBlur, detectChange
)
}
+ validation.setValidationClasses(this._classes)
+
return validation
}
@@ -149,14 +152,14 @@ export default class Validator {
validations && pull(validations, validation)
}
- validate ({ field = null, touched = false, noopable = false, cb = null } = {}) {
+ validate ({ el = null, field = null, touched = false, noopable = false, cb = null } = {}) {
if (!field) { // all
each(this.validations, (validation, key) => {
validation.willUpdateFlags(touched)
})
this._validates(cb)
} else { // each field
- this._validate(field, touched, noopable, cb)
+ this._validate(field, touched, noopable, el, cb)
}
}
@@ -214,7 +217,7 @@ export default class Validator {
}
- _validate (field, touched = false, noopable = false, cb = null) {
+ _validate (field, touched = false, noopable = false, el = null, cb = null) {
const scope = this._scope
const validation = this._getValidationFrom(field)
@@ -224,7 +227,7 @@ export default class Validator {
util.Vue.set(scope, field, results)
this._fireEvents()
cb && cb()
- }, noopable)
+ }, noopable, el)
}
}
@@ -290,11 +293,11 @@ export default class Validator {
}
- _manageBaseValidation (field, model, vm, el, scope, filters, detectBlur, detectChange) {
+ _manageBaseValidation (field, model, vm, el, scope, filters, initial, detectBlur, detectChange) {
let validation = this._validations[field] = new BaseValidation(
field, model, vm, el, scope, this, filters, detectBlur, detectChange
)
- validation.manageElement(el)
+ validation.manageElement(el, initial)
return validation
}
@@ -308,7 +311,7 @@ export default class Validator {
}
}
- _manageCheckboxValidation (field, model, vm, el, scope, filters, detectBlur, detectChange) {
+ _manageCheckboxValidation (field, model, vm, el, scope, filters, initial, detectBlur, detectChange) {
let validationSet = this._checkboxValidations[field]
if (!validationSet) {
let validation = new CheckboxValidation(field, model, vm, el, scope, this, filters, detectBlur, detectChange)
@@ -317,7 +320,7 @@ export default class Validator {
}
validationSet.elements++
- validationSet.validation.manageElement(el)
+ validationSet.validation.manageElement(el, initial)
return validationSet.validation
}
@@ -334,7 +337,7 @@ export default class Validator {
}
}
- _manageRadioValidation (field, model, vm, el, scope, filters, detectBlur, detectChange) {
+ _manageRadioValidation (field, model, vm, el, scope, filters, initial, detectBlur, detectChange) {
let validationSet = this._radioValidations[field]
if (!validationSet) {
let validation = new RadioValidation(field, model, vm, el, scope, this, filters, detectBlur, detectChange)
@@ -343,7 +346,7 @@ export default class Validator {
}
validationSet.elements++
- validationSet.validation.manageElement(el)
+ validationSet.validation.manageElement(el, initial)
return validationSet.validation
}
@@ -360,11 +363,11 @@ export default class Validator {
}
}
- _manageSelectValidation (field, model, vm, el, scope, filters, detectBlur, detectChange) {
+ _manageSelectValidation (field, model, vm, el, scope, filters, initial, detectBlur, detectChange) {
let validation = this._validations[field] = new SelectValidation(
field, model, vm, el, scope, this, filters, detectBlur, detectChange
)
- validation.manageElement(el)
+ validation.manageElement(el, initial)
return validation
}
diff --git a/test/specs/classes.js b/test/specs/classes.js
new file mode 100644
index 0000000..511c244
--- /dev/null
+++ b/test/specs/classes.js
@@ -0,0 +1,368 @@
+import assert from 'power-assert'
+import Vue from 'vue'
+import classes from 'component-classes'
+import { each, trigger } from '../../src/util'
+
+
+describe('validation classes', () => {
+ let el, vm
+
+ beforeEach(() => {
+ el = document.createElement('div')
+ })
+
+
+ describe('text', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let field1 = el.getElementsByTagName('input')[0]
+ let cls1 = classes(field1)
+ assert(!cls1.has('valid'))
+ assert(cls1.has('invalid'))
+ assert(!cls1.has('touched'))
+ assert(cls1.has('untouched'))
+ assert(cls1.has('pristine'))
+ assert(!cls1.has('dirty'))
+ assert(!cls1.has('modified'))
+
+ field1.value = 'hello'
+ trigger(field1, 'input')
+ trigger(field1, 'blur')
+ vm.$nextTick(() => {
+ assert(cls1.has('valid'))
+ assert(!cls1.has('invalid'))
+ assert(cls1.has('touched'))
+ assert(!cls1.has('untouched'))
+ assert(!cls1.has('pristine'))
+ assert(cls1.has('dirty'))
+ assert(cls1.has('modified'))
+
+ done()
+ })
+ })
+ })
+
+
+ describe('checkbox', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let checkbox1 = el.getElementsByTagName('input')[0]
+ let checkbox2 = el.getElementsByTagName('input')[1]
+ let checkbox3 = el.getElementsByTagName('input')[2]
+ let cls1 = classes(checkbox1)
+ let cls2 = classes(checkbox2)
+ let cls3 = classes(checkbox3)
+ let checkboxClasses = [cls1, cls2, cls3]
+
+ each(checkboxClasses, (cls, index) => {
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+ })
+
+ checkbox1.checked = true
+ trigger(checkbox1, 'change')
+ trigger(checkbox1, 'blur')
+ vm.$nextTick(() => {
+ each(checkboxClasses, (cls, index) => {
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+ })
+
+ checkbox2.checked = true
+ trigger(checkbox2, 'change')
+ trigger(checkbox2, 'blur')
+ vm.$nextTick(() => {
+ each(checkboxClasses, (cls, index) => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+ })
+
+ done()
+ })
+ })
+ })
+ })
+
+
+ describe('radio', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let radio1 = el.getElementsByTagName('input')[0]
+ let radio2 = el.getElementsByTagName('input')[1]
+ let radio3 = el.getElementsByTagName('input')[2]
+ let radio4 = el.getElementsByTagName('input')[3]
+ let cls1 = classes(radio1)
+ let cls2 = classes(radio2)
+ let cls3 = classes(radio3)
+ let cls4 = classes(radio4)
+ let group1 = [cls1, cls2]
+ let group2 = [cls3, cls4]
+
+ each(group1, (cls, index) => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+ })
+ each(group2, (cls, index) => {
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+ })
+
+ radio1.checked = false
+ radio3.checked = true
+ trigger(radio2, 'change')
+ trigger(radio2, 'blur')
+ trigger(radio3, 'change')
+ trigger(radio3, 'blur')
+ vm.$nextTick(() => {
+ each(group1, (cls, index) => {
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+ })
+ each(group2, (cls, index) => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+ })
+
+ done()
+ })
+ })
+ })
+
+
+ describe('select', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let select = el.getElementsByTagName('select')[0]
+ let option2 = el.getElementsByTagName('option')[1]
+ let option3 = el.getElementsByTagName('option')[2]
+ let cls = classes(select)
+
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+
+ option2.selected = true
+ option3.selected = true
+ trigger(select, 'change')
+ trigger(select, 'blur')
+ vm.$nextTick(() => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+
+ done()
+ })
+ })
+ })
+
+
+ describe('custom classes', () => {
+ describe('validator element directive', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({
+ el: el,
+ data: {
+ classes: {
+ valid: 'valid-ed-custom',
+ invalid: 'invalid-ed-custom',
+ touched: 'touched-ed-custom',
+ untouched: 'untouched-ed-custom',
+ pristine: 'pristine-ed-custom',
+ dirty: 'dirty-ed-custom',
+ modified: 'modified-ed-custom'
+ }
+ }
+ })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let field1 = el.getElementsByTagName('input')[0]
+ let cls1 = classes(field1)
+ assert(!cls1.has('valid-ed-custom'))
+ assert(cls1.has('invalid-ed-custom'))
+ assert(!cls1.has('touched-ed-custom'))
+ assert(cls1.has('untouched-ed-custom'))
+ assert(cls1.has('pristine-ed-custom'))
+ assert(!cls1.has('dirty-ed-custom'))
+ assert(!cls1.has('modified-ed-custom'))
+
+ field1.value = 'hello'
+ trigger(field1, 'input')
+ trigger(field1, 'blur')
+ vm.$nextTick(() => {
+ assert(cls1.has('valid-ed-custom'))
+ assert(!cls1.has('invalid-ed-custom'))
+ assert(cls1.has('touched-ed-custom'))
+ assert(!cls1.has('untouched-ed-custom'))
+ assert(!cls1.has('pristine-ed-custom'))
+ assert(cls1.has('dirty-ed-custom'))
+ assert(cls1.has('modified-ed-custom'))
+
+ done()
+ })
+ })
+ })
+
+ describe('v-validate directive', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({
+ el: el,
+ data: {
+ classes: {
+ valid: 'valid-ed-custom',
+ invalid: 'invalid-ed-custom',
+ touched: 'touched-ed-custom',
+ untouched: 'untouched-ed-custom',
+ pristine: 'pristine-ed-custom',
+ dirty: 'dirty-ed-custom',
+ modified: 'modified-ed-custom'
+ }
+ }
+ })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let field1 = el.getElementsByTagName('input')[0]
+ let cls1 = classes(field1)
+ assert(!cls1.has('valid-custom'))
+ assert(cls1.has('invalid-custom'))
+ assert(!cls1.has('touched-custom'))
+ assert(cls1.has('untouched-custom'))
+ assert(cls1.has('pristine-custom'))
+ assert(!cls1.has('dirty-custom'))
+ assert(!cls1.has('modified-custom'))
+
+ field1.value = 'hello'
+ trigger(field1, 'input')
+ trigger(field1, 'blur')
+ vm.$nextTick(() => {
+ assert(cls1.has('valid-custom'))
+ assert(!cls1.has('invalid-custom'))
+ assert(cls1.has('touched-custom'))
+ assert(!cls1.has('untouched-custom'))
+ assert(!cls1.has('pristine-custom'))
+ assert(cls1.has('dirty-custom'))
+ assert(cls1.has('modified-custom'))
+
+ done()
+ })
+ })
+ })
+ })
+})
diff --git a/test/specs/directives/validate-class.js b/test/specs/directives/validate-class.js
new file mode 100644
index 0000000..b4aafe8
--- /dev/null
+++ b/test/specs/directives/validate-class.js
@@ -0,0 +1,245 @@
+import assert from 'power-assert'
+import Vue from 'vue'
+import classes from 'component-classes'
+import { trigger } from '../../../src/util'
+
+
+describe('validate-class directive', () => {
+ let el, vm
+
+ beforeEach(() => {
+ el = document.createElement('div')
+ })
+
+
+ describe('text', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let field1 = el.getElementsByTagName('input')[0]
+ let div = el.getElementsByTagName('div')[0]
+ let cls1 = classes(div)
+ assert(!cls1.has('valid'))
+ assert(cls1.has('invalid'))
+ assert(!cls1.has('touched'))
+ assert(cls1.has('untouched'))
+ assert(cls1.has('pristine'))
+ assert(!cls1.has('dirty'))
+ assert(!cls1.has('modified'))
+
+ field1.value = 'hello'
+ trigger(field1, 'input')
+ trigger(field1, 'blur')
+ vm.$nextTick(() => {
+ assert(cls1.has('valid'))
+ assert(!cls1.has('invalid'))
+ assert(cls1.has('touched'))
+ assert(!cls1.has('untouched'))
+ assert(!cls1.has('pristine'))
+ assert(cls1.has('dirty'))
+ assert(cls1.has('modified'))
+
+ done()
+ })
+ })
+ })
+
+
+ describe('checkbox', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let checkbox1 = el.getElementsByTagName('input')[0]
+ let checkbox2 = el.getElementsByTagName('input')[1]
+ let div = el.getElementsByTagName('div')[0]
+ let cls = classes(div)
+
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+
+ checkbox1.checked = true
+ trigger(checkbox1, 'change')
+ trigger(checkbox1, 'blur')
+ vm.$nextTick(() => {
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+
+ checkbox2.checked = true
+ trigger(checkbox2, 'change')
+ trigger(checkbox2, 'blur')
+ vm.$nextTick(() => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+
+ done()
+ })
+ })
+ })
+ })
+
+
+ describe('radio', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let radio1 = el.getElementsByTagName('input')[0]
+ let radio2 = el.getElementsByTagName('input')[1]
+ let radio3 = el.getElementsByTagName('input')[2]
+ let fieldset1 = el.getElementsByTagName('fieldset')[0]
+ let fieldset2 = el.getElementsByTagName('fieldset')[1]
+ let cls1 = classes(fieldset1)
+ let cls2 = classes(fieldset2)
+
+ assert(cls1.has('valid'))
+ assert(!cls1.has('invalid'))
+ assert(!cls1.has('touched'))
+ assert(cls1.has('untouched'))
+ assert(cls1.has('pristine'))
+ assert(!cls1.has('dirty'))
+ assert(!cls1.has('modified'))
+
+ assert(!cls2.has('valid'))
+ assert(cls2.has('invalid'))
+ assert(!cls2.has('touched'))
+ assert(cls2.has('untouched'))
+ assert(cls2.has('pristine'))
+ assert(!cls2.has('dirty'))
+ assert(!cls2.has('modified'))
+
+ radio1.checked = false
+ radio3.checked = true
+ trigger(radio2, 'change')
+ trigger(radio2, 'blur')
+ trigger(radio3, 'change')
+ trigger(radio3, 'blur')
+ vm.$nextTick(() => {
+ assert(!cls1.has('valid'))
+ assert(cls1.has('invalid'))
+ assert(cls1.has('touched'))
+ assert(!cls1.has('untouched'))
+ assert(!cls1.has('pristine'))
+ assert(cls1.has('dirty'))
+ assert(cls1.has('modified'))
+
+ assert(cls2.has('valid'))
+ assert(!cls2.has('invalid'))
+ assert(cls2.has('touched'))
+ assert(!cls2.has('untouched'))
+ assert(!cls2.has('pristine'))
+ assert(cls2.has('dirty'))
+ assert(cls2.has('modified'))
+
+ done()
+ })
+ })
+ })
+
+
+ describe('select', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be added', (done) => {
+ let select = el.getElementsByTagName('select')[0]
+ let option2 = el.getElementsByTagName('option')[1]
+ let option3 = el.getElementsByTagName('option')[2]
+ let div = el.getElementsByTagName('div')[0]
+ let cls = classes(div)
+
+ assert(!cls.has('valid'))
+ assert(cls.has('invalid'))
+ assert(!cls.has('touched'))
+ assert(cls.has('untouched'))
+ assert(cls.has('pristine'))
+ assert(!cls.has('dirty'))
+ assert(!cls.has('modified'))
+
+ option2.selected = true
+ option3.selected = true
+ trigger(select, 'change')
+ trigger(select, 'blur')
+ vm.$nextTick(() => {
+ assert(cls.has('valid'))
+ assert(!cls.has('invalid'))
+ assert(cls.has('touched'))
+ assert(!cls.has('untouched'))
+ assert(!cls.has('pristine'))
+ assert(cls.has('dirty'))
+ assert(cls.has('modified'))
+
+ done()
+ })
+ })
+ })
+})
diff --git a/test/specs/index.js b/test/specs/index.js
index 4cae40a..634377e 100644
--- a/test/specs/index.js
+++ b/test/specs/index.js
@@ -33,4 +33,6 @@ require('./reset')
require('./validate')
require('./initial')
require('./async')
+require('./classes')
+require('./directives/validate-class')
require('./issues')
diff --git a/test/specs/issues.js b/test/specs/issues.js
index a48e659..712aabf 100644
--- a/test/specs/issues.js
+++ b/test/specs/issues.js
@@ -30,4 +30,96 @@ describe('github issues', () => {
done()
})
})
+
+ describe('#208', () => {
+ describe('radio', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should not validated', (done) => {
+ assert(vm.$validator1.fruits.valid === true)
+ assert(vm.$validator1.fruits.invalid === false)
+ assert(vm.$validator1.fruits.required === false)
+ assert(vm.$validator1.valid === true)
+ assert(vm.$validator1.invalid === false)
+ done()
+ })
+ })
+
+ describe('checkbox', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({
+ el: el
+ })
+ vm.$nextTick(done)
+ })
+
+ it('should be validated', (done) => {
+ 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 === false)
+
+ done()
+ })
+ })
+
+ describe('select', () => {
+ beforeEach((done) => {
+ el.innerHTML = '
'
+ + ''
+ + ''
+ vm = new Vue({ el: el })
+ vm.$nextTick(done)
+ })
+
+ it('should be validated', (done) => {
+ assert(vm.$validator1.lang.required === false)
+ assert(vm.$validator1.lang.valid === true)
+ assert(vm.$validator1.lang.touched === false)
+ assert(vm.$validator1.lang.dirty === false)
+ assert(vm.$validator1.lang.modified === false)
+ assert(vm.$validator1.valid === true)
+ assert(vm.$validator1.touched === false)
+ assert(vm.$validator1.dirty === false)
+ assert(vm.$validator1.modified === false)
+
+ done()
+ })
+ })
+ })
})
diff --git a/test/specs/validate.js b/test/specs/validate.js
index adca355..5b6efb9 100644
--- a/test/specs/validate.js
+++ b/test/specs/validate.js
@@ -63,7 +63,7 @@ describe('$validate', () => {
assert(vm.$validator1.dirty === true)
assert(vm.$validator1.modified === true)
assert(vm.$validator1.touched === false)
-
+
done()
})
})
@@ -254,6 +254,22 @@ describe('$validate', () => {
+ '
'
+ ''
vm = new Vue({ el: el })
@@ -353,5 +369,44 @@ describe('$validate', () => {
})
})
})
+
+ describe('touched for all validatable elements', () => {
+ it('should be validated', (done) => {
+ assert(vm.$validator1.field1.touched === false)
+ assert(vm.$validator1.field2.touched === false)
+ assert(vm.$validator1.checkbox.touched === false)
+ assert(vm.$validator1.radio.touched === false)
+ assert(vm.$validator1.select.touched === false)
+ assert(vm.$validator1.touched === false)
+
+ vm.$nextTick(() => {
+ vm.$validate('field2')
+ vm.$validate('checkbox')
+ vm.$validate('radio')
+ vm.$validate('select')
+
+ assert(vm.$validator1.field1.touched === false)
+ assert(vm.$validator1.field2.touched === false)
+ assert(vm.$validator1.checkbox.touched === false)
+ assert(vm.$validator1.radio.touched === false)
+ assert(vm.$validator1.select.touched === false)
+ assert(vm.$validator1.touched === false)
+
+ vm.$validate('field2', true)
+ vm.$validate('checkbox', true)
+ vm.$validate('radio', true)
+ vm.$validate('select', true)
+
+ assert(vm.$validator1.field1.touched === false)
+ assert(vm.$validator1.field2.touched === true)
+ assert(vm.$validator1.checkbox.touched === true)
+ assert(vm.$validator1.radio.touched === true)
+ assert(vm.$validator1.select.touched === true)
+ assert(vm.$validator1.touched === true)
+
+ done()
+ })
+ })
+ })
})
})