How to use Vue.js in eXo Platform
It’s pretty hard to keep up with all the new JavaScript libraries and frameworks. Three years ago, eXo introduced AngularJS to boost front-end development. Now there are add-ons to show how we can use eXo Platform with AngularJS, like the staging extension. There has also been an integration with ReactJS.
In this blog post, I’d like to share my experience using VueJS with eXo as a front-end solution. You’ll learn how to build a simple Vue.js portlet with eXo back end.
You can find the sample code base in GitHub. This is a Maven-based project, so it should be easy to import and run as-is.
Why Vue.js?
Well, simply because until now eXo hasn’t provided a ready-to-use sample with the current JavaScript trend known as Vue.js.
No, seriously, of all the frameworks and libraries I’ve worked with over the years, one thing about Vue.js that stands out for me is the documentation. It’s very detailed and easy to follow, covering multiple-use cases and all the various options, where applicable, for each of its built-in methods.
I decided to dive into Vue.js because a lot of good things are being said about it on the web (perfs, components, productivity, etc.).
Let’s start with the basics
AngularJS developers and ReactJS developers will find that Vue.js looks like those frameworks (like AngularJS in terms of data binding and ReactJS in terms of directives, components and props).
I won’t describe in detail how Vue.js works under the hood – that’s not the goal here. Instead, I’ll try to give an overview of some basic concepts.
Vue.js uses a template file containing both the template body (HTML tags) and a script section that acts as a controller. The following example is a simple template to give you an idea of how things are structured.
<div id="app"> {{text}} </div> <script> var app = new Vue({ el: '#app', data: { text: 'Hello eXo' }, created() { // API/Service calls would go here return { data: [ {}, {} ], }; }, methods: { search() { // Search method written here }, } }) </script>
The data() function is used to set up default data for the initial rendering of the component, and data binding within the View side is provided by the mustache pattern : {{}} or property v-html.
The created() function is similar to Reacts componentWillMount life cycle method and is the best place to perform the init process before the rendering phase.
The methods() function is where you define functions that will be used next in the View side, following the pattern @{DomEvent}=”youMethod”.
This is just a taste of what’s possible with Vue.js. You can see more examples in the Vue.js documentation.
Vue.js app within eXo Platform
To show you how to integrate Vue.js into the eXo stack, I decided to develop a small eXo add-on (a web app) to list all commits performed within a GitHub project.
Before we can start building our app based on Vue.js, we need to set up a few things:
- Maven 3: configured and installed to build and package the add-on
- JDK 8: installed
- your favorite IDE (can be IntelliJ CE, Eclipse or even Visual Studio): installed
- eXo Platform server (community or enterprise) 5.0
Develop an eXo add-on
eXo add-ons are mostly web apps that we deploy on top of eXo Platform to bring new capabilities to the platform or to customise some default configuration. To find out more, read the eXo documentation on how to create a portal extension.
In the current example, I’ll use an eXo extension with the following structure:
- services: Maven module to hold your API (a jar)
- portlets: Maven module to hold your portlets (web app)
- extension: Maven module to hold an eXo configuration (web app)
- packaging: Maven module to generate an add-on (zip file)
I’ll skip all the details of how to configure the extension – you can find these in the eXo documentation. However, to display the GitHub integration app within the home page, I used an eXo dynamic container, which allows us to display the portlet on the homepage, as explained in the official documentation.
Develop a portlet
We’ll focus on the integration of Vue.js 2.0. We assume our readers have played around with Vue.js and know the basics of the eXo ecosystem.
Wait, what is an eXo portlet?
A portlet is a web app that delivers its own resource files within a portal context. eXo Platform allows developers to create and deploy their portlets in different flavours:
- standard portlet API: how to create a standard portlet
- Juzu
- Spring MVC Framework
- other possibilities
For this example, I chose the Juzu framework as an MVC solution.
About the Juzu framework
Juzu is a web framework based on MVC concepts for developing apps. We’ve chosen Juzu because we already have a set of Juzu apps deployed within eXo out of the box, but you don’t need to use Juzu for this integration. You should be able to easily adapt this tutorial to your favourite web framework as it allows you to separate the front-end and server-side development. Our example uses Vue.js as a client-side framework and Juzu as an MVC framework.
The aim here isn’t to describe Juzu. If you want to master Juzu, we have a guide for that. Our goal is to explain the required steps for developers to integrate Vue.js into the Juzu portlet.
Vue.js library
Vue.js claims to be a progressive JavaScript framework. Though the core library of Vue.js is quite lightweight, developers have available a large ecosystem of tools and libraries to enhance it.
Installing Vue.js can be done in many ways:
- CDN: <script src=”https://cdn.jsdelivr.net/npm/vue@2.5.15/dist/vue.js”></script>
- NPN: outside the scope of this post
- Vue-cli: outside the scope of this post
In our case, we installed Vue.js as an AMD module using the latest stable version.
Maven module
You can create the project described above or you can use this example.
How to configure JavaScript modules
The AMD concept
AMD (or asynchronous module definition) is a JavaScript pattern that defines the way a library is loaded as a module, as opposed to a global object, that is available only to another module that ‘requires’ it. AMD is often used in eXo to improve client-side perfs. Read more about AMD in this document.
Use Vue.js as a module
To use Vue.js library, developers have to add the following configuration to gatein-resources.xml:
<module> <name>vuejs</name> <as>vuejs</as> <script> <path>/javascript/vue.js</path> </script> </module>
Easy, isn’t it?
Add a Vue.js module as a dependency to your portlet
As seen above, Vue.js is already configured as a module within eXo Platform. To use it in an app, developers need to declare it as a dependency in the same gatein-resources.xml:
<portlet> <name>GithubIntegrationControllerApplication</name> <module> <depends> <module>githubIntegration</module> </depends> </module> </portlet> <module> <name>githubIntegration</name> <script> <path>/javascript/github.js</path> </script> <depends> <module>vuejs</module> </depends> <depends> <module>juzu-ajax</module> </depends> <depends> <module>jquery</module> <as>jQuery</as> </depends> </module>
As you may have noticed, the configuration above brings a set of javascript libraries and injects them as AMD within our application when it is rendered by the portal:
- juzu-ajax : Module provided out of the box by Juzu Framework to enable communication between client side (javascript) and server side (business layer)
- jquery : Module provided out of the box by portal project to enable jquery framework
Vue.js in action
Using Vue.js within your code implies managing a Vue object instance through several phases, including observing data, initialising events and rendering. You can register life cycle hooks that will be employed in specific phases.
In the next example, we will use Vue.js to monitor a Git repository and then display the five most recent commits for each branch.
(function ($) { var apiURL = 'https://api.github.com/repos/kmenzli/sphinx-poc/commits?per_page=3&sha=' /** * Actual demo */ var demo = new Vue({ el: '#github', data: { branches: ['master', 'stable/4.4.x'], currentBranch: 'master', commits: null }, created: function () { this.usingJuzu() }, watch: { currentBranch: 'usingJuzu' }, filters: { truncate: function (v) { var newline = v.indexOf('\n') return newline > 0 ? v.slice(0, newline) : v }, formatDate: function (v) { return v.replace(/T|Z/g, ' ') } }, methods: { fetchData: function () { var xhr = new XMLHttpRequest() var self = this xhr.open('GET', apiURL + self.currentBranch) xhr.onload = function () { self.commits = JSON.parse(xhr.responseText) console.log(self.commits[0].html_url) } xhr.send() }, usingJuzu: function () { var $githubDiv = $("#github"); var createURL = $githubDiv.jzURL("GithubIntegrationController.create"); $.ajax({ type: 'POST', url: createURL, success: function (data) { // Reload project tree; this.commits = [] console.log(data.id); }, error: function (xhr) { if (xhr.status >= 400) { console.log(xhr.responseText); } else { alert('error while create new project. Please try again.'); } } }); } } }) })(jQuery);
Note: developer should adapt the javascript variable apiURL to his own github repository
Server-side rendering
The code for the main page is in the index.gtmpl file in the templates folder of the portlet. Here it is:
<div id="github"> <div class="UIGadgetThemes uiBox uiGithubIntegration"> <h6 class="gadgetTitle title center">Github Commit's history</h6> <div class="content row-fluid"> <template v-for="branch in branches"> <span class="uiRadio"> <input type="radio" :id="branch" :value="branch" name="optionsRadios" v-model="currentBranch"> <span></span> <label class="radioLabel" :for="branch">{{ branch }}</label> </span> </template> <div class="clearfix" /> <p> <span>Current branch : </span> <strong>{{ currentBranch }}</strong> </p> <div class="clearfix" /> <div class="list-group" v-for="record in commits"> <a :href="record.html_url" class="list-group-item active"> <h4 class="list-group-item-heading">{{ record.sha.slice(0, 7) }} - {{ record.commit.message | truncate }}</h4> <p class="list-group-item-text"> <span class="author"> <a :href="record.author.html_url" target="_blank">{{ record.commit.author.name }}</a> </span> at <span class="date">{{ record.commit.author.date | formatDate }}</span> </p> </a> </div> </div> </div> </div>
When the browser creates a Vue instance represented by the div with the GitHub identifier, it loads the HTML and performs the binding between the model and the view.
Bravo! You’ve successfully bootstrapped your own Vue.js app. Keep at it!
Deploying your add-on
Build process
The first step is to build your project, which means generating required artefacts to roll up into the eXo server. Building an eXo add-on is pretty simple. From the root folder, build your project by running the following Maven command line:
mvn clean install
The expected output is a zip file called github-integration.zip:
Deploy artefacts into the eXo server
Once the build process is complete, you need to deploy all the generated artefacts in the corresponding folders of the eXo Tomcat server. The first step is to unpack github-integration.zip somewhere on your file system, then copy and paste its contents into the following location:
- github-integration/lib into eXoPlatform/lib
- github-integration/webapps into eXoPlatform/webapps
Running your add-on
Start the server by running the following command line on the root folder of your eXo Platform instance (depending on your operating system):
./start_eXo.sh
Playing around
- Connect as a standard user.
- At the bottom of the home page, you’ll see the github-integration app.
- You can switch between branches and list commits by branch.
In upcoming posts, I’ll show you how to use Vue.js life cycles to build more reactive portlets – so stay tuned.