Binding Reactive Data to Form Elements

Forms are the primary way to receive input from users.  Below we review the various forms elements HTML and then discuss how we can bind reactive data to the form controls.

The official specification for HTML forms can be found in section 4.10 of the HTML Living Standard.  Note that the link leads to a page that shows only a portion of the specification for forms; from 4.10.1 to 4.10.5.  Chapter 4.10 has 22 subsections, from 4.10.1 t0 4.10.22.  You can get to all of the form documentation from the specification’s table of contents.

The links provided in the following section direct you to the mozilla documentation for each of the form elements.  The documentation is great. Each page shows you an example of the element’s use and discusses each of attributes that can be used with the element.

Form Elements

A <form> element consists of a set of labels and form controls. <fieldset> is used to organize and group related form elements. <legend> defines a caption for the <fieldset>.

Labels are used as captions for the form controls and are defined with the <label> tag and the <output> tag can be used to display data to the user (e.g. the results of a calculator computation).

Below are all of the possible form controls.

User Input Text Controls

<input> displays single line field that can hold arbitrary text, a search field, or validated text such as emailtelephone numberurl, and password.

<textarea> provides a multi-line text fields.

Programmable Selection Controls

<input> elements can display programmable selection controls like checkboxes and radio buttons. The value of an <input> element can also be set by the selection of an item in a drop down box using the <datalist> tag for the box and <option> tag for the items in the box.

<select> shows single or multiple choice drop down boxes. <option> defines a selectable item in a drop down box and selectable items can be grouped together with <optgroup> tags.

Numeric Selection and Display Controls

The <input> element can be used to display controls that allow the use to select numeric data.  These include number pickers and slider controls.

<progress> displays a progress bar whose value can change over time.

<meter> displays a bar that changes color based on the value.

Pickers

The <input> elements can be used to select system data using (datetime, and date-time) pickers, color pickers, and file pickers.

Button Controls

<input> elements can be used to create (submit, reset, image, normal button) buttons or we can use the <button> element for submit, reset, and normal buttons.

Hidden Input Controls

<input> elements can be hidden.

Binding Reactive Data to form Elements

We saw in an earlier tutorial that we can bind a reactive variable to the value attribute of an <input> element so that when the value of the reactive variable changes, so does the value of the value attribute.  This is a one-way binding.  If the user enters data in the <input> element, the reactive variable is not modified.

<input type="text" v-bind:value="user.name.first">
We can however register a handler on the <input> element, to bind in the other direction, so that when the user enters a string the value of the reactive variable is set to the string entered by the user (in event.target.value).
<input type="text" v-on:input="event => user.name.first = event.target.value">

To create a 2-way binding we can include both the v-bind and v-on directives.

<input type="text" v-bind:value="user.name.first"
  v-on:input="event => user.name.first = event.target.value">

Alternatively, we can use the simplified v-model directive which, behind the scenes does exactly what is done when using the v-bind and v-on directives together.

<input type="text" v-model="user.name.first">

Since v-model is a 2-way binding we have to be mindful of the value that we use when we initialize the reactive variable, since that will be the value of the value attribute of the input element.

Binding Single Checkboxes

When a single checkbox is checked it is given the checked attribute in the DOM.  When it is  unchecked the checked attribute is removed.

When binding reactive data to a single checkbox using v-model, the reactive data is bound to the <input> element’s checked attribute.  That is, if the attribute exists, the value of the reactive variable is true, otherwise it is false.

In the example below, the label will display either true or false depending on if the checkbox is checked or not.

<input type="checkbox" v-model="user.happy">

<label> {{ user.happy }} </label>

Binding Multiple Radio Buttons

When we have multiple radio buttons we associate them together by setting the name attribute to the same value.  This prevents more than one being selected at any point in time.  In order to determine which was selected  and set the value attribute in each of the radio button <input> elements to a unique value.
When we bind a reactive variable to the input elements, the value of the reactive element will be the string in the value attribute of the radio button that was selected.
<input type="radio" name="spam" value="yes" v-model="user.sendSpam">
<label>Yes</label>

<input type="radio" name="spam" value="no" v-model="user.sendSpam" checked>
<label>No</label>

Binding Multiple Checkboxes

A user may select multiple checkboxes in a group of checkboxes that have the same value in their name attributes.  When we bind a reactive variable to a group of checkboxes, the reactive variable must be initialized with an array so that it can hold all of the strings in the value attributes of the checkboxes that are selected.
In the example below, the <span> will display an array containing the strings in the value attributes of the selected checkboxes.
<input "type="checkbox" name="color" value="magenta" v-model="user.colors">
<label> Magenta </label>
<input type="checkbox" name="color" value="sea green" v-model="user.colors">
<label>Sea Green</label>

<span>Selected colors: {{ user.colors }} </span>

Binding Select Option Sets

Similar to groups of checkboxes, if a <select> element has the multiple attribute then users can choose multiple options in the list of options provided.
Similar to when binding multiple check boxes, the reactive variable bound to a <select> with the multiple attribute must be initialized with an array so that it can hold all of the strings in the value attributes of the <options> elements that are selected by the user.
<select v-model="user.favs" multiple>
  <option value="Pink Floyd">Pink Floyd</option>
  <option value="Foo Fighters">Foo Fighters</option>
  <option value="Janes Addiction">Janes Addiction</option>
  <option value="Radiohead">Radiohead</option>
</select>

Using File Controls

File <input> controls are read only.  That is, we cannot change the value of a file <input> element programmatically; the user has to choose a file.  Therefore we cannot use v-model to create a 2-way binding on the element.
We can however use the v-on (@) directive to register a handler for when the user chooses a file.  For example, below we registers a function named loadPreviewImg which is executed when the user selects a file.
<input type="file" accept="image/png, image/jpeg" @change="loadPreviewImg">

BONUS SECTION

In this section we show how to create an <img> using a file that a user chose from a file <input> element.
First, we assume there exists an input element in the HTML with an id set to “preview-img” like below.
<img id="preview-img">
We also assume that the file <input> element has the loadPreviewImg function registered for the onchange event as shown in the previous section.
The loadPreviewImg function is shown below.  The function first creates a new FileReader object.  The FileReader object will allow us to read the contents of the file that the user selected.  We next register a handler for the FileReader’s loadend event.  This will be called when a FileReader is finished reading a file.  Inside that handler we get the img element from the DOM and set its src attribute to the value of the result property of the reader.  Last, after we define the onloadend handler, we call the FileReader’s readAsDataURL method, passing to it the File object that was passed into the loadPreviewImg function on the event.
function loadPreviewImg(event) {
  let reader = new FileReader()

  reader.onloadend = function () {
    let img = document.querySelector("#preview-img")
    img.src = reader.result;
  }

  reader.readAsDataURL(event.target.files[0])
}

© 2023, Eric.