One of the cornerstone events of the last Fast Track release (Fast Track 5.9.2) was the work of Thomas on the new JavaScript client. If you are a web developer, a top-notch new gen JavaScript killer, or simply someone who’s not into the JEE stack, you will love this client. It provides all the features of the Nuxeo Platform in an HTML page.
Nuxeo.js is a library available on GitHub that you can import inside your page to wrap Nuxeo Platform API calls. Using nuxeo.js, you can easily get a file, upload one, transform content to PDF, or from a jpg to a png, or even annotate pictures. You can perform full text search, create a version of a document, and much more!
Nuxeo.js is currently provided in two flavors: one that depends on jQuery, that you can include in your HTML pages, and one that you can “require” in a node.js application.
In this blog post, I will focus on the jQuery implementation. I have experimented with how we can leverage this nuxeo.js client, with the goal of having minimal technical set up requirements, in comparison to working with big JavaScript frameworks like Angular.js, Ember.js and so on. These are also a very interesting approaches (some projects have already started on Angular.js), but not what I talk about here.
The Functional Goal
I am implementing a small HTML page and some js scripts to display our customer references information, stored in our Nuxeo DM intranet. The goal is to help our sales teams share knowledge of their customers. Although very interesting, I won’t get into the business details today, this could be a later post. Let’s limit the context to the fact that there is a “Customer” document type that contains properties such as a description of the business of the customer, of the project, the time to go live, the level of customization, the main modules used, the competitors during the sales phase, etc…
My webpage is quite simple: it displays a list of all references, allows me to browse each of them, display the properties and double-click on a field to modify its value. I can also drop a picture in the screenshot field. Note that as my goal was primarily to explore various topics around using the js client, I haven’t yet implemented the complete user story, or the CSS design. This is more like a lab. Here is a one minute video that will make things more concrete for the rest of the post.
The Technical Set Up, the Development Flow and the Software Architecture
I have a folder with an index.html page, a library folder with the JavaScript dependencies, including nuxeo.js, and a script folder with my main script, customer.js. I also have a css folder for CSS, an image folder and a templates folder.
I develop using the no security mode of chrome: (On Mac
open /Applications/Google\ Chrome.app –args –disable-web-security) but could also have configured Nuxeo with CORS. I use Sublime Text as my text editor.
Once I am happy with the result, I zip my index.html file and its side folders and I drop it in Nuxeo. It is then available using the preview restlet: this is an often unknown “feature” of Nuxeo preview service — it unzips the zip to check if there is an index.html file, and then just serves the static website (you can right click in the preview popup to get the URL of the corresponding frame).
The Nuxeo client connection happens in the index.html on first load of the page. Then the customer.js file contains a set of functions for most of the user actions:
- Performs the necessary requests to Nuxeo Platform server,
- Renders data via templates using mustache.js, and
- Updates the initial web page dom using jQuery, to inject the result of the templating phase.
I use Bootstrap for the HTML content of the templates and the main index.html page. I also added a JSON array that contains useful definition data of all the form fields, so as to facilitate maintenance by centralizing the information and maximizing generic code. No doubt that in the future, this definition will be in Studio! ;-)
Where to Find nuxeo.js, the Nuxeo Platform JavaScript Client
Nuxeo.js is available on GitHub. It is currently in version 0.1, the 1.0 version is targeted for the next LTS. You just need to include it as a library on your page to start using it:
<script src="lib/nuxeo.js"></script>
I recommend you to read the available tests suites which are good practical documentation to understand how to use the nuxeo.js library. It uses the node.js implementation, but nothing changes in the syntax and objects used.
First Steps Using nuxeo.js
As I said, my web page first displays a list of customers. To start with, the client needs to be instantiated once in the HTML page:
var connectInfo={
baseURL:"http://localhost:8080/nuxeo"
}
var client = new nuxeo.Client(connectInfo);
Here I don’t need to pass credentials as I deploy the pages on the Nuxeo Platform (see the technical set up section of this post above) where users are already authenticated. But I could have added in the connectInfo object username and password (and soon we will add token based authentication schemes). Then I set the schemas (document properties) I want to fetch for all the documents I will get in my coming requests:
client.schemas(["dublincore",
"nuxeo_sales_info","nuxeo_customer_identification"]);
I am ready to use the client to start the dialog with the Nuxeo server! I wrap in the browseListOfCustomers() function the query to fetch the Customer objects:
client.operation("Document.Query").params({
query: "select * from Customer where ecm:currentLifeCycleState != 'deleted'"})
.execute(function(error, data) {
// In the callback function you implement what you want to do with the server response, once you received
// it. The "data" object is a JSON from our REST API.
//You can use console.log(data) for introspecting it,
//or browse nuxeo/api/v1/doc on a Nuxeo server for more details.
});
Another example fetches the vocabulary values. I wrap the call in a function as it is used multiple times:
function getVocabularyData(directoryName, callback){
client.operation("Directory.Entries")
.params({"directoryName":directoryName})
.execute(function(error,data){
callback(data)});
}
Getting a document once you have its id is also very simple:
client.document(customerId).fetch(function(error, data) {//callback
});
But my favorite one is definitely the file upload, that wraps the batch upload API in an elegant way. Here is the sequence, where file is a javascript file object:
importOp = client.operation('Blob.Attach').params({
document : currentDocId,
save : 'true',
xpath : 'npi:screenshot1'
})
importOp.uploader().uploadFile(file,null);
//if the operation I called accepted multiple blobs, I could have had multiple uploadFile calls here, before calling the execute method
importOp.uploader().execute(function(error, data){});
Neat right ?
Mustache: the Templating Framework, with JQuery Mustache plugin
Just before working on this example, I had quickly worked on the implementation of a roadmap vizualizer website (wait for next week’s blog to learn more!) where I built the HTML in the middle of my JavaScript functions, with string concatenations. Escaping all the html was a real nightmare, and it is difficult to maintain – you just don’t want to go back to it once you’ve finished. So this time, even if my initial goal was to stay simple, I wanted to deal with this problem. I had a look at several JavaScript templating frameworks, and Mustache seemed to me well documented and one of the most used. The principle is to provide a string that is the template and to have variables replaced in it (the following example is from Mustache documentation):
var view = {
title: "Joe",
calc: function () {
return 2 + 4;
}
};
var output = Mustache.render("{{title}} spends {{calc}}", view);
Having less concatenations to do already makes things cleaner, but you still have to handle all your template strings in some way, and if you leave them in JavaScript vars, you still have to handle the escaping. That’s where adding the JQuery Mustache plugin makes things magical: the template strings are stored in html files that the plugin makes easy to load and then reference in rendering executions.
In a template file, let’s say the customer.html, I have a series of <script> instructions that contains the template strings:
<script id="CustomerView">
My Template string {{value1}}
</script>
<script id="my2ndTemplateName">Another templatized string {{.}}</script>
Then, if I want to use a template string, let’s say my “CustomerView”, here is the script flow, where doc is the document backing the Customer data:
$.Mustache.load('./templates/customer.html').done(function(){
var content = $.Mustache.render('CustomerView, doc);
$('body').html(content);
What is interesting here is that I can easily organize my templates in separate HTML files where the graphic designer won’t be lost if he has to tune a few things! See listOfCustomers.html for a simple example, or the customer.html template to have a more complete one (with multiple templates inside one template file). One problem I had using Mustache.js was that templates were cached and cache was never cleared, so I could not see my modifications without clearing the whole browser cache. Thanks to this blogpost, I found a workaround playing with Chrome javascript console settings.
In the end, I have a template for the list of customers, one for viewing a customer, and then some small ones for switching a field from view mode to edit mode: on a dblclick event, I call switchToEdit(propertyId,value) that re-renders the zone with a template that contains the edit field (a select, a textarea, …). On the rendered edit block, I call on the onblur event, the switchRead function, that re-renders the view-only block.
Summary of Used Libraries
Finally, my script include sections contains:
<script src="lib/jquery-1.10.1.min.js"></script>
<script src="lib/mustache.js"></script>
<script src="lib/jquery.mustache.js"></script>
<script src="lib/nuxeo.js"></script>
<script src="lib/bootstrap.min.js"></script>
<script src="lib/dropzone.js"></script>
<script src="js/customer.js"></script>
Aside from customer.js, that’s all what you need to start!
More to Talk About, More to Implement!
Following this post I will continue to cover this example. I plan to:
- Play with dropzone.js for uploading files in Nuxeo. For curious people, there is already an intitial integration in the code base, but there are more interesting things to do around it.
- Describe a way of leveraging blobs in the html page that come from automation requests (pdf transformations, data merging with office files, image resizing…). A small improvement will be added for this in an upcoming Fast Track.
- See the various options for authentication. (Actually we need to wait for more work to be achieved on the client for that one).
Looking forward to hearing about your first use cases!
The post A Content App Using Mustache, Bootstrap and nuxeo.js appeared first on Nuxeo Blogs.