Basics of Vue.js 2 - Directives, Template and Binding

Vue.js is a framework for building reactive user interfaces, focusing only on the view layer. Like React, Vue uses Virtual DOM to compute differences between the DOM elements and applies the minimal amount of DOM updates required. The reactive behaviour of Vue always keeps the data and the DOM in sync.


Note: Open your Web Console to tinker with the various example provided in this page.

1. Bootstrapping Vue Instance

Vue can be bootstrapped by creating a new Vue instance using the Vue() constructor and passing it a options object. The options object will contain options for data, element to work on, methods and lifecycle methods among others.

Example
var vm = new Vue({
// options
});

It is possible to extend the Vue constructor to create reusable components with pre-defined options.

var headerComponent = Vue.extend({
  // options
});

Although Vue can be extended, it recommended to compose Vue as custom elements called Components, which in turn are essentially extended Vue instances. The Component system will be discussed in later posts.

1.1 data Option

The data option is a object whose properties are,

reactive - Which means that any changes to the data properties trigger an view update if needed

and proxied - So any properties added to the data object after the instance has been created will not be reactive and hence will not trigger any view update.

Example
// Data Option Example
var postData = {
  title: "This is a sample post title",
  desc: "This is a sample post description"
};

var post = new Vue({
  data: postData
});

// instance is created and postData is proxied
console.log(post.title === postData.title); // true

postData.title = 'This is a updated title';

console.log(post.title === postData.title); // true, reactive

postData.createdAt = new Date(); // new property

console.log(post.createdAt === postData.createdAt); // false, not proxied

As seen in the above example, only the proxied data are reactive and properties added after instantiation is not reactive. The example can be checked in the broswer console.

1.2 Vue instance properties and methods

Along with the data properties, Vue also exposes a set of useful properties and methods. These properties and methods are prefixed with the $ symbol to differentiate them from the proxied data properties.

Example
// 1.2 Properties and methods example

var helpersData = {
  age: 25
};

var helpers = new Vue({
  // Id of the root element with the template for Vue to parse.
  el: '#helpers',
  data: helpersData
});

console.log('Vue instance properties and methods');

// few Helper properties
console.log(helpers.$data === helpersData);
console.log(helpers.$el === document.getElementById('helpers'));

// one of the Helper methods
helpers.$watch('age', function (oldAge, newAge) {
  // this callback will be called when the data.age changes
  console.log('Old age is - ' + oldAge + ', new age is - ' + newAge);
});

2. Vue Template

Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. Under the hood, Vue compiles the templates into Virtual DOM render functions. Combined with the reactivity system, Vue is able to intelligently figure out the minimal amount of components to re-render and apply the minimal amount of DOM manipulations when the app state changes.

Official Doc

2.1 Text Interpolations

Text interpolation in Vue template is done using the double curly braces {{ }} syntax.

Example
<div id="textInterpolation">
  <p>Hi {{name}}, welcome to Vue Template!!</p>
</div>

var textInterpolation = new Vue({
  el: '#textInterpolation',
  data: {
    // Will be replaced in the rendered HTML
    name: 'codedodle'
  }
});

Output

Hi {{name}}, welcome to Vue Template!!

In the example above, the name property will be updated in the view(replacing the {{name}}) once it is changed in the Vue instance data.

Try: You could give it a try by opening the console right now and changing the value of textInterpolation.name to something else.

Along with replacing just basic data types, Vue also supports parsing JavaScript Expression inside the text data binding and some Vue based expressions.

Example
{{ true ? 'Success' : 'Failure' }}

{{ name.reverse() }}

Note that only expressions are supported with this syntax, statements like variable declaration or flow controls are not supported.

2.2 Vue Directives

Directives are special attributes with the v- prefix. Directive attribute values are expected to be a single JavaScript expression. A directive’s job is to reactively apply side effects to the DOM when the value of its expression changes.

Official Doc

Vue uses these directives to identify and modify the DOM element(s) based on the directive specified.

2.2.1 Directive Syntax

Vue Directive Syntax

The directive name specifies what the directive will do. Followed by the directive name after the colon (:) is the argument for the directive, which informs Vue on which attribute of the element to bind to. The value of the attribute might be a data property or a single JavaScript expression.

2.2.2 v-bind and v-on directive

v-bind

The v-bind directive is used to bind HTML attributes to the data properties of the Vue instance.

