Using Form Data

Learn how submit, store and manage form data using Vueform.

Storing Form Data

By default Vueform stores form data internally which can be accessed via data property:

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'

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

    onMounted(() => {
      console.log(form$.value.data)
    })

    return {
      form$,
    }
  }
}
</script>

The same thing we can do with Options API:

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.form$.data)
  }
}
</script>

Every time we are referencing form$.value using Composition API (setup()) it's equivalent to this.$refs.form$ in Options API.

Storing Data Externally

We can have an external object to store form data using v-model:

<template>
  <Vueform v-model="data">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
export default {
  data() {
    return {
      data: {}
    }
  }
}
</script>

Contrary to regular v-model, two-way data binding is disabled by default in Vueform.

This means that when the form data is changed internally (eg. by user input) the changes are reflected in the v-model object, but not the other way around. If the v-model object is changed outside of Vueform, changes will not be synchronize back to the elements. This is also true for any inital data the v-model object might have.

Synchronizing External Data

Even though two-way data binding is disabled by default, it can be enabled with sync:

<template>
  <Vueform v-model="data" sync>
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
export default {
  data() {
    return {
      data: {
        name: 'John Doe',
        email: 'john@doe.com'
      }
    }
  }
}
</script>

When sync is enabled whenever the data if v-model object is changed, the changes will be reflected in both the external object and form elements.

When using a lot elements or deeply nested elements the performance can be affected if sync is enabled. Make sure to only use sync if you actually need two-way data binding.

Using Vuex to Store Data

To use Vueform with Vuex first we need to register an object in state and a mutation:

export default {
  state: {
    forms: {
      registration: {}
    }
  },
  mutations: {
    UPDATE_FORM_DATA(state, value) {
      state.forms[value.form] = value.data
    }
  }
}

Then we can use :model-value and @update:modelValue (or :value and @input in Vue.js 2) to set the Vuex state object as a value and commit a mutation when the data is changed.

Vueform has data property and update method that we can use to get and set data form data:

<template>
  <Vueform :model-value="data" @update:modelValue="updateData"> <!-- :value="data" @input="updateData" in Vue.js 2 -->
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      data: => state.forms.registration
    })
  },
  methods: {
    updateData() {
      this.$store.commit('UPDATE_FORM_DATA', {
        form: 'registration',
        data: this.data,
      })
    }
  }
}
</script>

If we want we can also enable sync option to synchronize Vueform data with the state object when it is changed outside of Vueform:

<Vueform :model-value="data" @updateModel:input="updateData" sync>

Managing Form Data via API

If we aren't using v-model and Vueform data is handled internally we can still access and update it programmatically:

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'

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

    onMounted(() => {
      console.log(form$.value.data) // outputs form data

      form$.value.update({ // updates form data
        name: 'John Doe',
        email: 'john@doe.com',
      })
    })

    return { form$ }
  }
}
</script>

We are using a ref to access Vueform component, which allows us to access its API.

Loading Form Data

When a form is mounted we can load initial data. Here's an example for loading user data from database:

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'
import axios from 'axios'

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

    onMounted(async () => {
      const data = (await axios.get('/user/1')).data
      form$.value.load(data)
    })

    return { form$ }
  }
}
</script>

When load() is used instead of update() all the elements will be cleared which don't have a value in the loaded data. This is useful when we load complete set of data and we want to make sure no previous values remain filled.

Formatting Loaded Form Data

We can pass a function to the form's :format-load option, which transforms any data loaded through load(data, true):

<template>
  <Vueform ref="form$" :format-load="formatLoadedData">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'
import axios from 'axios'

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

    const formatLoadedData = (data) => {
      return {
        name: data.name.toUpperCase(),
        email: data.name.toLowerCase(),
      }
    }

    onMounted(async () => {
      form$.value.load({
        name: 'John Doe',
        email: 'john@Doe.com'
      }, true) // `true` refers to `format: true` param
    })

    return { form$ }
  }
}
</script>

The loaded value will be:

{
  name: 'JOHN DOE',
  email: 'john@doe.com'
}

