Trifecta of Parent, Child and Layout components for reusable Form family
<template>
<ui-form name="address">
<div class="fields">
<ui-input
name="house_no"
label="House name / no"
:value.sync="values.house_no"
:rules="rules.house_no"
classes="four wide"
></ui-input>
<ui-input
name="street"
label="Street"
:value.sync="values.street"
:rules="rules.street"
classes="twelve wide"
></ui-input>
</div>
<div class="two fields">
<ui-input
name="town"
label="Town / City"
:value.sync="values.town"
:rules="rules.town"
classes="six wide"
></ui-input>
<ui-input
name="district"
label="District"
:value.sync="values.district"
:rules="rules.district"
classes="six wide"
></ui-input>
</div>
<div class="three fields">
<ui-dropdown
name="county"
label="County"
searchable
:rules="rules.county"
:value.sync="values.county"
classes="six wide"
>
<uk-counties/>
</ui-dropdown>
<ui-input
name="postcode"
label="Postcode"
:value.sync="values.postcode"
:rules="rules.postcode"
classes="six wide"
></ui-input>
</div>
<div class="four fields">
<ui-dropdown
name="time_at_address"
label="Time at address"
:value.sync="values.time_at_address"
:options="options.time"
></ui-dropdown>
</div>
<ui-populator/>
</ui-form>
</template>
<script>
import Form from 'ui/base/Form'
import UkCounties from 'ui/partials/UkCounties.vue'
import fields from 'data/fields'
import rules from 'data/validation'
import data from 'data/forms'
export default {
components: {
UkCounties,
},
extends: Form,
props: {
name: {
type: String,
default: null,
},
},
data: function () {
return {
rules: rules.address,
options: {
time: fields.get('address.time_at_address'),
county: fields.get('address.time_at_address'),
},
values: this.$store.initial('account/address'),
dummy: data.address
}
},
}
</script>
<style>
</style>
/**
* Base element or mixin for form components
*/
export default {
data () {
return {
fields: [],
}
},
computed: {
form () {
return this.$el.tagName.toLowerCase() === 'form'
? this.$el
: this.$el.querySelector('form')
},
errors () {
return this.fields
.map(child => child.error)
.filter(error => !!error)
},
validated () {
return this.errors.length === 0
}
},
mounted () {
this.form.addEventListener('submit', this.onSubmit)
},
beforeDestroy () {
this.form.removeEventListener('submit', this.onSubmit)
},
methods: {
registerField (node) {
if (this.fields.indexOf(node) === -1) {
this.fields.push(node)
}
},
unregisterField (node) {
this.fields.splice(this.fields.indexOf(node), 1)
},
validate () {
const errors = this.fields
.map(field => field.validate())
.filter(error => !!error)
return errors.length === 0
? Promise.resolve(this.values)
: Promise.reject(errors)
},
populate (data = 'dummy') {
data = typeof data === 'string'
? this[data]
: data
if (typeof this.values === 'object' && data) {
Object.assign(this.values, data)
}
else {
console.warn('[Form] Unable to populate values: invalid data / key supplied')
}
},
clear () {
Object.keys(this.values)
.map(key => {
this.values[key] = null
})
this.reset()
},
reset () {
// seems to be a delay to reset, so call on next tick
this.$nextTick(() => {
this.fields.forEach(child => child.reset())
})
},
onSubmit () {
this.$emit('submit')
}
}
}
<template>
<form data-layout="form"
:data-name="name"
class="ui-form ui form"
@submit.prevent="submit">
<div class="ui segment">
<slot>
<!-- content goes here -->
</slot>
<div data-name="buttons" class="ui unit">
<slot name="buttons">
<!-- buttons go here -->
</slot>
<button v-show="false">Submit</button>
</div>
</div>
</form>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true,
},
submit: {
type: Function,
default: function () {
this.$emit('submit')
}
}
},
}
</script>