Skip to content

6. project [vuejs-04]: directives [v-model, v-bind], computed properties, input form

The directory structure of the [vuejs-04] project is as follows:

Image

6.1. The main script [main.js]

It is the same as in the previous example.

6.2. The main component [App]

The code for [App.vue] is as follows:


<template>
  <b-container>
    <b-card>
      <!-- introductory message -->
      <b-row>
        <b-col cols="8">
          <b-alert show variant="success" align="center">
            <h4>[vuejs-04]: [v-model, v-bind] directives, computed properties, input form</h4>
          </b-alert>
        </b-col>
      </b-row>
      <Form />
    </b-card>
  </b-container>
</template>


<script>
  import Form from "./components/Form.vue";

  export default {
    name: "app",
    components: {
      Form
    }
  };
</script>

The [App.vue] component uses the new [Form] component (lines 12, 19, 24).

6.3. The [Form] component

The code for the [Form] component is as follows:


<template>
  <div>
    <!-- form -->
    <b-form>
      <!-- form elements -->
      <b-row>
        <b-col cols="4">
          <b-card bg-variant="light">
            <b-form-group label="Number of dependent children" label-for="children">
              <b-input type="text"
                       id="children"
                       placeholder="Enter the number of children"
                       v-model="children"
                       v-bind:state="validChildren" />
              <b-form-invalid-feedback :state="childrenValid">You must enter a positive number or zero</b-form-invalid-feedback>
            </b-form-group>
            <!-- button -->
            <b-button variant="primary" :disabled="formInvalid" @click="doSomething">Submit</b-button>
          </b-card>
        </b-col>
      </b-row>
    </b-form>
    <b-row class="mt-3">
      <b-col cols="4">
        <b-alert show variant="warning" align="center">
          <h4>children= {{children}}</h4>
        </b-alert>
      </b-col>
    </b-row>
  </div>
</template>

<!-- script -->
<script>
  export default {
    // name
    name: "Form",
    // static component attributes
    data() {
      return {
        // number of children
        children: ""
      };
    },

    // computed properties
    computed: {
      // [formInvalid] property
      formInvalid() {
        return !this.validChildren;
      },
      // attribute [childrenInvalid]
      childrenValid() {
        return Boolean(this.children.match(/^\s*\d+\s*$/));
      }
    },
    // methods
    methods: {
      doSomething() {
        // the number of children is known when validation occurs
        alert("Number of children: " + this.children);
      }
    }
  };
</script>

Visual output

Image

Image

Comments

  • lines 4–32: the <b-form> tag introduces a Bootstrap form;
  • line 6: the <b-row> tag introduces a row in the form;
  • line 7: the <b-col cols=’4’> tag introduces a column spanning 4 Bootstrap columns;
  • line 8: the <b-card> tag [6] introduces a Bootstrap card, an area framed by a border;
  • line 9: the <b-form-group> tag adds a group of form elements linked to each other. Here, a label (the [label] attribute) [1] is linked to an input field (the [label-for] attribute). The value of [label-for] is the value of the [id] field (line 12) of the input field;
  • lines 10–14: the <b-input> tag [2] introduces an input field:
    • line 10: [type='text'] indicates that text can be typed into the input field. We could have written [type='number'] with constraints [min='val1' max='val2' step='val3'] since we expect a number of children. We used [type='text'] to demonstrate how to validate an input;
    • line 12: the [placeholder] attribute [3] sets the message displayed in the input field until the user enters something;
    • line 13: the [v-model] directive bidirectionally binds the entered value to the [children] attribute, line 42, of the component:
      • when the entered value changes, the value of the [children] attribute also changes;
      • when the value of the [children] attribute changes, the entered value also changes, i.e., the content of [2] changes;
      • the important point to understand is that, thanks to the mechanism described above, when the user clicks the [Validate] button [5], the [children] attribute on line 42 takes on the value entered in [2];
    • Line 14: The [v-bind] directive establishes a binding between an attribute of the <b-input> tag—in this case, the [state] attribute—and a component attribute—in this case, [enfantsValide] on line 53. The [enfantsValide] attribute is unique in that it is a function that returns the attribute’s value. This type of attribute is called a computed attribute. Computed attributes are found in the [computed] property, line 47, of the component. Computed attributes are used in the same way as static attributes of the [data] property: On line 14, we do not write [v-bind:state=’enfantsValide()’] but [v-bind:state=’enfantsValide’], without the parentheses. Therefore, when reading the [template], it is impossible to distinguish a computed attribute from a static attribute. To do so, one must examine the component’s script code;
    • Line 14: The [state] attribute sets the valid/invalid status of the entered value: if [enfantsValide] returns [true], the entered value is considered valid; otherwise, it is considered invalid. The screenshot above shows the [b-input] component when the [enfantsValide] function returns [false];
    • Line 15: The <b-form-invalid-feedback> tag [4] displays a message when the input in [2] is invalid. Its attribute [:state='childrenValid'] is identical to the attribute [v-bind:state='childrenValid'] in line 14. The [v-bind] directive can be omitted, but the [:] symbol must be retained. The error message is therefore displayed when the [childrenValid] attribute is [false];
    • line 16: end of the <b-group> element group;
    • line 18: the [5] button that will validate the input:
      • it will be blue [variant='primary'];
      • [:disabled="formInvalide"]: the [disabled] attribute enables or disables the button. This attribute is bound (v-bind) to the computed attribute [formInvalide] on line 49;
      • [@click="doSomething"]: when the user clicks the button, the [doSomething] method on line 59 will be executed;
    • lines 19–22: closing the various open tags;
    • lines 23–29: a new line in the [template]. [class=’mt-3’] means [margin (m) top (t) equal to 3 spacers]. [spacer] is a Bootstrap spacing unit. This class creates the spacing [8] in the screenshot above. Without this class, area [7] is flush with areas [1-6];
    • line 24: a column spanning 4 Bootstrap columns;
    • lines 25–27: a [warning] alert displaying the value of the static [children] attribute (line 42). Since this attribute has a two-way binding with the input field, as soon as the user modifies it, the value displayed in the alert also changes;
    • lines 34–65: the component’s JavaScript code;
    • line 42: the component’s single static attribute;
    • lines 47–56: the component’s calculated attributes;
    • lines 53–55: the input is considered valid if it is a positive integer, optionally preceded or followed by spaces;
    • lines 49–51: the form is considered valid if the number of children entered is valid. Generally, a form has multiple fields and is considered valid if all of them are valid;
    • lines 58–63: the component’s methods that respond to its events. Here, there is only one event: the [click] on the button. We simply display the entered value to show that we have access to it;

6.4. Running the project

We modify the [package.json] file and run the project:

Image