JBay Solutions Development Blog on Java, Android, Play2 and others
RSS RSS RSS RSS

Vue.js quick start guide

Vue.js (v1.0.x) quick start guide

NOTE: We plan to update this guide to Vue 2.0 when the final release is out

Like most software companies which create web applications, we develop frontends with HTML and Javascript.
Having mostly used jQuery on our applications, we felt there had to be an easier and less verbose way develop web frontends.

The problem is...there literally hundreds of javascript libraries/frameworks out there, with new showing up every day!

After lots of searching and reading, we settled on Vue.js. Although its relatively recent Vue.js seemed like a great choice.

Since the core library is only a view layer and doesn't force you to build a complete SPA (single page application), it means we could just start using it some components on our existing web applications.

Documentation

Vue provides a very good official documentation, which we recommend you start with.

Our Setup

On our apps, the most basic setup always includes Vue, vue-resource for http requests, and vue-validator for form validation

Since we are not using webpack or any other module bundler, we just import the js files using a script tag.

    <script src="javascripts/vue/vue.js"></script>
    <script src="javascripts/vue/vue-resource.js"></script>
    <script src="javascripts/vue/vue-validator.js"></script>

Debug and Devtools

When on the developing environment, we enable these Vue global configs:

     Vue.config.debug = true;
     Vue.config.devtools = true;

This will enable stack traces and allow using the vue-devtools browser extension.

Page example

On each page, we create a div template, like explained in the Vue guide:

    <div id="app">
        {{ message }}
    </div>

And on the js script loaded for that page:

    new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue.js!'
      }
    })

It's highly recommended to give a different id to each template, it's pretty common for builds to concat and uglify multiple js files into one.
If you have multiple templates with the same id, there will be conflicts when binding Vue instances to the templates.

v-cloak

We highly recommend including the v-cloak directive in every template. This way, uncompiled bindinds will not be diplayed while the page loads:

Just include this rule on your css:

    [v-cloak] {
      display: none;
    }

Using the same page as before:

    <div id="app" v-cloak>
        {{ message }}
    </div>

You can check the working sample here

vue-resource

vue-resource is a plugin for Vue that provides for making web requests and handle responses using a XMLHttpRequest or JSONP.

We use it extensively when using Vue, mostly for HTTP GET and POST requests. There are other methods and options available, for more details check the official vue-resource docs.

GET

This is a basic example of a GET request where we use the Github API to retrieve a list of JBay Solutions organization projects.

You can check the working sample here

     var url = "https://api.github.com/orgs/jbaysolutions/repos";

     // GET request
     this.$http.get(url).then(function (response) {
         this.loading = false;
         // success callback, response.data will contain the data in JSON format

         // get status
         response.status;
         // get status text
         response.statusText;

     }, function (response) {
         // error callback
     });

POST

This is a basic example of a POST request where we use the jsonplaceholder.typicode.com fake server to send some data.

You can check the working sample here

    var url = "http://jsonplaceholder.typicode.com/posts";

    var data = {
        title: "a title",
        body: "a body",
        userId: 1
    };

    // POST request
    this.$http.post(url, data).then(function (response) {
        // success callback

        // get status
        response.status;
        // get status text
        response.statusText;

    }, function (response) {
        // error callback
    });

You can also use POST to send forms or upload files using FormData. Details here

vue-validator

vue-validator is form validation component for Vue.js. It comes with a few built in validators:

  • required: whether the value has been specified
  • pattern: whether the pattern of the regular expression
  • minlength: whether the length of specified value is less than or equal minimum length
  • maxlength: whether the length of specified value is less more or equal maximum length
  • min: whether the specified numerical value is less than or equal minimum
  • max: whether the specified numerical value is more than or equal maximum

Adicional validators can be declared globally or locally on each Vue instance. A global email validator is one we use on most apps:

    Vue.validator('email', function (val) {
        return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(val);
    });

Local validators can be declared inside the validators object on a Vue instance.

The sample below uses both cases, a global email validator and a locally declared validator to check if both passwords match. For more details about custom validatores check the docs here

You can check the working sample here

