vuex-module-validatable-state
v0.5.0
Published
Simple Vuex module to validate state for handling form
Downloads
16
Readme
vuex-module-validatable-state
Simple Vuex module to handle form fields and validations.
You can build a view model for your form, which runs valdations easily. You just provide initial fields and validators to build the module, then map getters/actions to components.
Play in this sandbox.
Usage
Installation
$ npm i vuex-module-validatable-state
Register to core Vuex module
This module provides the function to return Vuex module as default. The function takes arguments:
- Initial field set
- Validators
import validatableModule from "vuex-module-validatable-state";
const initialFields = {
amount: null,
description: "default text"
};
const validators = {
amount: [
({ amount }) => amount === null ? "Require this" : false
],
description: [
({ description }) => description.length > 15 ? "Should be shorter than 15" : false,
({ description, amount }) => description.indexOf(amount.toString()) ? "Should include amount" : false,
]
};
new Vuex.Store({
modules: {
myForm: {
namespaced: true
store,
getters,
actions,
mutations,
modules: {
...validatableModule(initialFields, validators) // <-- HERE
}
}
}
});
import { register } from "vuex-module-validatable-state";
const initialFields = {
amount: null,
description: "default text"
};
const validators = {
amount: [
({ amount }) => amount === null ? "Require this" : false
],
description: [
({ description }) => description.length > 15 ? "Should be shorter than 15" : false,
({ description, amount }) => description.indexOf(amount.toString()) ? "Should include amount" : false,
]
};
const store = new Vuex.Store({
modules: {
myForm: {
namespaced: true
store,
getters,
actions,
mutations
}
}
});
register(store, "myForm", initialFields, validators);
Map to Components
Provided Getters
|Getter name|Returns|
---|---
|GetterTypes.ALL_FIELDS_VALID
|boolean
whether all fields don't have error|
|GetterTypes.FIELD_VALUES
|All fields as { [fieldName]: value }
|
|GetterTypes.FIELD_ERRORS
|All errors as { [fieldName]: errorMessage }
|
|GetterTypes.FIELD_EDITABILITIES
|All editable flags as { [fieldName]: editability }
|
|GetterTypes.FIELD_DIRTINESSES
|All dirtiness flags as { [fieldName]: dirtiness }
|
|GetterTypes.ANY_FIELD_CHANGED
|boolean
whether all fields are not dirty|
Provided Actions
Import ActionTypes
from the module.
|Action name|Runs|
---|---
|ActionTypes.SET_FIELD
|Set value for a field, then runs validation if enabled|
|ActionTypes.SET_FIELDS_BULK
|Set values for fields at once, then make all dirtiness flags false|
|ActionTypes.RESET_FIELDS
|Reset values on field with initial values|
|ActionTypes.ENABLE_VALIDATION
|Enable interactive validation for a specific field, and run validations on this field immediately|
|ActionTypes.ENABLE_ALL_VALIDATIONS
|Enable interactive validations for all fields and run all validations immediately|
|ActionTypes.VALIDATE_FIELDS
|Validate for each field that is enabled for interactive validation|
|ActionTypes.SET_FIELDS_EDITABILITY
|Set editability flag for a field, disabled field is not updated nor validated|
|ActionTypes.SET_FIELDS_PRISTINE
|Make all dirtiness flags false|
Validators
You can pass validators when you initialize the module.
const validators = {
amount: [/* validators for filling error against to amount */],
description: [/* validators for filling error against to description */]
}
Each validator can take all fields values to run validation:
const validators = {
amount: [
({ amount, description }) => /* return false or errorMessage */
]
}
Optionally, can take getters on the store which calls this module:
const validators = {
description: [
({ description }, getters) => getters.getterOnStore && validationLogicIfGetterOnStoreIsTruthy(description)
]
}
And you can request "interactive validation" which valites every time dispatch(ActionTypes.SET_FIELD)
is called
const validators = {
amount: [
[({ amount }, getters) => /* validator logic */, { instant: true }]
]
}
Provided Typings
You can import handy type/interface definitions from the module.
The generic T
in below expects fields type like:
interface FieldValues {
amount: number;
description: string;
}
getters[GetterTypes.FIELD_VALUES]
returns values with following FieldValues
interface.
ValidatorTree<T>
As like ActionTree, MutationTree, you can receive type guards for Validators. By giving your fields' type for Generics, validator can get more guards for each fields:
SetFieldAction<T>
It's the type definition of the payload for dispatching ActionTypes.SET_FIELD
, you can get type guard for your fields by giving Generics.
FieldValidationErrors<T>
Type for getters[GetterTypes.FIELD_ERRORS]
FieldEditabilities<T>
Type for getters[GetterTypes.FIELD_EDITABILITIES]
FieldDirtinesses<T>
Type for getters[GetterTypes.FIELD_DIRTINESSES]
Working Sample
Registering to Vuex Store
const initialField = {
amount: 0,
description: null
};
const validators = {
amount: [
({ amount }) => (!amount ? "Amount is required" : false),
({ amount }) => (amount <= 0 ? "Amount should be greater than 0" : false)
],
description: [
({ amount, description }) =>
amount > 1000 && !description
? "Description is required if amount is high"
: false
]
};
const store = new Vuex.Store({
modules: {
...theModule(initialField, validators)
}
});
Mapping to Component
<template>
<form>
<div>
<label for="amount">Amount (Required, Positive)</label>
<input type="number" name="amount" v-model="amount">
<span v-if="errors.amount">{{ errors.amount }}</span>
</div>
<div>
<label for="description">Description (Required if amount is greater than 1000)</label>
<textarea name="description" v-model="description"/>
<span v-if="errors.description">{{ errors.description }}</span>
</div>
<button @click.prevent="submit">Validate and Submit</button>
</form>
</template>
<script>
import { GetterTypes, ActionTypes } from "vuex-module-validatable-state";
export default {
name: "App",
computed: {
amount: {
get() {
return this.$store.getters[GetterTypes.FIELD_VALUES].amount;
},
set(value) {
this.$store.dispatch(ActionTypes.SET_FIELD_VALUE, {
name: "amount",
value
});
}
},
description: {
get() {
return this.$store.getters[GetterTypes.FIELD_VALUES].description;
},
set(value) {
this.$store.dispatch(ActionTypes.SET_FIELD_VALUE, {
name: "description",
value
});
}
},
errors() {
return this.$store.getters[GetterTypes.FIELD_ERRORS];
}
},
methods: {
submit() {
this.$store.dispatch(ActionTypes.ENABLE_ALL_VALIDATIONS).then(() => {
if (this.$store.getters[GetterTypes.ALL_FIELDS_VALID]) {
alert("Form is valid, so now submitting!");
this.$store.dispatch(ActionTypes.SET_FIELDS_PRISTINE);
}
});
}
}
};
</script>