Tuesday, September 29, 2015

Keynote: AngularJS Less Backend

Angular JS


Take a look on the previous article to refresh your memory

Mock the Backend Responses

With using an Angular module called ngMockE2E u can mock the Back-end responses data, so u can develop without real BE ready.

  • When you sent some requests via $http service the responses with the locally mocked data would be returned by $httpBackend.
  • You can use it for the Unit Testing as well.

Add the Angular Mocks

  1. Add the angular-mocks.js into your index.html:

     ...
     <script src="bower_components/angular/angular.js"></script>
     <script src="bower_components/angular-mocks/angular-mocks.js"></script>
     ...
    
  2. To use routing u've to declare your module with ngMockE2E dependency: app.js

     'use strict';
     // this is the app module declaration
     angular.module('myApp', ['ngRoute', 'ngMockE2E'])
    
  3. Use $httpBackend service Pass the $httpBackend service into your module .run() method

      / This is what the app module declaration
      angular.module('myApp', ['ngRoute', 'ngMockE2E'])
      .run(['$httpBackend', function($httpBackend) {
         $httpBackend.whenGET(/contacts\/.*/).passThrough()
      }]) 
    
  4. Configure Expected responses. You have to add the request via expectGET otherwise u'll receive: Unexpected Request Error.
    You also need to add the response mock via .respond(), otherwise u'll receive: Response is not defined error.
    You can add your response at once with expectGet declaration.

      / This is what the app module declaration
      angular.module('myApp', ['ngRoute', 'ngMockE2E'])
      .run(['$httpBackend', function($httpBackend)
         // returns the current list of products
         $httpBackend.whenGET('/products').respond(productsArray);
         // mark as expected 
         $httpBackend.expectGET('\/products');
         $httpBackend.whenGET(/contacts\/.*/).passThrough()
      }])
    

Move your mock logic outside the module

Create the separate *.js file and point to it into your index.html.
Move your interception logic from the module to the new created file.

mock-backend.js

// mock $httpBackend, capturing routes and returning data
// it's not module declaration, just reffering 
angular.module('myApp')
    .run(['$httpBackend', function($httpBackend)
        // returns the current list of products
        $httpBackend.whenGET('/products').respond(productsArray);

        // mark as expected 
        $httpBackend.expectGET('\/products');

        $httpBackend.whenGET(/contacts\/.*/).passThrough()
    }]);

Encapsulate your $http calls into separate Service

Create the separate *.js file and point to it into your index.html
Move Your $http request under the new declared ServiceFactory

services/HttpService.js

angular.module('myApp').factory('HttpService', function HttpServiceFactory($http) {
var service = {
    query: function() {
        return $http.get('/products');

    },
    get: function(id) {
        return $http.get('/products/' + id);
    },
    // making save dual-function (no separate update w/PUT)
    save: function(data) {
        if(angular.isDefined(data.id)) {
            return $http.post('/products/' + data.id, data);
        } else {
            return $http.post('/products', data);
        }
    },
    delete: function(id) {
        return $http.delete('/trees/' + id);
    }
};

return service;
});

Use your created HttpService service

.controller('ShowCtrl', ['$scope', 'HttpService', '$routeParams', '$log', function ($scope, HttpService, $routeParams, $log) {
    
    HttpService.get($routeParams.id)
        .success(function (data) {
            $scope.product = data;
        })
        .error(function (response)
        {
            $log.error("ERROR: Trees response error status:" + response);
        })
}]);

Move your Server Data Mock outside

Create the separate *.js file and point to it into your index.html. Move your Data Access Model from the module to the new created file.

ServerDataMock.js

ngular.module('myApp.trees').service('ServerDataMock', function ServerDataMock(){
this.data = [
    {
        id: '1',
        price: 22.95,
        category: 'books',
        available: 'true',
        description: 'Fun book',
        date: new Date(2015, 9, 28)
    },
    {
        id: '2',
        price: 45,
        category: 'whiskey',
        available: 'false',
        description: 'Irish single-malt',
        date: new Date(2014, 4, 20)
    }
];

this.getData = function () {
    return this.data;
};

this.setData = function (data) {
    this.data = data;
};

this.findAll = function() {
    return this.getData();
};

this.findOne = function(id) {
    // find the game that matches that id
    var list = $.grep(this.getData(), function(element, index) {
        return (element.id == id);
    });
    if(list.length === 0) {
        return {};
    }
    // even if list contains multiple items, just return first one
    return list[0];
};
});

Use your data module into your mock-backend.js

angular.module('myApp').run(function($httpBackend, ServerDataMock)
    // returns the current list of products
    $httpBackend.whenGET('\/trees').respond(function(method, url, data) {
       var products = ServerDataMock.findAll();
       return [200, products, {}];
    });

    // mark as expected 
    $httpBackend.expectGET('\/products');

    $httpBackend.whenGET(/contacts\/.*/).passThrough();
     $httpBackend.whenGET(/^\/templates\//).passThrough();
 });

That's it, you don't need any Back-end anymore to finish your Front-end.

see Also


2 comments: