Masuga Smile Doodle

Using Vue.js with Server Rendered Form Input Values

Masuga
Catherine K. Sep 10, 2021 Read time: 4 minute read
Using Vue.js with Server Rendered Form Input Values

Vue.js is great for interactive forms, and updating page contents based on user interaction with a form. But Vue.js isn't so great for forms with input values already rendered from the server. We'll look at how to get around that roadblock.

Rendered via what now?

Vue.js really shines in single-page webapps and webpages that are rendered mostly on the client-side. When there's little interaction with the server, or if server interactions can happen asynchronously, you shouldn't run into too many problems with using Vue.js for your forms. However, sometimes you'll need to load a dynamic form already populated with data from the server.

An example of the former is an empty contact form: none of the inputs have values (which come from the server) in them when the page first loads. An example of the latter is an account edit form: it has to get the user's data from the server and populate the form's fields with that data from the start so that the user can edit it. That's where the roadblock lies.

So what's the problem?

To quote the docs:

v-model will ignore the initial value, checked or selected attributes found on any form elements. It will always treat the current active instance data as the source of truth. You should declare the initial value on the JavaScript side, inside the data option of your component.

That means that any data that's already in the form input components on page load (server-side rendered) gets completely ignored by Vue. That firstName field's value, fetched by your CMS? (Our CMS of choice is currently Craft CMS, for reference) Non-existent in the eyes of Vue. The user's communications preferences checkboxes, also gone. It's very hard to have an "edit account" form when you can't access the user's account information in order to edit it.

Then how do we fix it?

There are a few options, and none of them are great.

  1. Don't render those inputs with data from the server upon load. Instead, fetch those input fields' values asynchronously (AJAX, axios, etc) in your form component's mounted and feed those values to your component's data object.
    • This isn't a great option if your form has lots of fields. You'll have to sort through the response json for each field's value and assign it to the correct data property, and the possibility for error in that process increases with each field.
  2. Render the input fields' initial values as data attributes on the elements themselves, fetch them during the component's mounted event, and update the component's props with their values.
    • This is certainly fewer steps and less code than Option 1, but it's still more work than one might expect to be necessary from a full-featured framework and for such a common and simple task.
  3. Get the data first and construct the form second. Get the data asynchronously and create the form, with its inputs populated, based on that data.
    • This approach doesn't really differ much from Option 1, except that you may end writing string templates, which is even less fun.

We prefer to go with Option 2, since it's the most straightforward approach.

Can I have an example?

Sure! Here's a simplified form, with data attributes that hold the input fields' values that are fetched from the server. A couple of notes here:

  1. Yes, this is a DOM template. That's not always the preferred practice for Vue components (for more information, see this article or the docs). However, in this case, our whole application is not a Vue application; just this one piece is. It would be impractical to rewrite significant portions of a page to accommodate a single component's preferred practices.
  2. Craft CMS uses Twig as its templating engine, and both Twig and Vue make use of double curly braces ( {{ }} ) as their delimiters. For that reason, we pass Vue a delimiters parameter to tell Vue to ignore its standard double curly braces and instead use a different delimiter.

The HTML

<div id="accountForm">
  <form method="post" accept-charset="UTF-8">
    <label>Field One</label>
    <input type="text" name="fields[fieldOne]" id="fieldOne" data-value="{{ currentUser.fieldOne }}" v-model="fieldOne">
    <label>Field Two</label>
    <input type="text" name="fields[fieldTwo]" id="fieldTwo" data-value="{{ currentUser.fieldTwo }}" v-model="fieldTwo">
    <label>Field Three</label>
    <input type="text" name="fields[fieldThree]" id="fieldThree" data-value="{{ currentUser.fieldThree }}" v-model="fieldThree">
  </form>
</div>

The Javascript

import { createApp } from 'vue/dist/vue.esm-bundler.js';

var accountFormApp = createApp({
  delimiters: ['${', '}'],
  data() {
    return {
      fieldOne: '',
      fieldTwo: '',
      fieldThree: ''
    }
  },
  mounted() {
    this.fieldOne = document.getElementById('fieldOne').getAttribute('data-value');
    this.fieldTwo = document.getElementById('fieldTwo').getAttribute('data-value');
    this.fieldThree = document.getElementById('fieldThree').getAttribute('data-value');
  }
});

accountFormApp.mount('#accountForm');

In Closing

Tl;dr: you can't get a form input's default value to be used by v-model if that value was fetched from the server on page load. You can put that value in a data attribute and add it to your component's data object, instead. It's a surprising limitation, but once you're aware of it and know your options, it's not difficult to work around.

Gray Masuga Texture Gray Masuga Texture

You Might Also Like

How to Fix an Empty H1 Tag on Your Shopify Homepage

How to Fix an Empty H1 Tag on Your Shopify Homepage

Ryan Masuga Aug 15, 2022  ·  5 minute read

Shopify's Dawn theme homepage may get an SEO warning for an empty h1 header element. This is a missed opportunity to help search engines understand what your page is about. Small edits to the theme code can fix this technical SEO issue.

Subscribe to Our Newsletter

A few times a year we send out a newsletter with tips and info related to web design, SEO, and things you may find interesting.

No spam. Unsubscribe any time.

Decorative Masuga Border