diff --git a/readme.md b/readme.md index 2b0b421..082d7ff 100644 --- a/readme.md +++ b/readme.md @@ -127,26 +127,40 @@ connection middleware before sending data. ### Validation getter in components -All validations are injected into Vue components itself. The properties are named with a special key: +Validation getters are added to a component's vuex object with key `validators`. This bears +analogy to Vuex getters itself mapping global state to component's local state. ```` -$invalid$ +export default Vue.extend({ + ... + + vuex: { + getters: { + test: (state) => state.test, + test2: (state) => state.test2 + }, + validators: { + testInvalid: (validator) => validator.isInvalid("test"), + test2Invalid: (validator) => validator.isInvalid("test2") + } + } +}); ```` -So you can access validators for both properties used in the example above via: - -- `$invalid$test` -- `$invalid$test2` - -If you are using Vuex state modules it could be something like +isInvalid takes a property path as string. This is either the property name itself or module name and property name seperated via dot. -- `$invalid$userLastname` +```` +"" +"." +```` -for property `state.user.lastname` +All validator functions are mapped to the component's local computed getters. So it is possible to access validation properties in template: -This validation getter can also be used inside of templates and other computed properties. +```` +My property {{test}} is invalid: {{testInvalid}} +```` -As return value either a falsy value (`undefined`, `null` or `false`) is returend in case of this property validated through all rules. Otherwise an array of failing rules return values are returned. the return structure can be something like: +A falsy value (`undefined`, `null` or `false`) is returend in case of the property validated through all rules. Otherwise an array of failing rules return values are returned. the return structure can be something like: ```` [{ diff --git a/src/BaseValidator.js b/src/BaseValidator.js index 81e26c2..a9b14fb 100644 --- a/src/BaseValidator.js +++ b/src/BaseValidator.js @@ -99,7 +99,7 @@ export default class BaseValidator { propertiesMap[prop].push({ name, - validatorFunction + validatorFunction: validatorFunction.bind(validatorAssertions) }) }) } diff --git a/src/VuexValidator.js b/src/VuexValidator.js index b065641..eb4f25c 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -1,7 +1,8 @@ -import { reduce, camelCase, isArray } from "lodash" +import { reduce } from "lodash" const validators = [] const validatorsMap = {} +const propertyToValidator = {} class GlobalValidator { isValid(module = null) @@ -30,44 +31,39 @@ class GlobalValidator { const validator = new GlobalValidator() -function computedValidation(context, id, rulesLength) +function propertyValidator(state) { - return function() - { - let allResults = null - for (let index = 0; index < rulesLength; index++) + return { + isInvalid: (property) => { - const curResult = context[`${id}${index}`] - - if (curResult && curResult.valid === false) - if (isArray(allResults)) - allResults = allResults.concat(curResult) - else - allResults = [ curResult ] - } + const vals = propertyToValidator[property] + if (!vals) + { + const moduleValidator = validatorsMap[property] + if (moduleValidator) + { + const isValid = moduleValidator.isValid() + return isValid === true ? null : isValid + } - return allResults - } -} + return null + } -function callValidatorFunction(context, validatorFunction, state) -{ - return function() - { - return validatorFunction.call(context, state) - } -} + return reduce(vals.map((val) => val.validatorFunction(state)), (all, self) => + { + if (!self) + return all -function computedModuleValidation(context, module) -{ - return function() - { - let isValid = validator.isValid(module) + // It is possible, that a validation fails without being this property as reason + if (self.fields.indexOf(property) < 0) + return all - if (isValid === true) - return null + if (all) + return all.concat(self) - return isValid + return [ self ] + }, null) + } } } @@ -99,31 +95,34 @@ function install(Vue, { validators: _validators } = { validators: [] }) const options = this.$options const getters = options.computed = options.computed || {} const state = this.$store.state - const self = this + const statedPropertyValidator = propertyValidator(state) validators.forEach((item) => { item.getProperties().forEach((prop) => { - const id = `\$invalid\$${camelCase(prop)}` - const rules = item.getRulesByProperty(prop) - const rulesLength = rules.length - const ruleContext = item.getRuleContext() + if (!propertyToValidator[prop]) + propertyToValidator[prop] = [] - if (rulesLength > 0) - { - // TODO: Cache generated getters like Vuex do - rules.forEach((rule, index) => + const rules = item.getRulesByProperty(prop) + if (rules) + rules.forEach((rule) => { - getters[`${id}${index}`] = callValidatorFunction(ruleContext, rule.validatorFunction, state) + if (propertyToValidator[prop].indexOf(rule) < 0) + propertyToValidator[prop].push(rule) }) - getters[id] = computedValidation(self, id, rulesLength) - } }) - - if (item.module) - getters[`\$invalid\$module\$${item.module}`] = computedModuleValidation(self, item.module) }) + + if (options && options.vuex && options.vuex.validators) + { + const vals = options.vuex.validators + Object.keys(vals).forEach((prop) => + { + const currentPropertyFnt = vals[prop] + getters[prop] = () => currentPropertyFnt(statedPropertyValidator) + }) + } } const _init = Vue.prototype._init