Site icon Experiences Unlimited

Moving From Mustache.js and jQuery to Vuejs for Client Side View Management Reactively

Introduction

A month ago I wrote a post on using Mustache.js for client-side templating. I am pretty excited about the way we use Mustache.js, jQuery Ajax Client to build views. Its simple and does the work for us.

But? it’s not reactive!, we manage a lot of DOM manipulation which can often lead to a mess. And for someone reading the code for the first time, it would take a while to understand the flow.

What if we could use a reactive javascript framework? Or How about AngularJS?

Javascript Framework Choice

There are lots of Mustache templates lying around, we want to be able to reuse them and not just rework! Our first thoughts were to adopt Angular JS 1 or 2 but the way it changed drastically from 1 -> 2 -> 4 and the new TypeScript in ng4 just made us drop the idea.

I then played around with ReactJS in a workshop, it was amazing! But the workshop was Node.js based! So I didn’t exactly know how it would all fit in our maven-based Java applications. Then there was JSX which means our Mustache templates cannot be reused!

At a training on Elasticsearch, a fellow developer from Shanghai, China (of course he would know a hot framework in their region) introduced me to Vuejs, it was during the time Vuejs 2.0 was around the corner. And then I tried it out only recently and I was impressed, especially by the standalone version which makes it extremely easy to adopt in our maven-based setup while reusing our Mustache.js templates.

Enough of talk and let me show you how I easily moved my Mustache sample demonstrated here to Vuejs based.

Vuejs Templates

Below is the part of the code from the HTML which has Vuejs related directives and the Mustache like template markup:

<div class="container">
    <div class="page-header"><h1>Vuejs Demo</h1></div>
    <div class="row">
        <div class="col-md-offset-3 col-md-6">
            <h3>Select user</h3>
            <select id="git-users" class="form-control" v-model="gituser">
                <option value=""></option>
                <option value="facebook">Facebook</option>
                <option value="google">Google</option>
                <option value="netflix">Netflix</option>
                <option value="flipkart">Flipkart</option>
                <option value="amzn">Amazon</option>
                <option value="microsoft">Microsoft</option>
            </select>
        </div>
    </div>
    <div class="space"></div>
    <div class="row" v-cloak>
        <div class="col-md-offset-3 col-md-6">
            <div class="alert alert-info center" v-if="!userSelected">
                <i class="fa fa-exclamation-triangle"></i> Select a GitHub User
            </div>
            <div v-if="userSelected">
                <div class="center" >
                        
                    <div class="alert alert-info" v-if="loadingDetail">
                        <i class="fa fa-spinner fa-spin"></i> Loading Detail...
                    </div>
                    <div  v-if="!loadingDetail">
                        <img :src="detail.avatar_url" class="git-user-avatar" />
                        <div class="space"></div>
                        Github User <a :href="detail.html_url">{{detail.login}}</a>
                        <div class="space"></div>
                        Name <a :href="detail.blog">{{detail.name}}</a>
                        <div v-if="isBio" class="space">
                            {{detail.bio}}
                        </div>
                    </div>
                </div>
                <div class="space"></div>
                <div>
                    <div class="alert alert-info" v-if="loadingRepos">
                        <i class="fa fa-spinner fa-spin"></i> Loading Repos...
                    </div>
                    <ul class="list-group" v-if="!loadingRepos">
                        <li class="list-group-item" v-for="item in repos.items">
                            <span class="badge">
                                <i class="fa fa-star git-stars"></i> 
                                {{item.stargazers_count}}
                            </span>
                            <a :href="item.html_url">{{item.full_name}}</a>
                        </li>
                    </ul>
                </div>

            </div>
        </div>
        
    </div>
</div>

Following are the transformations I applied to my Mustache templates:

Vuejs State

I carefully thought about the states the UI can go through which is:

So I declared properties for each of the above scenarios into my data object to initialize my Vue object as shown below:

vm = new Vue({
    el: ".container",
    data: {
        gituser: "",
        loadingDetail: false,
        loadingRepos: false,
        detail: {},
        repos: {}
    },
    computed: {
        userSelected: function(){
            return this.gituser != '';
        },
        isBio: function(){
            return this.detail.bio != '' 
                && this.detail.bio != null;
        }
    },
    watch:{
        gituser: function(){
            if ( this.gituser){
                getUserDetail(this.gituser);
                getMostStarredRepos(this.gituser);
            }
        }
    }
});

In the above code:

Vuejs State Management

I have bound the value of the selection to the gituser property of the data object using v-model. This is a two way binding which means when the value in the selection is changed, it automatically updates the gituser property and which in turn triggers the watcher method attached to the property.

In the watcher method, we invoke the RESTful APIs using jQuery’s Ajax API. There is no official recommendation for accessing remote services in Vuejs, so I thought of sticking with what I already have. But the methods which invoke the APIs are very simple and just manipulate the Vuejs state and not the DOM as shown below:

function getUserDetail(username){
    vm.loadingDetail = true;
    $.getJSON('https://api.github.com/users/'+username, function(data){
        vm.detail = data;
        vm.loadingDetail = false;
    });
}

function getMostStarredRepos(username){
    vm.loadingRepos = true;
    $.getJSON('https://api.github.com/search/repositories?q=user:'
        +username+'&sort=stars&per_page=10', function(data){
        vm.repos = data;
        vm.loadingRepos = false;
    });
}

So whenever the state i.e properties defined in the data object changes, Vue updates its Virtual DOM and thus updates the view.

Once you have the code running in the browser, you can open the console and try these:

//this will load the Vuejs detail
vm.gituser = "vuejs"
//will load a message
vm.gituser = ""

Conclusion

You can find the complete Vuejs sample here and can also compare with the Mustache.js version available here.
I have tried to use a standalone version of Vuejs (i.e by including Vue.js source via the script tags) to create a reactive application reusing much of the infrastructure I had for Mustache.js.

There are few places where I can improve like:

I will cover these in subsequent posts. For now, this is an amazing improvement from Mustache.js. I also have planned few posts around using standalone Vuejs in a Spring Boot based application which is much more interesting, but that’s for sometime in future.

PS: I am not comparing Vuejs with Mustache.js instead it’s about moving from Mustache.js based client templates to a reactive UI leveraging most of the Mustache work!

Exit mobile version