Skip to content

Creating your component

Composition API 2.0.0+

To create a field component you make use of different composables (?) to get the behaviour you want the component to have. Different composables handle different functionality inside the field component.

Every component must at least use these composables to work properly:

  • useFieldEmits - returns all events emitted by a field component;
  • useFieldProps - returns all props used by a field component;
  • useFormModel - used to get the current model value for this field component.

Optional:

Basic example

vue
<template>
  <input
    :id="id"
    class="field-input"
    type="text"
    :name="field.name"
    :required="isRequired"
    :disabled="isDisabled"
    :placeholder="field.placeholder"
    :autocomplete="field.autocomplete || 'off'"
    :value="currentModelValue"
    @input="onFieldValueChanged"
    @blur="onBlur"
  >
</template>
vue
<script setup>
import { toRefs } from 'vue'
import { useFormModel, useFieldAttributes, useFieldValidate, useFieldProps, useFieldEmits } from '@/composables'

const emits = defineEmits(useFieldEmits())
const props = defineProps(useFieldProps())

const { field, model } = toRefs(props)

const { currentModelValue } = useFormModel(model.value, field.value)
const { isRequired, isDisabled } = useFieldAttributes(model.value, field.value)
const { errors, validate } = useFieldValidate(
  model.value,
  field.value,
  isDisabled.value,
  isRequired.value,
  false
)

const onBlur = () => {
  validate(currentModelValue.value).then((validationErrors) => {
    emits('validated',
      validationErrors.length === 0,
      validationErrors,
      field.value
    )
  })
}

const onFieldValueChanged = ({ target }) => {
  errors.value = []
  emits('onInput', target.value)
}

Advanced example

For a more advanced example, you can take a look at the FieldSelect (source) component.

Registering your component

To use your component inside the form generator, your component must be globally available throughout your app. You do this by simply registering it in your main.js file. Names must start with Field.

javascript
// other imports etc.
import FieldCustom from '@/fields' // your import goes here

app.component('FieldCustom', FieldCustom)

Options API (Mixin) deprecated <2.0.0

The base of every field component is the abstractField mixin. Thus, in order to create a custom component, you'll need to make use of the Options API. An example of a field that uses these methods can be found here.

To start creating your own field component, create a new Vue file and import the abstractField mixin like so:

vue
<script>
  import { abstractField } from "@kevinkosterr/vue3-form-generator";
  
  export default {
    mixins: [ abstractField ]
  }
</script>

Every field must make use of one of the following methods to pass its new value to the form generator:

  • Handle one of the built-in input events and pass onFieldValueChanged as the event handler.
  • Manually emit an onInput event with the new value as its argument.
vue
<template>
    <input type="checkbox" @change="onFieldValueChanged" >
</template>

<script>
  import { abstractField } from "@kevinkosterr/vue3-form-generator";

  export default {
    mixins: [ abstractField ],
    methods: {
      /** Will be called by onFieldValueChanged() to 
       * determine the definitive value to pass, returns `target.value` 
       * by default */
      formatFieldValue (target) { // Event target is passed here.
        return target.checked
      }
    }
  }
</script>
vue
<template>
  <div v-for="card in cards" class="card" @click.prevent="selectCard(card.name)">
    {{ card.body }}
  </div>
</template>

<script>
  import { abstractField } from "@kevinkosterr/vue3-form-generator";

  export default {
    mixins: [ abstractField ],
    data () {
      return {
        cards: [
          { name: 'foo', body: 'Wow, that is awesome!' },
          { name: 'bar', body: 'This is cooler!'  }
        ]
      }
    },
    methods: {
      selectCard (name) {
        this.$emit('onInput', name) // Card with 'name' was chosen
      }
    }
  }
</script>