Since we mostly use Bootstrap for our apps layout, the sample below has is built with conditions to display errors using help blocks.

    <validator name="sampleValidator">
        <form v-on:submit.prevent="saveUser" novalidate>
            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.firstName.invalid && $sampleValidator.firstName.touched}">
                <label for="firstName" class="control-label">First Name *</label>
                <input class="form-control" type="text" name="firstName" placeholder="" v-model="user.firstName" v-validate:first-name="{ required: true, maxlength: 50 }"/>
                <span class="help-block" v-show="$sampleValidator.firstName.touched && $sampleValidator.firstName.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.firstName.touched && $sampleValidator.firstName.maxlength">The max size is 50.</span>
            </div>

            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.lastName.invalid && $sampleValidator.lastName.touched}">
                <label for="lastName" class="control-label">Last Name *</label>
                <input class="form-control" type="text" name="lastName" placeholder="" v-model="user.lastName" v-validate:last-name="{ required: true, maxlength: 100 }"/>
                <span class="help-block" v-show="$sampleValidator.lastName.touched && $sampleValidator.lastName.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.lastName.touched && $sampleValidator.lastName.maxlength">The max size is 100.</span>
            </div>

            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.country.invalid && $sampleValidator.country.touched}">
                <label for="country" class="control-label">Country Code</label>
                <input class="form-control" type="text" name="country" placeholder="" v-model="user.country" v-validate:country="{ maxlength: 3 }"/>
                <span class="help-block" v-show="$sampleValidator.country.touched && $sampleValidator.country.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.country.touched && $sampleValidator.country.maxlength">The max size is 3.</span>
            </div>

            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.mail.invalid && $sampleValidator.mail.touched}">
                <label for="email" class="control-label">Email *</label>
                <input class="form-control" type="email" name="email" v-model="user.email" v-validate:mail="{ required: true, email: true }"/>
                <span class="help-block" v-show="$sampleValidator.mail.touched && $sampleValidator.mail.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.mail.touched && $sampleValidator.mail.email && !$sampleValidator.mail.required">The email address is not valid.</span>
            </div>

            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.password.invalid && $sampleValidator.password.touched}">
                <label for="password" class="control-label" >Password *:</label>
                <input type="password" class="form-control" name="password" v-model="user.password" v-validate:password="{ required: true, minlength: 6, maxlength: 10 }">
                <span class="help-block" v-show="$sampleValidator.password.touched && $sampleValidator.password.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.password.touched && $sampleValidator.password.minlength && !$sampleValidator.password.required">Please enter at least 6 characters.</span>
                <span class="help-block" v-show="$sampleValidator.password.touched && $sampleValidator.password.maxlength">The max size is 255.</span>
            </div>

            <div class="form-group" v-bind:class="{ 'has-error': $sampleValidator.confirmPassword.invalid && $sampleValidator.confirmPassword.touched}">
                <label for="confirmPassword" class="control-label" >Confirm password *:</label>
                <input type="password" class="form-control" name="confirmPassword" v-model="confirmPassword" v-validate:confirm-password="{ required: true, passwordValidator: { rule: user.password }}">
                <span class="help-block" v-show="$sampleValidator.confirmPassword.touched && $sampleValidator.confirmPassword.required">This field is required.</span>
                <span class="help-block" v-show="$sampleValidator.confirmPassword.touched && $sampleValidator.confirmPassword.passwordValidator && !$sampleValidator.confirmPassword.required">The passwords don't match.</span>
            </div>

            <button class="btn btn-primary" type="submit">Add</button>
        </form>
    </validator>

When the form is submited, we only save the user if the form is valid:

    saveUser: function () {
        this.$validate(true);
        if (this.$sampleValidator.valid) {
            // save the user
        }
    },

Our custom components

Below are the components we most commonly use on our apps:

Datepicker

This datepicker component is a wrapper for bootstrap-datepicker.
The lib js and css files need to be included in your html.

    <script src="js/bootstrap-datepicker.js" type="text/javascript"></script>
    <link rel="stylesheet" media="screen" href="css/bootstrap-datepicker3.css")">

Below is the full component source:

    var datepickerComponent = Vue.extend({
        template: '<div class="input-group date" v-el:inputgroup>' +
        '   <input type="text" class="form-control" v-model="value">' +
        '   <span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>' +
        '</div>',
        props: {
            value: ''
        },
        data: function () {
            return {};
        },
        watch: {
            value: function () {
                this.datepicker.datepicker("update", this.value);
            },
        },
        ready: function () {
            this.datepicker = $(this.$els.inputgroup).datepicker({
                format: 'yyyy-mm-dd',
                autoclose: true
            });
        }
    });

    Vue.component('datepicker', datepickerComponent);

And to use it:

    <datepicker :value.sync="myDate"/>

bootstrap-select

This select component is a wrapper for bootstrap-select

The lib js and css files need to be included in your html.

    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.11.2/js/bootstrap-select.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.11.2/css/bootstrap-select.min.css">

Below is the full component source:

    var bSelectComponent = Vue.extend({
        //v-el:select
        template: '<select v-el:select>' +
        '   <option v-for="item in items" value="{{item.value}}">{{item.text}}</option>' +
        '</select>',
        props: {
            value: "",
            items: [],
        },
        data: function () {
            return {};
        },
        watch: {
            items: function () {
                var select = $(this.$els.select);
                select.selectpicker("refresh");
                select.selectpicker("val", this.value);
            },
            value: function () {
                var select = $(this.$els.select);
                if (this.value !== select.selectpicker("val")) {
                    select.selectpicker("val", this.value);
                }
            }
        },
        ready: function () {
            var select = $(this.$els.select);
            select.selectpicker();
            select.selectpicker("val", this.value);
            var self = this;
            select.on("changed.bs.select", function () {
                self.value = select.selectpicker("val");
            });
        }
    });
    Vue.component('b-select', bSelectComponent);

And to use it:

    <b-select :value.sync="myValue" :items="itemList"/>

Where itemList is an array of objects:

[
    {
        "text": "Item 1",
        "value": "1"
    },
    {
        "text": "Item 2",
        "value": "2"
    },
    {
        "text": "Item 3",
        "value": "3"
    },

]

To enable any of the js component options, like live search, you can use it like this:

    <b-select :value.sync="myValue" :items="itemList" data-live-search="true"/>

vue-grid-layout

A draggable and resizable grid layout, for Vue.js.

We developed this component to be used primarily in our SaaS app Draxed.

Read all about it HERE.

vue-bootstrap-table

A sortable and searchable table, as a Vue component, using bootstrap styling.

We developed this component to be used primarily in our SaaS app Draxed.

Read all about it HERE.



comments powered by Disqus