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

Commit

Permalink
Merge pull request #4 from sebastian-software/1-vuex-getter-style
Browse files Browse the repository at this point in the history
Vuex like getter style
  • Loading branch information
fastner committed May 12, 2016
2 parents be2e8bd + 5a5fefc commit 57fb2e7
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 60 deletions.
38 changes: 26 additions & 12 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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$<property in camelCase>
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`
````
"<property name>"
"<module name>.<property name>"
````

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:

````
[{
Expand Down
2 changes: 1 addition & 1 deletion src/BaseValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class BaseValidator {

propertiesMap[prop].push({
name,
validatorFunction
validatorFunction: validatorFunction.bind(validatorAssertions)
})
})
}
Expand Down
93 changes: 46 additions & 47 deletions src/VuexValidator.js
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 57fb2e7

Please sign in to comment.