Most of you must already be familiar with the Nuxeo REST API, and Automation API and I am sure you must have already tried our Workflow Engine in Studio. Today, I will show you how you can put all of these in a magic cauldron and transform them to a simple and great looking single page application using Polymer with Material Design. Let’s get started!
Start the Polymer Application
Generate the Skeleton
Yeoman helps you to kickstart new projects, prescribing best practices and tools to help you stay productive.
It sounds like something that could be useful to start with. So let’s use it to generate the Polymer boilerplate.
$ brew install node $ npm install -g yo generator-polymer
Then generate the project:
$ yo polymer
Now let the magic happen, aka install all npm and Bower dependencies and start our newly generated application:
$ cd app $ gulp serve
TA DA! You can now start customizing your new Polymer-based application.
Proxying the Nuxeo Platform
As our target application will be hosted inside a Nuxeo Platform server, while developing we need to simulate the environment and have a running Nuxeo Platform behind /nuxeo path on the development server.
To do so, we will tweak the gulpfile.js a little to create a proxy while serving the files:
Install proxy-middleware module and save it as a dev dependency:
$ npm install --save-dev proxy-middleware
Locate the serve task in gulpfile.js, and add the following lines:
// Also see https://github.com/nuxeo/nuxeo-timeoff/blob/96d09c478bef6da38c27ae80d59dfbb877851ae7/nuxeo-timeoff-web/gulpfile.js#L255-L256 // setup our local proxy var proxyOptions = require('url').parse('http://localhost:8080/nuxeo'); proxyOptions.route = '/nuxeo';
Add the proxy to the server middlewares:
// Also see https://github.com/nuxeo/nuxeo-timeoff/blob/96d09c478bef6da38c27ae80d59dfbb877851ae7/nuxeo-timeoff-web/gulpfile.js#L276 middleware: [historyApiFallback(), require('proxy-middleware')(proxyOptions)],
Start the Nuxeo Platform (on http://localhost:8080/nuxeo), and execute a gulp serve to start your application (on ‘http://localhost:5000/'). You should be able to access the Nuxeo Platform with (‘http://localhost:5000/nuxeo').
Querying the Workflow Endpoint
Now that we have a working Polymer application, we want to interact with the Nuxeo Platform and the workflow engine.
Working up the Magic
$ bower install --save nuxeo/nuxeo-elements
Add the element list to the app/elements/elements.html like we did in our sample app. We can now add components to call Operation, REST endpoint, Workflow, etc.
It’s always easier to explain a trick using a real use case and I have just the right one for you! Open the timeoff-pending.html web component and I’ll walk you through the steps.
The target we have is an application running inside the Nuxeo Platform, which means that besides our Authentication filter, we can assume that the current user is already authenticated with his HTTP session. So, no need to worry about that in this configuration.
Here’s a sample from the sources:
<nuxeo-resource id="nxoPending" auto path="workflow" response=></nuxeo-resource>
This element defines a request to the workflow endpoint with id nxoPending, which binds his response to the data property. It is executed automatically when the component is loaded. In the Nuxeo playground workflow endpoint documentation, you can see that the data property contains all workflow instances launched by the current user.
Looping Through the Data Property
Here’s another sample from the sources. Using the property binding, the template element will be rendered when data contains something and loop over it:
<template id="tmpl_entries" is="dom-repeat" items='[[data.entries]]' as="item" filter="timeOffWkf"> <div class="request flex"> <timeoff-entry data$=[[item]] id="entry_[[item.id]]"></timeoff-entry> <div class="actions"> <button-edit-workflow class="button" on-edited="refreshTasks" workflow="[[item]]"></button-edit-workflow> <button-delete-workflow class="button" on-deleted="refresh" workflow-id="[[item.id]]"></button-delete-workflow> </div> </div> </template>
For each entry, a timeoff-entry component is rendered, which displays the state of the current workflow. And, as you can see, we are using the same principle to handle edit-workflow and delete-workflow actions bound to the current workflow.
Note that we do not want to display all the workflows, and we use a dom-repeat attribute filter which will filter the displayed item.
This is how you can use a component to request a workflow endpoint to have all the current user instances, filter them and display them using an other component. It’s kind of magical, isn’t it?
Something More to Try out
If you want to do more with the workflow API, like validating or rejecting a task, you can look at our timeoff-pending-task component and more precisely the timeoff-accept-task or the timeoff-reject-task. To go further, clone our sample application nuxeo-timeoff which contains Task validation, Workflow creation, Content Enricher, and more.
Let me show you some more tricks that might be useful as you start delving deeper into Polymers.
This is a really useful one. Sharing some code using behaviors is great.
For instance, you can easily mutualize non-lifecycle methods between several components like we did in our timeoff-helper behavior. Or you can do something even better, for instance sharing some properties or lifecycle method like our timeoff-task-comment behavior. This makes it possible to have several components with the ability to prepare a request for the Workflow API with a comment in their variables.
Write it once, use it everywhere! That must be your thought when using the Polymer Behaviors mechanism.
Styling Custom Property
Styling Custom Property could be useful if you’d like not to hard code anything in your style, especially in the shadow DOM. Let’s dig deeper into how timeoff-entry-user handles the color for the user’s initials.
The goal is to have a unique color for a username. We can use a simple array of hardcoded colors, then calculate a simple index for a username, pick the color and that’s all. But, we have something better! CSS is providing a variable system implemented in Polymer, and Polymer provides us some color variables that are great with Material Design. But due to the component lifecycle, the CSS variables are rendered before the loaded state and we are not able to know the username before that. So, we need to recompute the component style when we finish calculating the username’s color. That’s where the custom property API come to our rescue!