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>