[Summer of API] Apisparkifying Angular applications

For the Summer of API, Im implementing an Angular application (see my first post on the subject) to display open data on maps in a convenient way. I want to use all the classical tools provided by the community to make my development life easier.

Concretely, Im using Yeoman with the Angular generator. This means that the project structure was generated for me. In addition Yeoman leverages tools like NPM and Bower for dependencies and also Grunt for the build script. Yeoman also configured the project to be able to use LiveReload and build / optimize it for production.

In this context, implementing the application is really convenient with the local server. To deploy the application within an APISpark Web API, we need however to make some adjustments to follow its required structure and restrictions:

  • No support of hierarchical folders
  • No possibility to put files at the root of the path

This means that I need to define several folders for the following contents:

  • html for the HTML elements
  • images for the images
  • scripts for the JavaScript files
  • styles for the CSS files

I dont have problems for the two last ones since Grunt will gather code within a minimum set of files and no sub folders will be required in them. Here is a sample content for these folders:

dist/
  scripts/
    scripts.e5b45497.js
    vendor.59008981.js
  styles/
    main.9dcbb5ce.css
    vendor.8fe9e3e1.css

For the folder images, if we are a bit careful (not use sub folders), there is also no problem. Contrariwise,

For the folder html, things are a bit more tricky. As a matter of fact, the files are present at the root folder. So we need to copy them into the folder html and update the links to related JS and CSS files. For this, we will show how to implement and use a very simple Grunt task apisparkify.

You can also notice that there is also a folder views in the folder dist. We will use Angular template preloading in order not to have to use it after deploying.

Preloading Angular views

To preload Angular views, we can use the tool grunt-html2js to integrate such processing directly within the build chain. The installation of this tool can be simply done within the file package.json, as described below:

{
    (...)
    "devDependencies": {
        (...)
        "grunt-html2js": "0.3.2",
        (...)
    }
    (...)
}

Simply type then the following command to install the tool within your project:

npm install

Then you need to configure this tool within your Gruntfile file. This consists in loading the tool, defining its configuration within the function grunt.initConfig and finally add it within the task processing flow.

module.exports = function (grunt) {
    // Define the configuration for all the tasks
    grunt.initConfig({
        (...)

        html2js: {
            options: {
                base: 'app',
                module: 'mapManager.templates',
                singleModule: true,
                useStrict: true,
                htmlmin: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    removeComments: true,
                    removeEmptyAttributes: true,
                    removeRedundantAttributes: true,
                    removeScriptTypeAttributes: true,
                    removeStyleLinkTypeAttributes: true
                }
            },
            main: {
                src: ['app/views/**/*.html'],
                dest: 'app/scripts/templates.js'
            },
        }
    });

    grunt.loadNpmTasks('grunt-html2js');

    (...)

    grunt.registerTask('test', [
        'clean:server',
        'wiredep',
        'concurrent:test',
        'autoprefixer',
        'html2js',
        'connect:test',
        'karma'
    ]);

    (...)

    grunt.registerTask('build', [
        'clean:dist',
        'wiredep',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'html2js',
        'concat',
        'ngAnnotate',
        'copy:dist',
        'cdnify',
        'cssmin',
        'uglify',
        'filerev',
        'usemin',
        'htmlmin'
    ]);

    (...)
};

You can notice that we only add the task html2js task within the build one. As a matter of fact, we only need to compile views into JavaScript when executing the application within APISpark. Its not necessary for development with command grunt serve.

As you can see, we specify a module name for the generated template source code. To actually use this source code, we need not to forget to do two things.

The first one is to Add the generate JavaScript file (here app/scripts/templates.js) within our file index.html, as described below:

<html>
    (...)
    <body>
        (...)
        <!-- build:js({.tmp,app}) scripts/scripts.js -->
        (...)
        <script src="scripts/templates.js"></script>
        <!-- endbuild -->
    </body>
</html>

The second one is to define the module as dependency of our Angular application within our file app/scripts/app.js, as described below:

angular
    .module('mapManagerApp', [
        (...)
        'mapManager.templates'
    ])
    .config(function ($routeProvider) {
        (...)
    });

Now this is completed, we need to tackle HTML files to be able correctly load and use the Angular application.

Configuring HTML files

As described previously, we need to implement following processing within the build chain:

  • Create a folder dist/html
  • Copy the file index.html from folder dist to dist/html
  • Update the references on files JS and CSS within the copied file index.html

For this, we will create a custom Grunt task. We will leverage the utility functions of Grunt relative to files and folders and the library Cheerio to parse and update links within the HTML file. Following snippet describes the implementation of this custom task:

grunt.registerTask('apisparkify', function() {
    var fs = require('fs');
    var $ = require('cheerio');

    // Create an html directory
    grunt.file.mkdir('dist/html');
    grunt.log.ok('Created folder dist/html');

    // Copy the index.html file into the created folder
    grunt.file.copy('dist/index.html', 'dist/html/index.html', {});
    grunt.log.ok('Copied file dist/index.html to folder dist/html');

    // Update links in it
    var indexFile = fs.readFileSync('dist/html/index.html').toString();
    var parsedHTML = $.load(indexFile);

    parsedHTML('script').each(function(i, elt) {
        var wElt = $(elt);
        var srcAttr = wElt.attr('src');
        if (srcAttr != null) {
            wElt.attr('src', '../' + srcAttr);
        }
    });
    grunt.log.ok('Updated script tags');

    parsedHTML('link').each(function(i, elt) {
        var wElt = $(elt);
        var hrefAttr = wElt.attr('href');
        if (hrefAttr != null) {
            wElt.attr('href', '../' + hrefAttr);
        }
    });
    grunt.log.ok('Updated link tags');

    fs.writeFileSync('dist/html/index.html', parsedHTML.html());
    grunt.log.ok('Written updated file dist/index.html');
});

Now the Grunt task is implemented, we need to put it within the task processing chain. We put it at the very end of the task build, as described below:

grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'html2js',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin',
    'apisparkify'
]);

Lets deal now with the way to deploy the Angular application within the APISpark platform.

Deploying the frontend application

Now we did the work, installing the application on the APISpark platform is quite easy! We simply need to upload files within corresponding folders. We use the same names as those generated by Grunt when creating our Web API. Uploading can be done either using the file brower of the underlying file store used or directly using the Web API with paths corresponding to folders.

As a reminder, we described in the previous post the structure of our Web API and how to create them.

This part isnt really handy and boring so we need to industrialize using scripts. In our next post, we will describe the implementation of a Node application:

  • To create the different cells used by our Web API and link them together
  • To create simply the data schema using JSON sample data files
  • To install the front end application
Advertisements
This entry was posted in Angular, APISpark, Summer of API, Tips and tagged , , , , . Bookmark the permalink.

One Response to [Summer of API] Apisparkifying Angular applications

  1. Pingback: Summer of APIs: Apisparkifying Angular applications | Restlet - We Know About APIs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s