wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Domino, Extlib, GRUNT, JSON and Yeoman


TL:TR

With a few tweaks and clever setup, you can have web developers deliver front-ends for Domino without ever touching it.
Contemporary web development workflows separate front-end and back-end through a JSON API and HTTP (that's 21st century 3270 for you). The approach in these workflows is to treat the webserver as source of static files (HTML, CSS, JS) and JSON payload data being shuffled back and forth. This article describes how my development setup makes all this work with Domino and Domino designer.

The full monty

The front-end tools I use are:
  • Node.js: a JavaScript runtime based on Google's V8 engine. It provides all runtime for all the other tools. If you don't have a node.js installation on your developer workstation, where were you hiding the last 2 years?
  • Bower: a dependency manager for browser files like CSS, JS with their various frameworks. You add a depencency (e.g. Bootstrap) to the bower.json file and Bower will find the right version for you
  • Grunt: a task runner application, used for assembly of web sites/applications (or any other stuff). Configured using a Gruntfile.js: it can do all sorts of steps like: copy files, combine and minify, check code quality, run tests etc.
  • Yeoman: A application scaffolding tool. It puts all the tools and needed configurations together. It uses generators to blueprint different applications from classic web, to reveal application to mobile hybrid apps. The site lists hundreds of generators and you are invited to modifiy them or roll your own
  • GIT: the version control system
The list doesn't include one or the other editor, one or the other IDE or version control client, since theses choices don't influence the workflow steps or setup. Pick what you like.
My general directory layout starts with a project folder that has entries for code, documentation, nsf and a misc. folder. I only put the code folder into the GIT version control.
Seperation of concerns for frontend and backend development
The front-end developer doesn't need any of the Domino components available to create user interface and interaction models. The back-end developer will use exclusively the REST controls from the Domino XPages Extension library (which is part of a default current Domino server install). The two directory structures are linked in a GIT project, but that isn't mandatory. One step that made my live much easier: take the dist directory and link it to WebContent. This saves me the step to copy things around. The fine-tuning of the setup is where the fun starts.But first things first:

Installation

You follow the Node.js instructions for your platform to get a running node.js version (v4.2.1 as time of writing). Thereafter you install as admin ( sudo for Linux/Mac) Bower, Grunt and Yeoman:
npm install -g bower
npm install -g grunt-cli
npm install -g yo
npm install -g generator-karma generator-angular

I use the generator-angular, but looking very promising is mcfly. Go and checkout the Yeoman website for the many more options you have for generators.
Change to the Frontend directory and initialize your new front-end application using
 yo angular myAwesomeApp
You get walked through a series of questions, node.js does some magic (requires internet connection) and you have a basic Angular.js application ready. There are tons of tutorials how to use Yeoman, Bower, Grunt and Angular.js, so go google them, I'll limit myself to the tweaks I made to make my Domino live easier.
Once the yo magic work concludes, you can use
grunt serve
to have a first look at your new frontend in action (by default on port 9000). As long as that grunt task is running, any change you save will automatically reload the browser and show you the new result (praise to the second monitor).

Tweaks

  • All my JSON controls live on an XPage with the name api.xsp.
    I create a directory under the app directory named api.xsp and add one file each that matches the path property in the XPages REST controls (api.xsp/people, api.xsp/setup.json etc). These files contain sample JSON of what the Domino REST control eventually will return.
  • To prevent that directory to be copied into the NSF (when grunt build runs), I edit the Gruntfile.js and change the copy:dist:files:src array to include '!api.xsp'.
    If you use more XPages you could use a wildcard. Be careful to include the exclamation mark which stands for not
  • With the file open, I adjust the clean section where I add '!<%= yeoman.dist %>/WEB-INF{,*/}*' to clean:dist:files:src.
    Again don't forget the exclamation mark. This allows me to keep WebContent and dist linked and the build task won't wipe out the WEB-INF directory.
    Now we can make JSON calls (in Angular services/providers) to our REST endpoints without having anything Domino installed (which allows you to use a Mac or Linux for this part of the exercise). The only missing step: posting things back.
  • This part is a little trickier easy when you ask the right people:
    in a frontend dev environment you don't want to fully simulate the server, a static reply or just echo the submitted JSON does suffice.
    The default configuration of the Gruntfile.js only caters to GET requests, but I wasn't the first to ask, so there's precedence. It turned out, that it is just a few lines of changes at the top of your Gruntfile.js (and 2 lines down):
    var bodyParser = require("body-parser");
    var postResponder = function(request, response, next) {
    if (request.method === 'POST') {
       console.log(request.method+" "+request.url);
       response.setHeader('Content-Type', 'application/json');
       response.statusCode = 200;
       response.end(JSON.stringify(request.body));
       } else {
         next();
      }
    };
    

    and to the connect.livereload.options.middleware object:
    function (connect) {
       return [
          connect().use('/api.xsp', bodyParser.json()),
          connect().use('/api.xsp', postResponder),
          connect.static('.tmp'),
          connect().use('/bower_components', connect.static('./bower_components')),
          connect().use('/app/styles', connect.static('./app/styles')),
          connect.static(appConfig.app)
       ];
    }
    

    Note: You only add the first two lines after the return statement, the others are already there.
Musing (insider pun intended) to put all this into a generator .
As usual YMMV!

Posted by on 30 October 2015 | Comments (3) | categories: XPages

Comments

  1. posted by Arun Agnihotri on Saturday 31 October 2015 AD:
    Steve...great post as usual.. one thing where most of it gets stuck is file attachment handling... can you tell us a little bit more about handling attachments in above scenario??
  2. posted by Stephan H. Wissel on Monday 02 November 2015 AD:
    The short answer: you don't.

    The long answer: A JSON based REST service serves JSON. An attachment is binary data. JSON != binary. You could start shoehorning files into a JSON API, but that's most likely not a brilliant idea.

    You have 2 use cases: extending existing application where you need to preserve attachments inside your main document and new applications where you can do what you deem fit. In the later case you save yourself a lot of trouble storing attachments in their own documents. In the former you deal with XPages native way to upload files (e.g. using the multi-upload control). I'll blog about this as need arises in my projects
  3. posted by Avi Haiat on Wednesday 04 November 2015 AD:
    Hi there, nice article, and thks for mentioning the generator-mcfly