From 1856d9bb666fd89b8a6e48f5a60ec09e36fc1c63 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 21:11:56 +0200 Subject: [PATCH 01/12] Add propertyValidator simmilar to vuex getters --- src/VuexValidator.js | 52 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index b065641..43bc86b 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -1,7 +1,8 @@ -import { reduce, camelCase, isArray } from "lodash" +import { reduce, camelCase, isArray, curry } from "lodash" const validators = [] const validatorsMap = {} +const propertyToValidator = {} class GlobalValidator { isValid(module = null) @@ -30,6 +31,30 @@ class GlobalValidator { const validator = new GlobalValidator() +const propertyValidator = { + isInvalid: (property) => + { + const vals = propertyToValidator[property] + if (!vals) + return null + + return reduce(vals.map((val) => val.isValid()), (all, self) => + { + console.log("TEST >>>>>", all, self) + if (all === true && self === true) + return true + + if (all !== true && self === true) + return all + + if (all === true && self !== true) + return self + + return all.concat(self) + }) + } +} + function computedValidation(context, id, rulesLength) { return function() @@ -101,6 +126,29 @@ function install(Vue, { validators: _validators } = { validators: [] }) const state = this.$store.state const self = this + validators.forEach((item) => + { + item.getProperties().forEach((prop) => + { + if (!propertyToValidator[prop]) + propertyToValidator[prop] = [] + + if (propertyToValidator[prop].indexOf(item) < 0) + propertyToValidator[prop].push(item) + }) + }) + + if (options && options.vuex && options.vuex.validators) + { + const vals = options.vuex.validators + Object.keys(vals).forEach((prop) => + { + const curriedFnt = curry(vals[prop], 1)(propertyValidator) + getters[prop] = curriedFnt + }) + } + + /* validators.forEach((item) => { item.getProperties().forEach((prop) => @@ -123,7 +171,7 @@ function install(Vue, { validators: _validators } = { validators: [] }) if (item.module) getters[`\$invalid\$module\$${item.module}`] = computedModuleValidation(self, item.module) - }) + }) */ } const _init = Vue.prototype._init From e711658b1dd99ad74f73f614c39266b1a472129e Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 21:27:46 +0200 Subject: [PATCH 02/12] Bind assertions to validator function --- src/BaseValidator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) }) }) } From 8338c1c767962b72372158797a4ac63b6a14e48c Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 21:27:58 +0200 Subject: [PATCH 03/12] Flatten rules array --- src/VuexValidator.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 43bc86b..359fde4 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -133,8 +133,13 @@ function install(Vue, { validators: _validators } = { validators: [] }) if (!propertyToValidator[prop]) propertyToValidator[prop] = [] - if (propertyToValidator[prop].indexOf(item) < 0) - propertyToValidator[prop].push(item) + const rules = item.getRulesByProperty(prop) + if (rules) + rules.forEach((rule) => + { + if (propertyToValidator[prop].indexOf(rule) < 0) + propertyToValidator[prop].push(rule) + }) }) }) From 6914b3487d1d354887026aea149c29140d3b1ca2 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 21:38:17 +0200 Subject: [PATCH 04/12] Bind state to propertyValidator closure --- src/VuexValidator.js | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 359fde4..a2bd659 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -31,27 +31,29 @@ class GlobalValidator { const validator = new GlobalValidator() -const propertyValidator = { - isInvalid: (property) => - { - const vals = propertyToValidator[property] - if (!vals) - return null - - return reduce(vals.map((val) => val.isValid()), (all, self) => +function propertyValidator(state) +{ + return { + isInvalid: (property) => { - console.log("TEST >>>>>", all, self) - if (all === true && self === true) - return true + const vals = propertyToValidator[property] + if (!vals) + return null + + return reduce(vals.map((val) => val.validatorFunction(state)), (all, self) => + { + if (all === true && self === true) + return true - if (all !== true && self === true) - return all + if (all !== true && self === true) + return all - if (all === true && self !== true) - return self + if (all === true && self !== true) + return self - return all.concat(self) - }) + return all.concat(self) + }) + } } } From 4bb4fb07aadcd4069091a836406e6f5f40832b4d Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 21:40:53 +0200 Subject: [PATCH 05/12] Bind state --- src/VuexValidator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index a2bd659..784532a 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -150,7 +150,7 @@ function install(Vue, { validators: _validators } = { validators: [] }) const vals = options.vuex.validators Object.keys(vals).forEach((prop) => { - const curriedFnt = curry(vals[prop], 1)(propertyValidator) + const curriedFnt = curry(vals[prop], 1)(propertyValidator(state)) getters[prop] = curriedFnt }) } From 4b88d4683adad234d50981b2e91dd860395fa4ca Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:02:23 +0200 Subject: [PATCH 06/12] Clean up old validator access properties --- src/VuexValidator.js | 69 +------------------------------------------- 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 784532a..0472179 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -1,4 +1,4 @@ -import { reduce, camelCase, isArray, curry } from "lodash" +import { reduce } from "lodash" const validators = [] const validatorsMap = {} @@ -57,47 +57,6 @@ function propertyValidator(state) } } -function computedValidation(context, id, rulesLength) -{ - return function() - { - let allResults = null - for (let index = 0; index < rulesLength; index++) - { - const curResult = context[`${id}${index}`] - - if (curResult && curResult.valid === false) - if (isArray(allResults)) - allResults = allResults.concat(curResult) - else - allResults = [ curResult ] - } - - return allResults - } -} - -function callValidatorFunction(context, validatorFunction, state) -{ - return function() - { - return validatorFunction.call(context, state) - } -} - -function computedModuleValidation(context, module) -{ - return function() - { - let isValid = validator.isValid(module) - - if (isValid === true) - return null - - return isValid - } -} - function install(Vue, { validators: _validators } = { validators: [] }) { /* eslint no-invalid-this: 0, no-console:0 */ @@ -126,7 +85,6 @@ function install(Vue, { validators: _validators } = { validators: [] }) const options = this.$options const getters = options.computed = options.computed || {} const state = this.$store.state - const self = this validators.forEach((item) => { @@ -154,31 +112,6 @@ function install(Vue, { validators: _validators } = { validators: [] }) getters[prop] = curriedFnt }) } - - /* - 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 (rulesLength > 0) - { - // TODO: Cache generated getters like Vuex do - rules.forEach((rule, index) => - { - getters[`${id}${index}`] = callValidatorFunction(ruleContext, rule.validatorFunction, state) - }) - getters[id] = computedValidation(self, id, rulesLength) - } - }) - - if (item.module) - getters[`\$invalid\$module\$${item.module}`] = computedModuleValidation(self, item.module) - }) */ } const _init = Vue.prototype._init From ddb7ca16a71be971e04583b03389d222b1af72c2 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:02:53 +0200 Subject: [PATCH 07/12] Remove curried fnt and replace with closure --- src/VuexValidator.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 0472179..ad4a083 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -85,6 +85,7 @@ function install(Vue, { validators: _validators } = { validators: [] }) const options = this.$options const getters = options.computed = options.computed || {} const state = this.$store.state + const statedPropertyValidator = propertyValidator(state) validators.forEach((item) => { @@ -108,8 +109,8 @@ function install(Vue, { validators: _validators } = { validators: [] }) const vals = options.vuex.validators Object.keys(vals).forEach((prop) => { - const curriedFnt = curry(vals[prop], 1)(propertyValidator(state)) - getters[prop] = curriedFnt + const currentPropertyFnt = vals[prop] + getters[prop] = () => currentPropertyFnt(statedPropertyValidator) }) } } From 5af0e708f5d2f54fea879618d5f2ae03aa2d68f8 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:03:11 +0200 Subject: [PATCH 08/12] Fix validation error merging --- src/VuexValidator.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index ad4a083..4a7e59e 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -42,16 +42,13 @@ function propertyValidator(state) return reduce(vals.map((val) => val.validatorFunction(state)), (all, self) => { - if (all === true && self === true) - return true - - if (all !== true && self === true) + if (!self) return all - if (all === true && self !== true) - return self + if (all) + return all.concat(self) - return all.concat(self) + return [ self ] }) } } From 60b42b7d305a5a36b123ff116765d0a51a107672 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:14:32 +0200 Subject: [PATCH 09/12] Set start value for reduce to be null --- src/VuexValidator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 4a7e59e..0403526 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -49,7 +49,7 @@ function propertyValidator(state) return all.concat(self) return [ self ] - }) + }, null) } } } From 5ed437e2be7e80530cb0ace5e9c7be5925394d20 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:26:56 +0200 Subject: [PATCH 10/12] Add support for module validation --- src/VuexValidator.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index 0403526..c5274b9 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -38,7 +38,16 @@ function propertyValidator(state) { const vals = propertyToValidator[property] if (!vals) + { + const moduleValidator = validatorsMap[property] + if (moduleValidator) + { + const isValid = moduleValidator.isValid() + return isValid === true ? null : isValid + } + return null + } return reduce(vals.map((val) => val.validatorFunction(state)), (all, self) => { From 98fe1f8ee870d5c3acc230684fe0175082be85d9 Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:36:05 +0200 Subject: [PATCH 11/12] Skip if current property is not the real reason for validation failure --- src/VuexValidator.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/VuexValidator.js b/src/VuexValidator.js index c5274b9..eb4f25c 100644 --- a/src/VuexValidator.js +++ b/src/VuexValidator.js @@ -54,6 +54,10 @@ function propertyValidator(state) if (!self) return all + // It is possible, that a validation fails without being this property as reason + if (self.fields.indexOf(property) < 0) + return all + if (all) return all.concat(self) From 5a5fefce2651387c06d0e2ff440723af8e9a039a Mon Sep 17 00:00:00 2001 From: Sebastian Fastner Date: Thu, 12 May 2016 22:52:06 +0200 Subject: [PATCH 12/12] Add documentation for new validation getter style --- readme.md | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) 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: ```` [{