Managing Element Data via API

We can also reach and update an element's data using the Vueform's el$(path) method:

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'

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

    onMounted(() => {
      const name = form$.value.el$('name')
      const email = form$.value.el$('email')

      console.log(name.value) // getting `name` element value
      console.log(email.value) // getting `email` element value

      name.update('John Doe') // setting `name` element value
      email.update('john@doe.com') // setting `email` element value
    })

    return { form$ }
  }
}
</script>

Formatting Loaded Element Data

We can pass a function to the form's :format-load option, which transforms any data loaded through load(data, true):

<template>
  <Vueform ref="form$">
    <TextElement name="name" label="Name" :format-load="formatLoadedName" />
    <TextElement name="email" label="Email" :format-load="formatLoadedEmail" />
  </Vueform>
</template>

<script>
import { ref, onMounted } from 'vue'

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

    const formatLoadedName = (value) => {
      return value.toUpperCase()
    }

    const formatLoadedEmail = (value) => {
      return value.toLowerCase()
    }

    onMounted(() => {
      form$.value.load({
        name: 'John Doe',
        email: 'john@Doe.com'
      }, true)
    })

    return { form$ }
  },
}
</script>

The loaded value will be:

{
  name: 'JOHN DOE',
  email: 'john@doe.com'
}

Submitting Form Data

We can tell Vueform where to submit our data by setting endpoint and method props:

<template>
  <Vueform endpoint="/form/submit" method="post">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

The default values can be configured in vueform.config.js:

// vueform.config.js

export default {
  endpoints: {
    submit: {
      url: '/form/submit',
      method: 'post'
    }
  }
}

Data vs. Request Data

There are two types of form data the form can submit. One is requestData that exludes elements with available: false or submit: false properties. The available property is only false if the element has conditions which are unmet. The submit property can be set manually for any element to prevent submitting its data. The other one is data which contains the data of all form elements regardless of the available and submit properties.

The form submits the requestData by default.

In case we'd like to change this and submit the complete form data without any restrictions we can pass a function to the form's :form-data option that returns the form's data (instead if requestData):

<template>
  <Vueform :form-data="form$ => form$.convertFormData(form$.data)">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

The convertFormData() method converts the data object to FormData. This might be left out if you don't want to send multipart/form-data request.

Formatting Data Before Submit

We can pass a function to :format-data option of Vueform or any elements, that will format its requestData:

<template>
  <Vueform :format-data="formatFormData">
    <TextElement
      name="name"
      label="Name"
      default="John Doe"
      :format-data="formatName"
    />
    <TextElement
      name="email"
      label="Email"
      default="john@Doe.com"
      :format-data="formatEmail"
    />
  </Vueform>
</template>

<script>
export default {
  mehtods: {
    formatFormData({ name, email }) {
      return {
        name: `<div>${name}</div>`,
        email: `<div>${email}</div>`,
      }
    },
    formatName(name, value) {
      return { [name]: value.toUpperCase() } // must return an object
    },
    formatEmail(name, value) {
      return { [name]: value.toLowerCase() } // must return an object
    }
  }
}
</script>

The submitted data will be:

{
  name: '<div>JOHN DOE</div>',
  email: '<div>john@doe.com</div>'
}

This can be used in conjuction with formatLoad methods to transform data between databases and forms.

Preparing for Submit

We can pass an async function to :prepare option of Vueform, that will be executed before the form submits:

<template>
  <Vueform :prepare="prepareForm">
    <TextElement name="name" label="Name" />
    <TextElement name="email" label="Email" />
  </Vueform>
</template>

<script>
export default {
  methods: {
    async prepareForm(data) {
      try {
        await // async process
      } catch (error) {
        throw error // this will cancel the submit process
      }
    }
  }
}
</script>

The prepare function runs after submit was initiated, the elements are validated and none was found invalid.

The submit process halts until all prepare functions are finished. The submit process can be cancelled by throwing an error in the prepare function.

👋 Hire Vueform team for form customizations and development Learn more