Example
<div id="directiveArg">
  <a v-bind:href="url" target="_blank">Click to visit - {{ url }}</a>
</div>

var directiveArg = new Vue({
  el: '#directiveArg',
  data: {
    url: "http://www.demos.codedodle.com"
  }
});

Output

As shown above, the href attribute of the a element is reactively bound to the url data property. Any update to the url property will trigger a update in the view.

v-on

Another one of the directive is the v-on , which is used to listen to DOM events.

Example
<div id="directiveEvents">
  <button v-on:click="showAlert">Show alert</button>
</div>

var directiveEvents = new Vue({
  el: '#directiveEvents',
  methods: {
    showAlert: function (event) {
      alert('The button has been clicked');
    }
  }
});

Output

Apart from the v-bind and v-on directives, there are lot more directives available in Vue to construct the view.

3. Computed Properties

In template expressions, computed properties can be used in places where too much logic is required to display the data. computed is one of the options that can be passed while creating the Vue instance.

For example,

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

The above code displays the reverse of the message data property, but it takes us a second to understand what the code does. Putting too much logic in template expression makes the template much complicated. In such cases we can use computed properties.

The above example can be rewritten using computed properties as below.

<div id="computedProps">
  <span>Message: </span><input type="text" v-model="message" />
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

var computedProps = new Vue({
  el: '#computedProps',
  data: {
    message: 'Hello'
  },
  computed: {
    // a computed getter
    reversedMessage: function () {
      // `this` points to the vm instance
      return this.message.split('').reverse().join('')
    }
  }
});

Output
Message:

Original message: "{{ message }}"

Computed reversed message: "{{ reversedMessage }}"

While parsing the template, Vue invokes the reversedMessage function as a getter and replaces the value returned by the method in the View.

Computed property is reactive, so any changes to the message data that the computed property is depending on will trigger a view updated to the computed property reversedMessage as well. If you type any new value in the message input field, the message property will change, so does the reversedMessage.

3.1 Computed Setter

By default the computed property is just a getter. Any access to that computed property will invoke the method as a getter. We can also provide a setter which can be invoked when a new value for the computed property is assigned.

Example
<div id="computedSetter">
  <p>{{fullName}}</p>
</div>

var computedSetter = new Vue({
  el: '#computedSetter',
  data: {
    firstName: 'code',
    lastName: 'dodle'
  },
  computed: {
    fullName: {
      // getter
      get: function () {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set: function (newValue) {
        var names = newValue.split(' ')
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
      }
    }
  }
});

Output

{{fullName}}

If we assign a new value for computedSetter.fullName = 'John Scully', then the value of firstName and lastName will be updated accordingly.

3.2 Caching Computed

Instead of using the computed property, the same can be achieved using the methods.

Example
<p>Reversed message: "{{ reverseMessage() }}”</p>

// in component
methods: {
  reverseMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

However, the difference is that computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed. This means as long as message has not changed, multiple access to the reversedMessage computed property will immediately return the previously computed result without having to run the function again.

Official Doc

This means that the following will not be reactive, because Date.now() is not a reactive dependency.

computed: {
  now: function () {
    return Date.now()
  }
}

Why caching ?

Caching is useful when there is a lot of expensive computation to be done to determine the value of a property and you don’t have to do it again and again when the value is cached. Frequent request to the same property will return the cached value until the dependencies of the property changes.

4. Watchers

Vue provides a watch option which can be used to react to data changes on a particular element. This is most useful when performing asynchronous and expensive operations in response to data changes.

Following is a official example for the watch feature with a tiny modification.

<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
  <p v-if="answerImage !== ''">
    <img v-bind:src="answerImage" />
  </p>
</div>

The v-model is one of Vue’s directives used to bind input element values to the data properties. The value of the input element will be bound to the data property assigned.

var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!',
    answerImage: ''
  },
  watch: {
    // whenever question changes, this function will run
    question: function (newQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.getAnswer()
    }
  },
  methods: {
    // _.debounce is a function provided by lodash to limit how
    // often a particularly expensive operation can be run.
    // In this case, we want to limit how often we access
    // yesno.wtf/api, waiting until the user has completely
    // finished typing before making the ajax request. To learn
    // more about the _.debounce function (and its cousin
    // _.throttle), visit: https://lodash.com/docs#debounce
    getAnswer: _.debounce(
      function () {

        // Check if there is ? present in the question
        if (this.question.indexOf('?') === -1) {
          this.answer = 'Questions usually contain a question mark. ;-)'
          return
        }

        this.answer = 'Thinking...'

        var vm = this

        // Perform an Ajax request to the yesno.wtf endpoint
        axios.get('https://yesno.wtf/api')
          .then(function (response) {
          vm.answer = _.capitalize(response.data.answer);
          vm.answerImage = response.data.image;
        })
          .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
      },
      // This is the number of milliseconds we wait for the
      // user to stop typing.
      500
    )
  }
});

