Breaking Forms into Steps

Learn how to break you forms into steps to enchance user experience.

Using Inline Form Steps

We can use FormSteps and FormStep components to break forms into steps:

<Vueform>
  <template #empty>
    <FormSteps>
      <FormStep name="first" label="First" :elements="['first_input']" />
      <FormStep name="second" label="Second" :elements="['second_input']" />
      <FormStep name="third" label="Third" :elements="['third_input']" />
    </FormSteps>

    <FormElements>
      <TextElement name="first_input" placeholder="First input" />
      <TextElement name="second_input" placeholder="Second input" />
      <TextElement name="third_input" placeholder="Third input" />
    </FormElements>

    <FormStepsControls />
  </template>
</Vueform>

When using them inline we have to put all the content of the form into #empty slot, becase the default Vueform slots is only intended for rendering elements.

Form elements have to be put into FormElements component which serves as a wrapper.

We also need to add FormStepsControls to the template, which renders the form controls (Previous, Next, Finish).

We can use these three parts anywhere in out template, which gives us the freedom to create any kind of layout we want.

Step Options

Each FormStep component must have at least 2 props:

  • name - the internal name of the step we can later use to reach its API
  • elements - an array of element names (as string) that the step should include.

And also label, which is the label visible to the user if it is not defined inline between <FormStep></FormStep>.

Customizing Steps Controls

We can hide buttons for a step using buttons option. For example:

<FormStep name="first" :buttons="{
  previous: false
}" ... />

This will hide the previous button on the first step. We can also hide next and finish buttons.

We can also change the labels of buttons for a step using labels options. For example:

<FormStep name="first" :labels="{
  previous: '< Previous',
  next: 'Next >'
}" ... />

Button labels can contain HTML or they can be Vue components:

<FormStep name="first" :labels="{
  previous: { template: `<span><icon name='arrow-left' /> Previous</span>` },
  next: { template: `<span>Next <icon name='arrow-right' /></span>` },
}" ... />

We can provide default control templates to FormStepsControls via slots:

<FormStepsControls :labels="false">
  <slot #previous><icon name='arrow-left' /> Previous</slot>
  <slot #next>Next <icon name='arrow-right' /></slot>
  <slot #finish><icon name='tick' /> Finish</slot>
</FormStepsControls>

When using slots for custom controls template FormStepsControls component must receive :labels="false" option and it will ignore the :labels object of FormStep.

Using Form Steps as an Object

If we are using objects to describe our forms we can use steps option to create a configuration for form steps:

<script>
import { Vueform, useVueform } from '@vueform/vueform'

export default {
  mixins: [Vueform],
  setup: useVueform,
  data: () => ({
    
    vueform: {
      steps: {
        first_step: {
          label: 'First',
          elements: ['first_input']
        },
        second_step: {
          label: 'Second',
          elements: ['second_input']
        },
        third_step: {
          label: 'Third',
          elements: ['third_input']
        },
      },
      schema: {
        first_input: { type: 'text', placeholder: 'First input' },
        second_input: { type: 'text', placeholder: 'Second input' },
        third_input: { type: 'text', placeholder: 'third input' },
      }
    }
  })
}
</script>

Steps API

Once we have steps we can reach their API and use their methods and properties.

This is how we can reach FormSteps and FormStep components:

<template>
  <Vueform ref="form$">
    <template #empty>
      <FormSteps>
        <FormStep name="first" ... />
      </FormSteps>

      <!-- ... -->
    </template>
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue' // '@vue/composition-api' in Vue2

export default {
  setup(props, context) {
    const form$ = ref(null)

    form$.value.steps$ // returns FormSteps component instance
    form$.value.steps$.steps$.first // returns FormStep component instance named `first`

    return { form$ }
  }
}
</script>
<script>
import { Vueform, useVueform } from '@vueform/vueform'

export default {
  mixins: [Vueform],
  setup: useVueform,
  data: () => ({
    vueform: {
      steps: {
        first_input: {
          // ...
        },
      },
      // ...
    }
  }),
  mounted() {
    this.steps$ // returns FormSteps component instance
    this.steps$.steps$.first // returns FormStep component instance named `first`
}
</script>

Later we can use consult FormSteps and FormStep API Reference to see what method and properties can be used.

Using Tabs

Steps are useful for users to submit data in a more user friendly way, but it's less convenient for changing existing data. If we'd like to load existing data which was initially submitted via steps, we can use FormTabs and FormTab components to replace steps:

<Vueform>
  <template #empty>
    <FormTabs>
      <FormTab name="first" label="First" :elements="['first_input']" />
      <FormTab name="second" label="Second" :elements="['second_input']" />
      <FormTab name="third" label="Third" :elements="['third_input']" />
    </FormTabs>

    <FormElements>
      <TextElement name="first_input" placeholder="First input" />
      <TextElement name="second_input" placeholder="Second input" />
      <TextElement name="third_input" placeholder="Third input" />
    </FormElements>
  </template>
</Vueform>

Tabs can also be used as an object, just like steps:

<script>
import { Vueform, useVueform } from '@vueform/vueform'

export default {
  mixins: [Vueform],
  setup: useVueform,
  data: () => ({
    vueform: {
      tabs: {
        first_input: {
          label: 'First',
          elements: ['first']
        },
        second_input: {
          label: 'Second',
          elements: ['second']
        },
        third_input: {
          label: 'Third',
          elements: ['third']
        },
      },
      schema: {
        first_input: { type: 'text', placeholder: 'First input' },
        second_input: { type: 'text', placeholder: 'Second input' },
        third_input: { type: 'text', placeholder: 'third input' },
      }
    }
  })
}
</script>

Tabs API

We can also reach FormTabs and FormTab components' API once they are mounted:

<template>
  <Vueform ref="form$">
    <template #empty>
      <FormTabs>
        <FormTab name="first" ... />
      </FormTabs>

      <!-- ... -->
    </template>
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue' // '@vue/composition-api' in Vue2

export default {
  setup(props, context) {
    const form$ = ref(null)

    form$.value.tabs$ // returns FormTabs component instance
    form$.value.tabs$.tabs$.first // returns FormTab component instance named `first`

    return { form$ }
  }
}
</script>
<script>
import { Vueform, useVueform } from '@vueform/vueform'

export default {
  mixins: [Vueform],
  setup: useVueform,
  data: () => ({
    vueform: {
      tabs: {
        first_input: {
          // ...
        },
      },
      // ...
    }
  }),
  mounted() {
    this.tabs$ // returns FormTabs component instance
    this.tabs$.tabs$.first // returns FormTab component instance named `first`
}
</script>

Later we can use consult FormTabs and FormTab API Reference to see what method and properties can be used.