# Getting Started
EOM+ form is a library that allows you to generate schema-driven forms. It has been inspired by project like vue-form-generator, dynamic-schema-vuelidate or formvuelatte
We create this to help us to solve real life forms problems into real life projects. I mean that EOM+ form is not the fastest form processor neither the lightest but it does the job. We use it in production with Laravel REST api as backend.
# Screenshots
# Features
- reactive forms based on schemas
- group and repeat form elements
- over 20 built-in validators
- use bootstrap-vue components and styles
# What we would like to implement
- Dependency model like in XForms
- Expressions into JSON
- Framework7 version
- Formbuilder
# Dependencies
eom-form uses mainly
moreover it uses other components like
we also include open source icon fonts such font awesome 5, ionicons,open iconic, stroke icons 7, linearicons
# Installation
# NPM
You can install it via NPM or yarn.
$ npm install eom-form
# Usage
# EomForm props
The EomForm
component requires you to pass it schema
, model
and ref
properties.
The schema
can be both an object
or an array
, internally it will be transformed to an array
.
You can embed directly the JSON data, import it from local file or fetch it from your rest API.
The model
are the data themselves. The payload that you will submit to your API. For complex forms with repeat blocks you must declare an empty structure to get it working.
As with schema
prop, you can embed, import or fetch the content.
Finally, you must set a ref
property as it's necesarry to be able to call EomForm's validate method on submit form.
# Main file (App.vue)
<div id="app">
<b-container>
<b-alert :variant="status.variant" dismissible v-model="status.message" v-if="status.message">
{{ status.message }}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-btn variant="success" type="submit">submit</b-btn>
</form>
</b-container>
</div>
</template>
<script>
import Vue from 'vue'
import EomForm from 'eom-form'
import schema from './data/schema.json'
Vue.use(EomForm)
export default {
name: 'App',
data () {
return {
status: {
message: '',
variant: 'success'
},
schema,
model: {},
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status.message = 'invalid_datas'
this.status.variant = 'danger'
return
}
}
}
}
</script>
# Schema
{
"firstName": {
"component": "EomInput",
"label": "firstname",
"icon": "ion ion-ios-person",
"help" : "introduce tu nombre",
"validations": {
"required": {
},
"minLength": {
"min": 3
}
}
},
"description": {
"component": "EomTextarea",
"label": "description",
"icon": "ion ion-ios-chatboxes",
"help": "enter a long description",
"maxlength": 50
},
"enabled": {
"component": "EomCheckbox",
"label": "enabled",
"help": "this checkbox enable/disable something"
},
"checkboxGroup": {
"component": "EomCheckboxGroup",
"label": "checkboxGroup",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
},
"radioGroup": {
"component": "EomRadioGroup",
"label": "radioGroup",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
},
"selectOne": {
"component": "EomSelectOne",
"label": "selectOne",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
}
}
# Playground
{ "firstName": "ABC", "description": "", "enabled": 0 }
<template>
<b-container class="p-2">
<b-alert :variant="status.variant" dismissible v-model="status.invalid" v-if="status.message">
{{ $t(status.message) }}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-row>
<b-col>
<b-btn variant="success" type="submit">submit</b-btn>
</b-col>
</b-row>
</form>
<pre class="language-json" v-html="model"></pre>
</b-container>
</template>
<script>
const SCHEMA = {
firstName: {
component: "EomInput",
label: "firstname",
icon: "ion ion-ios-person",
help : "introduce tu nombre",
validations: {
required: {
},
minLength: {
min: 3
}
}
},
description: {
component: "EomTextarea",
label: "description",
icon: "ion ion-ios-chatboxes",
help: "enter a long description",
maxlength: 50
},
enabled: {
component: "EomCheckbox",
label: "enabled",
help: "this checkbox enable/disable something"
},
checkboxGroup: {
component: "EomCheckboxGroup",
label: "checkboxGroup",
options: [
{ text: "Option A", value: "A" },
{ text: "Option B", value: "B" }
]
},
radioGroup: {
component: "EomRadioGroup",
label: "radioGroup",
options: [
{ text: "Option A", value: "A" },
{ text: "Option B", value: "B" }
]
},
selectOne: {
component: "EomSelect",
label: "selectOne",
options: [
{ text: "Option A", value: "A" },
{ text: "Option B", value: "B" }
]
}
}
export default {
data () {
return {
status: {
message: '',
variant: 'success',
invalid: false
},
schema: SCHEMA,
model: {
firstName: 'ABC',
description: '',
enabled: 0
},
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status = {
message: 'invalid_datas',
variant: 'danger',
invalid: true
}
return
}
}
}
}
</script>
# Examples
# Select Example
<template>
<b-container class="p-2">
<b-alert :variant="status.variant" dismissible v-model="status.invalid" v-if="status.message">
{{status.message}}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-row>
<b-col>
<b-btn variant="success" type="submit">submit</b-btn>
</b-col>
</b-row>
</form>
</b-container>
</template>
<script>
const SCHEMA = {
selectOne: {
component: "EomSelect",
label: "selectOne",
options: [
{text: "Option A", value: "A"},
{text: "Option B", value: "B"}
]
},
select: {
component: "EomSelect",
label: "select",
multiple: "true",
options: [
{text: "Option A", value: "A"},
{text: "Option B", value: "B"}
]
}
}
export default {
data () {
return {
status: {
message: '',
variant: 'success',
invalid: false
},
schema: SCHEMA,
model: {
select: [ 'A', 'B' ],
selectOne: 'B',
},
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status = {
message: 'invalid_datas',
variant: 'danger',
invalid: true
}
return
}
}
}
}
</script>
# Repeat Example
This example show how to have repeatable structure in your form. You can dynamically add/remove row.
{ "firstname": "", "description": "", "enabled": 0, "group": [ { "ipaddress": "", "macaddress": "" } ] }
<template>
<b-container class="p-2">
<b-alert :variant="status.variant" dismissible v-model="status.invalid" v-if="status.message">
{{status.message}}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-row class="py-2">
<b-col>
<b-btn variant="success" type="submit">submit</b-btn>
</b-col>
</b-row>
</form>
<pre class="language-json" v-html="model"></pre>
</b-container>
</template>
<script>
const SCHEMA = [
{
component: "EomInput",
model: "firstname",
label: "firstname",
icon: "ion ion-ios-person",
help: "introduce tu nombre",
validations: {
required: {
},
minLength: {
min: 3
}
}
},
{
component: "EomRepeat",
model: "group",
schema: [
[
{
component: "EomInput",
model: "ipaddress",
label: "ipaddress",
autocomplete: "off",
validations: {
required: {},
ip: {}
}
},
{
component: "EomInput",
model: "macaddress",
label: "macaddress",
autocomplete: "off",
validations: {
required: {},
macAddress: {}
}
}
]
]
}
]
export default {
data () {
return {
status: {
message: '',
variant: 'success',
invalid: false
},
schema: SCHEMA,
model: {
firstname: '',
description: '',
enabled: 0,
group: [
{
ipaddress : '',
macaddress: ''
}
]
}
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status = {
message: 'invalid_datas',
variant: 'danger',
invalid: true
}
return
}
}
}
}
</script>
# Upload Example
<template>
<b-container class="p-2">
<b-alert :variant="status.variant" dismissible v-model="status.invalid" v-if="status.message">
{{status.message}}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-row>
<b-col>
<b-btn variant="success" type="submit">submit</b-btn>
</b-col>
</b-row>
</form>
</b-container>
</template>
<script>
const SCHEMA = [
{
component: "EomFileUpload",
model: "attachments",
label: "attachments",
url: "https://eom.nixus.es:8443/api/v1/contacts/40/files",
acceptedMimeTypes: "image/jpeg"
}
]
export default {
data () {
return {
status: {
message: '',
variant: 'success',
invalid: false
},
schema: SCHEMA,
model: {
attachments: []
}
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status = {
message: 'invalid_datas',
variant: 'danger',
invalid: true
}
return
}
}
}
}
</script>
# Validators Example
<template>
<b-container class="p-2">
<b-alert :variant="status.variant" dismissible v-model="status.invalid" v-if="status.message">
{{status.message}}
</b-alert>
<form class="mb-3" @submit.prevent="handleSubmit" novalidate>
<EomForm
:schema="schema"
v-model="model"
ref="eomForm"
/>
<b-row>
<b-col>
<b-btn variant="success" type="submit">submit</b-btn>
</b-col>
</b-row>
</form>
</b-container>
</template>
<script>
const SCHEMA = {
spanishId: {
component: "EomInput",
label: "spanishId",
icon: "ion ion-ios-finger-print",
help: "",
validations: {
required: {
},
spanishId: {
}
}
},
bankAccount: {
component: "EomInput",
label: "bankAccount",
icon: "ion ion-logo-euro",
help: "",
validations: {
required: {
},
iban: {
}
}
},
ipAddress: {
component: "EomInput",
label: "ipAddress",
help: "",
validations: {
required: {
},
ip: {
}
}
},
ipv6Address: {
component: "EomInput",
label: "ipv6Address",
help: "",
validations: {
required: {
},
ipv6: {
}
}
},
macAddress: {
component: "EomInput",
label: "macAddress",
help: "",
validations: {
required: {
},
macAddress: {
}
}
},
e164PhoneNumber: {
component: "EomInput",
label: "e164PhoneNumber",
help: "",
validations: {
required: {
},
e164PhoneNumber: {
}
}
},
alpha: {
component: "EomInput",
label: "alpha",
validations: {
required: {
},
alpha: {
}
}
},
alphaDash: {
component: "EomInput",
label: "alphaDash",
validations: {
required: {
},
alphaDash: {
}
}
},
alphaNumeric: {
component: "EomInput",
label: "alphaNumeric",
validations: {
required: {
},
alphaNumeric: {
}
}
},
digits: {
component: "EomInput",
label: "digits",
validations: {
required: {
},
digits: {
}
}
},
digitsBetween: {
component: "EomInput",
label: "digitsBetween",
help: "enter number between 5 and 50",
validations: {
required: {
},
digitsBetween: {
min: 5,
max: 50
}
}
},
amount: {
component: "EomCurrency",
label: "amount",
validations: {
required: {
}
}
},
numeric: {
component: "EomNumeric",
label: "numeric",
validations: {
required: {
}
}
},
date: {
component: "EomDatepicker",
label: "date",
icon: "ion ion-ios-calendar",
validations: {
required: {
}
}
},
startsWith: {
component: "EomInput",
label: "startsWith",
help: "must starts with https://",
validations: {
required: {
},
startsWith: {
needle: "https://"
}
}
},
endsWith: {
component: "EomInput",
label: "endsWith",
help: "must ends with EUROS",
validations: {
required: {
},
startsWith: {
needle: "EUROS"
}
}
}
}
export default {
data () {
return {
status: {
message: '',
variant: 'success',
invalid: false
},
schema: SCHEMA,
model: {
spanishId: '39921948C',
bankAccount: 'ES0504873597264184221852',
ipAddress: '192.168.20.7',
ipv6Address: '2001:1:2:3:4:5:6:7',
macAddress: '0000.0000.0000',
e164PhoneNumber: '+34968000730',
alpha: 'abcd',
alphaDash: 'a-b_c',
alphaNumeric: 'abc123',
digits: '1234',
digitsBetween: 25,
amount: '5.50',
numeric: '18.36',
date: '2020-05-07',
startsWith: 'https://',
endsWith: 'EUROS'
},
}
},
methods: {
handleSubmit () {
this.status.message = ''
let valid = this.$refs.eomForm.validate()
if (!valid) {
console.warn('invalid form')
this.status = {
message: 'invalid_datas',
variant: 'danger',
invalid: true
}
return
}
}
}
}
</script>
# Components
All components have common properties to setup things like icon or help hint
# EomInput
Create various type inputs such as: text, password, number, url, email, search, range, date and more
{
"field": {
"component": "EomInput",
"label": "my label",
}
}
# EomPasswordChecker
{
"field": {
"component": "EomPasswordChecker",
"label": "my label",
}
}
# EomCurrency
{
"field": {
"component": "EomCurrency",
"label": "my label",
}
}
# EomTextarea
{
"field": {
"component": "EomTextarea",
"label": "my label",
}
}
# EomCheckbox
{
"field": {
"component": "EomCheckbox",
"label": "my label",
}
}
# EomCheckboxGroup
{
"field": {
"component": "EomCheckboxGroup",
"label": "my label",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
}
}
# EomRadioGroup
{
"field": {
"component": "EomRadioGroup",
"label": "my label",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
}
}
# EomSelect
{
"field": {
"component": "EomSelect",
"label": "my label",
"options": [
{ "text": "Option A", "value": "A" },
{ "text": "Option B", "value": "B" }
]
}
}
# EomDatepicker
{
"field": {
"component": "EomFileUpload",
"label": "my label",
}
}
# EomFileUpload
{
"field": {
"component": "EomFileUpload",
"label": "my label",
}
}
# EomEditor
{
"field": {
"component": "EomEditor",
"label": "my label",
}
}
# Validators
This library comes with over 20 built in validators.
- minLength:
- alpha:
- alphaNumeric:
- alphaDash:
- date:
- beforeDate:
- beforeOrIgualDate:
- digits:
- digitsBetween:
- endsWith:
- startsWith:
- email:
- url:
- sameAs:
- regex:
- phoneNumber:
- spanishID: validate spanish personal identity number (CIF,NIF,NIE)
- iban: validate an IBAN bank account number
- e164Number: validate a E.164 phone number (e.g. +34968123456)
- macAddress: validate a mac address (e.g. af:af:af:af:af:af)
- ip: validate an ipv4 address (e.g. 192.168.1.1)
- ipV6: validate an ipv6 address (e.g. 2001:1:2:3:4:5:6:7)
# Contribution
We need hands. Please send pull requests improving the usage and fixing bugs, improving documentation and providing better examples, or providing some testing.