Output

Ask a yes/no question:

{{ answer }}

Above example shows how watch is used to show intermediate state until the asynchronous operation is completed.

5. Class and Style Binding

As seen in previous examples, v-bind can be used to create data binding between the element attributes and Vue data properties. Attributes like class and style require a lot of string manipulation when there is a specific modification needed. Operations like adding or removing class names to the class attribute, adding and modifying CSS style in the style attribute are cumbersome. Vue makes this a bliss by providing enhancement for the class and style attributes when used with v-bind.

5.1 Class Binding

With Vue, modifying the class list of an element is very easy. There are two ways the classes can be modified using Vue data properties.

5.1.1 Object Syntax

In Object syntax, a JavaScript Object is used to represent the class list. The properties of the object reflect the class name and the truthiness of their value determines if the class must be present in the class attribute.

Example
<div id="classBinding">
  <p v-bind:class="classObject">
    This is an example of class binding in Vue.js
  </p>
  <button v-on:click="switchClasses">Switch Classes</button>
</div>

var classBinding = new Vue({
  el: '#classBinding',
  data: {
    classObject: {
      'text-primary': true,
      'bg-primary': false
    }
  },
  methods: {
    switchClasses: function () {
      this.classObject['bg-primary'] = !this.classObject['bg-primary'];
      this.classObject['text-primary'] = !this.classObject['text-primary'];
    }
  }
});

Output

This is an example of class binding in Vue.js

In the above example, the classObject is the data property bound to the class attribute of the p element. On clicking the switch classes button, it toggles the presence of both the classes alternatively.

So initially, the element would render as below.

// ...
    <p class=“text-primary”>This is an example of class binding in Vue.js</p>
// ...

5.1.2 Array Syntax

Vue also offers an alternative Array syntax to adding and modifying classes in the class attribute. Using the Array syntax, we can list the array of classes required from the data property directly.

Example
<div id="classBindingArr">
  <p v-bind:class="[bg, text]">
    With both the classes
  </p>
  <p v-bind:class="[text, isActive ? 'bg-primary':'']">
    This is an example of class binding in Vue.js
  </p>
</div>

var classBindingArr = new Vue({
  el: '#classBindingArr',
  data: {
    isActive: false,
    text: 'text-primary',
    bg: 'bg-primary'
  }
});

Output

With both the classes

This is an example of class binding in Vue.js

The example shows two p elements. The first one contains both the bg and text properties. So the element class attribute will have both text-primary and bg-primary classes.

In the second element, the bg presence is based on the truthiness of the isActive data. If isActive is changed to true, then the bg-primary class will be added.

5.2 Inline Style Binding

5.2.1 Object Syntax

The object syntax for v-bind:style is pretty straightforward - it looks almost like CSS, except it’s a JavaScript object. You can use either camelCase or kebab-case (use quotes with kebab-case) for the CSS property names

Official Doc

5.2.2 Array Syntax

In Array syntax, multiple style objects can be passed in as an array and all the styles in the objects will be added.

Example
<div id="styleBinding">
  <!-- Object binding -->
  <p v-bind:style="{fontSize: fontSize + 'px'}">
    Inline style binding with data properties
  </p>
  <p v-bind:style="fontStyles">
    Inline style binding with data object property
  </p>
  <!-- Array binding -->
  <p v-bind:style="[fontStyles, bgStyles]">
    Inline style binding with Array of Style Objects
  </p>
</div>

var styleBinding = new Vue({
  el: '#styleBinding',
  data: {
    fontSize: 16,
    fontStyles: {
      fontSize: '14px',
      color: 'red'
    },
    bgStyles: {
      backgroundColor: 'grey'
    }
  }
});

Output

Inline style binding with data properties

Inline style binding with data object property

Inline style binding with Array of Style Objects

Above example shows the inline style implementation of both the Object syntax and Array syntax.