v-generic-form
v1.1.13
Published
- ~~Allow user to choose schema layout via slot~~ - ~~Optional passing of form data when not using slots~~ - Create Typescript validation function to provide better intellisense when defining schema - Individual passing of specific individual fields, e
Downloads
17
Readme
v-Generic-Form
TODO:
- ~~Allow user to choose schema layout via slot~~
- ~~Optional passing of form data when not using slots~~
- Create Typescript validation function to provide better intellisense when defining schema
- Individual passing of specific individual fields, e.g.
- Add rules directive to children of generic form ( nice to have honestly )
- Typescript rewrite overall
- Allow global custom errors
Quickstart
Vue 2
yarn add v-generic-form-vue2
OR
npm i v-generic-form-vue2
import Vue from 'vue'
import vGenericForm from 'v-generic-form-vue2'
Vue.use(VGenericForm, {
...
})
Vue 3
yarn add v-generic-form
OR
npm i v-generic-form
import { createApp } from 'vue'
import vGenericForm from 'v-generic-form'
const el = document.getElementById('app');
const app = createApp();
app.use(vGenericForm, {
...
}).mount(el)
Basic usage
<template>
<div>
<v-generic-form :fields="fields" @submit="onSubmit" :onDataChange="onDataChange" />
</div>
</template>
<script>
export default {
name: 'Form',
computed: {
fields() {
return [
{
name: 'input',
placeholder: 'input',
label: 'input label',
type: 'text',
}
]
}
},
methods: {
onSubmit(data) {
console.log(data)
},
onDataChange(data) {
console.log(data)
}
}
};
</script>
Adding validation
vGenericForm uses validate.js behind the scenes for validation so refer to it for adding custom rules or finding all available validators.
Validation is as simple as passing a rules
object to the field in your schema.
[
{
name: "input",
defaultValue: 'some supported value',
placeholder: "input",
label: "input label",
type: "text",
rules: {
required: true,
exclusion: {
within: ['Not Supported Value 1', 'Not Supported Value 2'],
message: "^We don't support %{value} right now, sorry",
},
},
},
];
Adding a custom validator
<script>
import validate from 'validate.js'
validate.validators.isTest = function (value, options, key, attributes) {
console.log(value);
console.log(`input`, attributes.atest) // same results as previous line
console.log(options);
console.log(key);
console.log(attributes);
const { message } = options
if (value !== "test") {
return message || 'must equal test'
}
// return null if success
return null
};
export default {
computed: {
fields() {
return [
{
name: "atest",
placeholder: "input",
rules: {
isTest: {
message: "This value does not equal test!",
},
},
// disable showing input name in validation message
fullMessages: false
},
];
},
},
};
</script>
Using different variants
By default, vGenericForm comes with 2 inputs. Input, Select, RadioButton(coming soon), and Textarea. It will default to Input but to use a different variant, simply pass a variant as a string to the desired input you'd like to modify.
[{
name: 'input',
placeholder: 'input',
label: 'input label',
type: 'text',
variant: 'Select',
values: ['option1', 'option2']
}]
Using a external component with the form
<script>
import Custom from '@/components/custom'
export default {
computed: {
fields() {
return [
{
name: "input",
placeholder: "input",
label: "input label",
type: "text",
component: Custom,
rules: {
required: true, // validation still works perfectly on these
},
customData: {
customPropForCustomComponent: "123",
},
},
];
},
},
};
</script>
Custom.vue
<template>
<div>
<button @click="sendUpdatedValueToForm()">{{ value }}</button>
{{ customData.customPropForCustomComponent }}
</div>
</template>
<script>
export default {
// default props available from generic form
props: {
value: {
type: String,
default: '',
},
inputType: {
type: String,
default: 'text',
},
name: {
type: String,
default: '',
},
values: {
type Array,
default: () => []
},
label: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
errors: {
type: [Array, String, Object],
default: null,
},
customData: {
type: Object,
default: null,
},
getAllFields: {
type: Array,
default: () => {}
},
},
data() {
return {
componentValue: "first value",
};
},
methods: {
sendUpdatedValueToForm() {
this.componentValue = Math.random();
$emit("setValue", {
name,
value: componentValue,
});
},
},
};
</script>
Defining default components
import { createApp } from 'vue'
import MyInput from '@/components/myInput'
import vGenericForm from 'v-generic-form'
const el = document.getElementById('app');
const app = createApp();
app
.use(vGenericForm, {
components: {
// define default inputs here
MyInput, // you will be able to pass this as a variant now by passing variant: 'MyInput' to object scheme,
submitText: 'Okay'
},
})
.mount(el);
Styling
The generic form is structured like so:
div>
<form
:class="(options && options.formClass) || ''"
>
<div
v-for="(field, i) in fields.filter(
(f) => f.show === undefined || f.show !== false
)"
:key="i"
:class="field.divClass"
>
We can change the structure very easily by passing a options object
<v-generic-form :options="{
formClass: 'flex justify-between'
}">
As you may imagine, you can control the widths of the divs each one of your fields are in by passing a divClass
to your field schema:
[
{
name: "input",
placeholder: "input",
label: "input label",
type: "text",
divClass: "w-2/3",
inputClasses: "bg-green-200", // you can also style the default inputs by passing this inputClasses key
},
];
Changing submit
<v-generic-form @submit="onSubmit">
<template v-slot:submit>
<button type="submit">my custom submit button!</button>
</template>
</v-generic-frorm>
Asynchronous validation
<template>
<div>
<v-generic-form ref="form" :fields="fields" @submit="onSubmit" :beforeSubmit="beforeSubmit" />
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "Form",
computed: {
fields() {
return [
{
name: "name",
placeholder: "Your Name",
label: "Name",
},
{
name: "email",
placeholder: "Your Email",
label: "Email",
},
];
},
},
methods: {
async beforeSubmit({email}) {
const { data: { exists } } = await axios.post("https://myendpoint.com/checkEmails", { email });
if (exists) {
// prevent form from submitting by setting a custom error
this.$refs.form.setCustomError({
key: "email",
value: "Email already exists.",
});
} else {
// we can clear it if it doesn't exist
this.$refs.form.setCustomError({
key: "email",
clear: true,
});
}
},
},
};
</script>
Defining your own layout
<template>
<v-generic-form :schema="schema" @submit="submit" ref="form">
<template v-slot="{ errors, firstError, formData }">
<generic-input v-model="formData.test1" />
<span class="text-red-500">{{ firstError('test1') }}</span>
<generic-input v-model="formData.test2" />
<span class="text-red-500">{{ errors('test1') }}</span>
<button class="custom-submit" type="submit">Submit</button>
</template>
</v-generic-form>
</template>
<script>
import GenericInput from "./GenericInput.vue";
export default {
name: "CustomForm!",
components: {
GenericInput,
},
methods: {
submit(data) {
console.log(`data`, data);
},
},
computed: {
schema() {
return {
test1: {
defaultValue: "test",
rules: {
required: true,
},
},
test2: {
rules: {
required: true,
},
},
};
},
},
};
</script>
<style scoped>
.custom-submit {
background: blue;
color: white;
padding: 0.5rem 2rem;
display: block;
margin-top: 1rem;
cursor: pointer;
}
</style>
Using your own formData object
<template>
<v-generic-form :schema="schema" @submit="submit" :customFormData="customFormData" ref="form">
<template v-slot="{ errors, firstError, formData }">
<generic-input v-model="formData.test1" />
<span class="text-red-500">{{ firstError('test1') }}</span>
<generic-input v-model="formData.test2" />
<span class="text-red-500">{{ errors('test1') }}</span>
<button class="custom-submit" type="submit">Submit</button>
</template>
</v-generic-form>
</template>
<script>
import GenericInput from "./GenericInput.vue";
export default {
name: "CustomForm!",
components: {
GenericInput,
},
data() {
return {
customFormData: {
test1: "",
test2: "434",
},
};
},
methods: {
submit(data) {
console.log(`data`, data);
},
},
computed: {
schema() {
return {
test1: {
defaultValue: "test",
rules: {
required: true,
},
},
test2: {
rules: {
required: true,
},
},
};
},
},
};
</script>