Deploy Angular 2 CLI app to Heroku

Angular2 CLI generated apps can’t be directly deployed in Heroku. But it is easy to configure them to do that using scripts to build and serve the app.

I used angular-cli 1.0.0-beta.20-4 to generate and Angular2/Webpack app.
npm install -g angular-cli
ng new ng2-demo-app

And configured the package.json file using heroku-prebuild, heroku-postbuild and start scripts to build the app and serve it using http-server.

"scripts": {
  "heroku-prebuild": "npm install -g http-server",
  "heroku-postbuild": "ng build --prod",
  "start": "http-server dist/",

Heroku-prebuild and heroku-postbuild are Heroku-specific build steps, they are equivalent to the generic preinstall or postinstall but using them we avoid running those scripts locally.

In Heroku dev-dependencies are not installed by default, that means that to make the ng build script to work we need to move dev-dependency to the dependencies block.

"dependencies": {
  "@angular/common": "~2.1.0",
  "@angular/compiler": "~2.1.0",
  "@angular/core": "~2.1.0",
  "@angular/forms": "~2.1.0",
  "@angular/http": "~2.1.0",
  "@angular/platform-browser": "~2.1.0",
  "@angular/platform-browser-dynamic": "~2.1.0",
  "@angular/router": "~3.1.0",
  "core-js": "~2.4.1",
  "rxjs": "5.0.0-beta.12",
  "ts-helpers": "~1.1.1",
  "zone.js": "~0.6.23",

  // moved here these devDependencies
  "angular-cli": "1.0.0-beta.20-4",
  "@angular/compiler-cli": "~2.1.0",
  "@types/jasmine": "~2.2.30",
  "@types/node": "~6.0.42",
  "typescript": "~2.0.3"
},
"devDependencies": {
  "codelyzer": "~1.0.0-beta.3",
  "jasmine-core": "2.4.1",
  "jasmine-spec-reporter": "2.5.0",
  "karma": "1.2.0",
  "karma-chrome-launcher": "~2.0.0",
  "karma-cli": "~1.0.1",
  "karma-jasmine": "~1.0.2",
  "karma-remap-istanbul": "~0.2.1",
  "protractor": "4.0.9",
  "ts-node": "1.2.1",
  "tslint": "3.13.0",
  "webdriver-manager": "10.2.5"
}

Notice that it’s preferable to move only the dependencies you actually need for production builds. For that reason I moved only the dependencies required for the build but not the testing or linting dependencies.

If you don’t want to be moving dependencies it is possible to disable the production mode in the Heroku instance with
heroku config:set NPM_CONFIG_PRODUCTION=false
With this Heroku will install all dependencies, included the dev ones.

Another alternative to the http-server solution explained here is to use the NGINX and Heroku-buildpack-static.

Docs:
https://www.angularonrails.com/deploy-angular-cli-webpack-project-heroku/
https://github.com/angular/angular-cli/issues/2517
https://m.alphasights.com/using-nginx-on-heroku-to-serve-single-page-apps-and-avoid-cors-5d013b171a45#.1zpyh4lao
https://github.com/heroku/heroku-buildpack-static

Advertisements

Jasmine-Matchers – Adding more common matchers to Jasmine.

Jasmine-Matchers is a library of test assertion matchers for a range of common use-cases, to improve the readability of tests written using the Jasmine testing framework.

Installation

Install the package

npm install karma-jasmine-matchers --save-dev

And configure it in karma.conf in the frameworks section

frameworks: ['jasmine', 'jasmine-matchers’],

To use the additional matchers also in protractor e2e, protractor should be configured to import the ‘jasmine-expect’ module before the tests run. That can be done in the protractor.conf.js like this:

onPrepare: function () {
// Import additional jasmine matchers
require('jasmine-expect’);
}

Usage

The list of additional matchers is available here https://github.com/JamieMason/Jasmine-Matchers#matchers

Examples

Examples of unit test refactors using new matchers

toBeArrayOfSize
– expect(clients.length).toBe(2);
+ expect(clients).toBeArrayOfSize(2);

and instead of failing like this
Expected 1 to be 2.
when it fails, the new matcher look like this:
Expected [ Object({ id: ‘client-id’, name: ‘A Client’ }) ] to be array of size 2.

toBeEmptyString
– expect(vm.getCategory({})).toBe(‘’);
Expected ‘Test’ to be ‘’
+ expect(vm.getCategory({})).toBeEmptyString();
Expected ‘Test’ to be empty string.

– expect(vm.projectsFilter.name).toEqual(‘’);
Expected ‘Test’ to be ‘’
+ expect(vm.projectsFilter.name).toBeEmptyString();;
Expected ‘Test’ to be empty string.

toBeEmptyObject
– expect(data).toEqual({});
Expected Object({ location: ‘ftp.mysite.com’, username: ‘username’, password: ‘password’, directory: ‘test’ }) to equal Object({ }).
+ expect(data).toBeEmptyObject();
Expected Object({ location: ‘ftp.mysite.com’, username: ‘username’, password: ‘password’, directory: ‘test’ }) to be empty object.

toBeTrue
– expect(vm.showHelp).toBe(true);
Expected false to be true.
+ expect(vm.showHelp).toBeTrue();
Expected false to be true.

Documentation

https://github.com/JamieMason/Jasmine-Matchers
https://www.npmjs.com/package/karma-jasmine-matchers
https://blog.pivotal.io/labs/labs/writing-beautiful-specs-jasmine-custom-matchers
https://github.com/JamieMason/Jasmine-Matchers/issues/60

Decorating Angular $httpBackend service.

An example of how to ​decorate the angular $httpBackend mock server with custom logic. In this example, I log to console the request method and URL.

// Configure the Mock HTTP Backend
angular
    .module('my-app')
    .config(['$provide', function ($provide) {
        $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
    }]);

// Decorate Mock HTTP Backend to log requests
angular
    .module('my-app')
    .config(function ($provide) {
        $provide.decorator('$httpBackend', function ($delegate) {
            let decoratedHttpBackend = function (method, url, data, callback, headers, timeout, withCredentials, responseType) {
                console.log(method + ' ' + url);

                return $delegate.call(this, method, url, data, callback, headers, timeout, withCredentials, responseType);
            };

            for (var key in $delegate) {
                if ($delegate.hasOwnProperty(key)) {
                    decoratedHttpBackend[key] = $delegate[key];
                }
            }

            return decoratedHttpBackend;
        });
    });

Angular – Caching $http calls using $cacheFactory service

In this post I am going to explain how we can cache requests to REST backends in our angular applications.

Introduction to $cacheFactory
Angular $cacheFactory is a service that allows us to create and access Cache objects. Think on a Cache as a simple key value store with put, get, destroy methods.

var myCache = $cacheFactory('cacheId');
myCache.put('key', 'value');
console.log(myCache.get('key'));

The angular $http services could use that Cache objects to cache response data.

angular.module('app', [])
    .factory('MyService', function ($http, $cacheFactory) {
		var myCache = $cacheFactory('cacheId');
        return {
            getPerson: function (id) {
                $http
                    .get('//server.com/api/people/' + id + '/', {cache: myCache})
                    .then(function (result) {
                        console.log(result.data.name);
                    });
            }
        }
    });

It uses the request URL as a key to store values. In the previous example if we call getPerson for the same person twice it will perform a http request only the first time and will return directly from the cache the second time.

Caching in Swagger APIs
In my current apps I don’t use directly the $http service to interact with the backend APIs, instead I use swagger generated API services that abstract me from writing that code. But the generated services allows also to use cache functionality. The way that we should use it is passing a cache object as a parameter of the services methods.
For example, we have the API service CurrentUserApi and its method getCurrentUser.

var currentUserApi = new CurrentUserApi(RestDomains.myService);
currentUserApi.getCurrentUser();

And we want to use cache here to avoid hitting the backend multiple times. The only thing that we need to do is to pass a Cache object as a param of the getCurrentUser method and the API service will use it to cache requests. Again, notice that we don’t need to deal with the complexity of managing the cache ourselves.

var currentUserApi = new CurrentUserApi(RestDomains.myService);
var currentUserCache = $cacheFactory('currentUser');
currentUserApi.getCurrentUser({$cache: currentUserCache});

Using a CacheService
We can simplify the use of the cache in our services using the same cache object. For that we can create a singleton CacheService with a local cache created with $cacheFactory. It can be injected in our services and passed directly as the cache param.

angular
    .module('App')
    .factory('CacheService', ['$cacheFactory', function ($cacheFactory) {
        return $cacheFactory('CacheService');
    }]);
 
 
angular
    .module('App')
    .factory('UsersService', ['RestDomains', 'CacheService', 'CurrentUserApi', function (RestDomains, CacheService, CurrentUserApi) {
    var currentUserApi = new CurrentUserApi(RestDomains.myService);

    return {
        getCurrentUser: function () {
            return currentUserApi.getCurrentUser({$cache: CacheService});
        }
    };
}]);

Considerations
The use of caching in the angular services could reduce the number of requests that the single page application performs, improving performance and making the UI more responsive. But it is really important to choose properly the endpoints and resource to cache. Usually we want to cache resources that we now that are not going to change during the user “session”, resources like current user information, reference data, etc.

It is possible to use different implementations of the $cacheFactory services like angular-cache that provides Cache objects with timeouts or the possibility of caching using HTML5 local storage. The good thing of the angular’s built-in version is its simplicity.

Notice also that the use of Cache objects directly by the $http or the swagger APIs services it is a cleaner option that dealing directly in our service with the cache of values.

Documentation:
https://docs.angularjs.org/api/ng/service/$cacheFactory
https://egghead.io/lessons/angularjs-cachefactory
http://jmdobry.github.io/angular-cache/
https://github.com/wcandillon/swagger-js-codegen

Mock an Angular Service

When we unit test an angular controller we test it in isolation replacing​ the services that the controller uses with test doubles (aka mocks). In Jasmine, the kind of test double used is called Spy​. (Read more: http://jasmine.github.io/2.0/introduction.html)

The next code example shows the most basic beforeEach initialization block for a controller test suit. In it we use the $controller service to instantiate the controller under test MyCtrl injecting a new clean $scope before each test execution.

beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
 
    $controller('MyCtrl', {
        $scope: scope // inject the stubbed scope
    });
}));

During the instantiation of the controller, we can configure the mock services or stubbed data to inject. Notice that for all the dependencies that we don’t configure explicitly​ the unit test will use the real ones.
There are different ways to mock dependencies, I am going to show two alternatives here:

Replacing a service by a spy object

var stateSpy;
 
beforeEach(inject(function ($rootScope, $controller, MyService) {
    scope = $rootScope.$new();
    controller = $controller;
 
    stateSpy = jasmine.createSpyObj('$state', ['go']); // create a test double for a $state service with function called go
 
    $controller('MyCtrl', {
        $scope: scope,
        $state: stateSpy // inject the test double in the controller
    });
 
}));

Replacing the functions of a service by spies
Let’s say that MyCtrl makes use of a function getData() on a service called MyService. We can remplace that function with an spy and fake their response.

beforeEach(inject(function ($rootScope, $q, $controller, MyService) {
    scope = $rootScope.$new();
    q = $q;
    controller = $controller;
 
    spyOn(MyService, 'getData').and.callFake(function () {
        var deferred = $q.defer();
        deferred.resolve(TEST_DATA);
        return deferred.promise;
    });
 
    $controller('MyCtrl', {
        $scope: scope
    });
 
}));

With this approach, we are replacing the functions of a service,​ not the service itself. If the function to spy doesn’t exist in the service the test will fail.

Angulartics custom plugin which uses an Angular service

Recently I started to use in my Angular projects Angulartics to deal with web analytics. It is a clean solution that could be directly used with multiple providers like Google Analytics or Piwik, see the list of plugins available.

If you want to use a different analytics or usage​ tracking service, you can create your own vendor plugin. The project documentation explains how to do it and claims that

It’s very easy to write your own plugin

And indeed it’s easy, the only thing to do is write your code inside these two functions:

angular.module('angulartics.myplugin', ['angulartics'])
  .config(['$analyticsProvider', function ($analyticsProvider) {

  $analyticsProvider.registerPageTrack(function (path) {
    // your implementation here
  }

  $analyticsProvider.registerEventTrack(function (action, properties) {
    // your implementation here
  }

}]);

If you take a look to the existing plugins implementation all of them access to an external api defined in the global scope, for example the piwik plugin uses window._paq.

But what I want to do is call from these track methods an angular service that allows me to post usage resources to a REST API. In “the angular way”​ without using any global scope function.

The problem here is that we should write our plugin code is inside an angular config block. And from a config block angular doesn’t allow to instantiate services neither inject the instance $injector.

I solved that issue using this approach:

var $injector = angular.element(document.documentElement).injector();

it allows us to get an instance injector and with it instantiate any server that we need.

As an example this is the code for an angulartics custom plugin that uses $log and MyUsageTrackingService services:

(function (angular) {
    'use strict';

    /**
     * @name angulartics.customplugin
     * Custom Angulartics plugin that uses an Angular Service for the Usage Tracking.
     *
     * Implementation docs:
     * https://github.com/luisfarzati/angulartics/blob/master/README.md
     * http://stackoverflow.com/questions/26890042/how-to-inject-location-into-a-click-event-handler-defined-in-a-config-function
     */
    angular.module('angulartics.customplugin', ['angulartics'])

        .config(['$analyticsProvider', function ($analyticsProvider) {

            /**
             * Track Page
             */
            $analyticsProvider.registerPageTrack(function (path) {

                /* Instantiate $log service */
                var $log = instanceInjector().get('$log');

                /* Log Page */
                $log.debug('Page tracking: ', path);

            });

            /**
             * Track Event
             */
            $analyticsProvider.registerEventTrack(function (action, properties) {

                /* Instantiate services */
                var $log = instanceInjector().get('$log');
                var myUsageTrackingService = instanceInjector().get('MyUsageTrackingService');

                /* Track */
                $log.debug("Event tracking: ", action, properties);

                var event = {
                    "applicationCode": "MYAPP",
                    "eventType": action,
                    "message": properties.message,
                    "appData": properties.appData
                };

                myUsageTrackingService.trackEvent(event);
            });

            /**
             * Returns an instance injector to allow the injection of services inside config blocks.
             */
            function instanceInjector() {
                return angular.element(document.documentElement).injector();
            }

        }]);

})(angular);

AngularJS – Select value property in ng-options

The angular select ng-options attribute is really useful to dynamically generate the options of a select. Additionally​, it brings another great benefit, the possibility​ of selecting as value other javascript data types and not only strings.

Example of use:

<script>
angular.module('demoApp', []).controller('DemoController', function($scope) {

 $scope.options = [
 { label: 'one', value: 1 },
 { label: 'two', value: 2 }
 ];
 
 $scope.numberSelected= $scope.options[1].value;
});
</script>

<body ng-app="demoApp">
  <div ng-controller="DemoController">
    <select ng-model="numberSelected" ng-options="opt.value as opt.label for opt in options">
    </select>

     The value selected is {{ numberSelected }}.
  </div>
</body>

Fiddler