vue-smart-form
Plugin provides two mixins:
mixSmartForm
with basic form featuresmixFormSubmitter
with some common submit-related logic
mixSmartForm
mixin is based on Vuelidate package and doesn't work without it, so vuelidate
should be installed and registered in application via Vue.use()
Table of contents
Installation
npm install --save vue-smart-form
Import and register plugin
import Vue from 'vue'
import VueSmartForm from 'vue-smart-form'
Vue.use(VueSmartForm, {
serverErrorsFormatter: function (response) {
// custom logic to fit API errors with expected format
}
})
Import specific mixins in your components:
Form mixin:
import { mixSmartForm } from 'vue-smart-form'
Form submitter mixin:
import { mixFormSubmitter } from 'vue-smart-form'
mixSmartForm
Props
name | description |
---|---|
uid | Unique form identifier. Can be used for repeateable forms rendered via v-for to handle $vstate updates in parent. |
value | Object with initial values, the part of v-model binding. Useful only if we need to populate form with any initial data from parent component. |
state | The readonly form state object aggregates fields values and validation states. This prop is in sync with a parent via .sync modifier. It's strongly recommended to use this prop in one-way binding manner just to obtain state updates in parent component. |
sending | Should be passed from parent and used inside a form e.g. to show loader or block UI |
serverResponse | Response from server if code is 400. Should be passed from parent. Triggers displaying error messages from server-side to user. |
disableSuccessFields | Allows to disable success state for all fields or for array of fields |
touchDelay | Delay in ms between touch() method called and validation really triggered (Vuelidate's $touch() called) |
Data properties
name | description |
---|---|
fields | Object. All form fields which should be validated and added to form data in submit payload, should be placed in this property. See example for more details |
vmessages | All validation messages should be described here |
subforms | Subforms states. State of each particular subform should be exeplicitly binded with child subform via state.sync |
Computed properties
name | description |
---|---|
$vstate | Aggregated validation state with merged client & server side validation errors. See structure under the table |
$vf | Just a shorthand for $vstate.fields . So you can use $vf.email.msg instead of $vstate.fields.email.msg |
isFormComplete | Returns true if all fields are dirty and valid - all required fields are filled with valid values and each field has complete: true |
areSubformsComplete | Returns true if all subforms are complete |
subformsHasErrors | Returns true if at least one subform has an error |
areSubformsDirty | Returns true if all subforms are dirty, and false if at least one - not |
$vstate
structure
{
dirty: Boolean, // true if at least one field is dirty (was touched)
error: Boolean, // true if at least one field has an error (subforms errors are not included)
complete: Boolean, // true if all fields are complete (was filled and successfully validated at the client-side)
client: Boolean, // true if there is at least one client-side validation error
server: Boolean, // true if there is at least one server-side validation error
fields: { // object contains validation details by each field
somefield: {
dirty: Boolean, // true if the field was touched
complete: Boolean, // true if the field was touched and successfully validated
error: Boolean, // true if there is validation error (client-side or server-side whatever)
msg: String, // client-side validation message from `vmessages` or raw message received from server-side
source: String, // indicates error source, can be 'client' or 'server'
type: String // 'is-success', 'is-danger' - can be used to setup appropriate class to a field wrapper. Initially it was hardcoded to use with Buefy's `<b-field>` component.
}
},
subforms: { // aggregated info about results of subforms validation
error: Boolean, // true if at least one subform has an error
complete: Boolean // true if all subforms are complete
}
}
Methods
name | description |
---|---|
formDataCompose() | Returns compacted (without empty values) fields object. Can be overridden in components to define a custom logic of composing data for submit event payload |
formatServerErrors(response) | Can be overridden in components to define a custom logic to fit server response with expected format |
onInput(fieldName) | Input event handler. Usage example <input v-model="fields.lastname" @input="onInput('lastname') />" . This handler performs this.reset(fieldname) call and then consecutive calls of this.vModelSync() and this.stateSync() . If you wanna just reset validation errors and avoid syncing with parent on input, you can use reset method as event handler, e.g. @input="reset('lastname') |
onBlur(fieldName) | Can be used to trigger field validation on blur. It's just a delayed call of this.touch(fieldname) |
reset(fieldname) | Reset validation state. Will reset a state of particular field if a name of a field is passed and will reset all form (states for all fields) if called without arguments or passed field doesn't exist in $data.fields |
touch(fieldname) | Run validation. Will validate a particular field if a name of a field is passed and will validate all fields in a form if called without arguments or passed field doesn't exist in $data.fields |
submit() | Triggers form validation and emits corresponding event with form data in the payload |
submitResult() | |
autofocusCall() |
Events
name | payload | description |
---|---|---|
input | Result of this.formDataCompose() |
Just a part of v-model binding, fires only in one case: right after onInput(fieldName) call |
vstateUpdated | { uid: 'some-uid', vstate: {...} } |
Fires on: - created hook- $vstate changed.Payload is an object with unique form identifier in uid property and with actual validation state object in vstate property. |
update:state | { uid: 'some-id', fields: {...}, vstate: {...} subforms: {...} } |
Fires on: - created hook- onInput call- $vstate changed- subforms property in data changed- beforeDestroy hook. |
submit | Result of this.formDataCompose() |
Fires after submit() call but only if validation passed |
Server-side errors handling option
Use the serverErrorsFormatter
plugin option to pass a custom function to fit server response with format expected by mixin.
Expected format is:
{
fieldName: [
'some error message',
'another error message'
],
anotherFieldName: [
// ...
],
//...
}
☝️ Although each field can have an array of error messages, for now, in$vf.fieldName.msg
mixin uses only the first one
So if your back-end send you e.g. the following:
[
{
property_path: 'fieldName',
message: 'Some shit happened'
}
]
you can just define serverErrorsFormatter
function, e.g.:
Vue.use(VueSmartForm, {
serverErrorsFormatter: function (response) {
return response.data.reduce((res, item) => {
if (!_.get(res, item.property_path)) res[item.property_path] = []
res[item.property_path].push(item.message)
return res
}, {})
}
})
Now you can access error message from your back-end via the same way as for client-side validation messages: $vf.fieldName.msg
mixFormSubmitter
Component option
In your component you must define the separate root option forms
with array of forms ids.
E.g.
components: {
// ...
},
forms: ['loginForm', 'registrationForm'],
props: {
// ...
}
Data properties
name | structure | description |
---|---|---|
submitter | { form1: { sending: Boolean, errorResponse: null } } |
Stores submitter data (sending and errorResponse values)grouped by form id. errorResponse can be Array , Object , String or NULL it depends on your back-end. |
Computed properties
name | description |
---|---|
$sd | Submitter data. Just returns this.submitter |
Methods
name | description |
---|---|
submitStart(formId) | Sets this.submitter[formId].errorResponse to NULL and this.submitter[formId].sending to true . You should call this method right before making request to your back-end. |
submitOk(formId) | It just sets this.submitter[formId].sending to false . You should use this method in case of success response with code 200 or 2xx . |
submitFailed(formId, error) | Sets an error response into this.submitter[formId].errorResponse and sets this.submitter[formId].sending to false . You should use this method in case of error with code 400 . |
Example
You can play around with that here
Features
Form mixin features list:
- sync with parent via
v-model
- sync fields values and validation state with parent via
state
prop withsync
modifier - reactive validation state
- merging of client-side and server-side validation errors to provide the easy way to display validation errors messages of both types in one place
- subforms validation with reactive validation state tree (also syncable via
state.sync
) - support of multiple forms in single parent component
sending
state handling (can be used to block UI while request is performing)- double submit protection
- server response handling
- ability to set a delay between
$touch
called and validation really triggered to prevent flashing of error messages in case of conditional forms switching - is form complete detection (all rquired fields are filled with valid values)
- prefilling form with initial data
- ability to define custom function
serverErrorsFormatter
to fit error response from your back-end with format expected by mixin - autofocusing desired field when form mounted
- perfectly fits with Buefy framework components
<b-field>
and<b-input>
- customizing validation error messages with gracefull degradation from field-specific messages to common messages for specific validator
Submitter mixin features list:
- controlling
sending
state passed into child component with form - storing server response to pass into form
submitStart
,submitOk
,submitFailed
methods to integrate with API calls- support of multiple forms in single parent component