AngularJS: Integration of promises ($q) and $http in factory

Standard

I have integrated promises ($q) with $http services, this will make code more extensible:

This will be shared service which will be consumed by all modules for executing CRUD operation,  Request Type, URL, Parameter Object will be passed to this shared service, so it will make code more maintainable, readable and scalable. If we don’t go through this method then we have to use $http.get() or $http.post method, every where in services files of each module, content negotiation issues can be simply handled over here, If you want to append anything with each URL like ‘Http:\\mydomain\’ then instead of copy it on every service file,  just hard-code this thing in this file and append URL from their respective services. We don’t need to mention protocol and host-name now in every URL request.

– It will be json valid.
– It will handle server errors.
– It will automatically display custom error message
– If it enters .then() everything went as planned.​
GitHub repository: GitHub Link

Factory:

app.factory("webApi", [
        '$http', '$templateCache', '$q', function ($http, $templateCache, $q) {
            return {
                withParameter: function (methodType, webApiUrl, parameterObject) {
                    var deferred = $q.defer();
                    $http({
                        method: methodType,
                        url: window.location.protocol + '//' + window.location.host + window.location.pathname + webApiUrl,
                        data: parameterObject,
                        cache: $templateCache
                    })
                    .success(deferred.resolve)
                    .error(deferred.reject);
                    return deferred.promise;
                },
                nonParameter: function (methodType, webApiUrl) {
                    var deferred = $q.defer();
                    $http({
                        method: methodType,
                        url: window.location.protocol + '//' + window.location.host + window.location.pathname + webApiUrl,
                        cache: $templateCache
                    })
                    .success(deferred.resolve)
                    .error(deferred.reject);
                    return deferred.promise;
                },
            }
        }
    ]);

 

Controller:

vm.candidates = candidateService.getAllCandidates()
            .then(function (data) {
                vm.candidates = data;
            })
            .catch(function (data, status) {
                console.error('error', response.status, response.data);
            })
            .finally(function () {
                console.log("finally");
            });

Usage of shared services in angularjs application containing mini SPA’s

Standard

GitHub Repository:  GitHub Link

For complete understanding, I have drawn a basic sequence diagram for flow:

Angular: Module Service (onboardingapp.candidate.controller.js)
  // Invoke your service layer for this module​ and will contain .success and .error content of each request
Angular: Module Service (onboardingapp.candidate.services.js)
    // This will serve as abstraction layer for underlying implemntation, Just pass URL, Request Type, Parameter Object
    // FYI -> Its time to get our hand dirty on actual http request, so introduce new Get method in WebAPI home controller

Angular: Global Service (appModule.JS)

     // This will contain shared/global service which will be consumed by all modules for executing CRUD operation,
    // Request Type, URL, Parameter Object will be passed to this shared service, so it will make code more maintainable
    // readable and scalable​. If we dont go through this method then we have to use $http.get() or $http.post method
    // every where in services files of each module, content negotiation issues can be simply handled over here,
    // If you want to append anything with each URL like ‘Http:\\Jobcorp\’ then instead of copy it on every service file
    // just hard-code this thing in this file and append URL from their respective services.
    // We don’t need to mention protocol and host-name now in every URL request.

For further clarification I have mentioned case of second module, how its controller and service will utilize this global service file.

sequence diagram

Implementation of AngularJS syntax for “Controller As” and the “vm” Variable

Standard

GitHub Link

HTML Implementation:

vm for html

Controller Implementation:

'use strict';

(function () {
    var app = angular.module('onBoardingApp.candidate.controllers', []);

    app.controller('CandidateController', ['$scope', 'candidateService', function ($scope, candidateService) {

        var vm = this;

        // Invoke your service layer for this module
        vm.candidates = candidateService.getAllCandidates()
            .then(function (data) {
                vm.candidates = data;
            })
            .catch(function (data, status) {
                console.error('error', response.status, response.data);
            })
            .finally(function () {
                console.log("finally");
            });
    }]);
})();

Guidelines to implement ideal scalable angularJS application

Standard
Food for thought: Please go through code in VS, Its a sample angular application, doesn’t have that much of meat in it, a skeleton app which deals functionality like:
GitHub Repository:  GitHub Link
Directory Structure
Directory Structure
– File Naming Convention
File Naming Convention
– Angular UI routing
app.config(['$stateProvider', function ($stateProvider) {
        $stateProvider.state('allCandidates', {
            url: '/candidates',
            controller: 'CandidateController',
            controllerAs: 'vm',
            templateUrl: '/Scripts/app/modules/candidate/views/onBoardingApp.candidate.html'
        });
    }]);
– Angular UI Bootstrap 3 (Nothing that much has been done in terms of styling and look)
Bootstrap
Dependency Injection for each module (onBoardingApp.candidate.js file)
   var app = angular.module('onBoardingApp.candidate', [
            'onBoardingApp.candidate.services',
            'onBoardingApp.candidate.controllers'
        ]);
Routing for each module (onBoardingApp.candidate.js file)
app.config(['$stateProvider', function ($stateProvider) {
        $stateProvider.state('adminHome', {
            url: '/',
            controller: 'HomeController',
            controllerAs: 'vm',
            templateUrl: '/Scripts/app/modules/home/views/onBoardingApp.home.html'
        });
    }]);
– For scalability using Controller AS feature for $scope replacement (Please check onBoardingApp.candidate.controller.js and onBoardingApp.candidate file)
– Sample test factory (onBoardingApp.candidate.services.js file)

AngularJS: Ideal Directory Structure

Standard

AngularJS is a MVC framework. While when you are working in an MVC framework a common practice is to group files together based on file type:

templates/
    _login.html
    _feed.html
app/
    app.js
    controllers/
        LoginController.js
        FeedController.js
    directives/
        FeedEntryDirective.js
    services/
        LoginService.js
        FeedService.js
    filters/
        CapatalizeFilter.js

This seems like an obvious layout, However once the app begins to scale, this layout causes many folders to be open at once. Whether using Sublime or Visual Studio, a lot of time is spent scrolling through the directory tree.

Instead of keeping the files grouped by type, group files based on features:

app/
----- shared/   // acts as reusable components or partials of our site
---------- sidebar/
--------------- sidebarDirective.js
--------------- sidebarView.html
---------- article/
--------------- articleDirective.js
--------------- articleView.html
----- components/   // each component is treated as a mini Angular app
---------- home/
--------------- homeController.js
--------------- homeService.js
--------------- homeView.html
---------- blog/
--------------- blogController.js
--------------- blogService.js
--------------- blogView.html
----- app.module.js
----- app.routes.js
assets/
----- img/      // Images and icons for your app
----- css/      // All styles and style related files (SCSS or LESS files)
----- js/       // JavaScript files written for your app that are not for angular
----- libs/     // Third-party libraries such as jQuery, Moment, Underscore, etc.
index.html

This directory structure makes it much easier to find all the files related to a particular feature which will speed up development. It may be controversial to keep .html files with .js files, but the time savings are more valuable.

AngularJS: Promises usage in angular application

Standard

Promises can be used to unnest asynchronous functions and allows one to chain multiple functions together – increasing readability and making individual functions, within the chain, more reusable.

function getCandidateCareer() {
    ajaxLoader.show();
    careerDetailService.getCountryList()
        .then(function (response) {
            if (response.data)
                $scope.countryList = response.data;
            return careerDetailService.getJobTypeList();
        })
        .then(function (response) {
            if (response.data)
                $scope.jobTypeList = response.data;
            return careerDetailService.getExpertiseLevelList();
        })
        .then(function (response) {
            if (response.data)
                $scope.expertiseLevelList = response.data;
            return careerDetailService.getCandidateCareer();
        })
        .then(function (response) {
            if (response.data) 
                $scope.career = response.data[0];
            ajaxLoader.hide();
        });
};

If you look at code closely, in order to resolve chaining, next async call depends upon completion of previous ajax call, you consume the output as per need of current call and then execute the next async call in queue.

Refactoring: Usage of Guard Clauses

Standard

During initial days of development, I inclined towards using nested if statements, but with passage of time when code requirements started to be more complex and involve lot of if statements, this result in nightmare in terms of maintenance and readability of code. So I started using Guard clauses to improvise readability and duplication of code.

Before Guard Clause code::

[HttpPost]
public Candidate AddUpdateCandidateDependents(Candidate newObject)
{
    if (newObject == null)
    {
        if (newObject.ID == 0)
        {
            Candidate obj = newtObject;
            obj.ID = GetCandidateDetailId();
            obj.Active_f = true;
            _context.CandidateDependents.Add(obj);
            _context.SaveChanges();
            return obj;
        }
        else
            return null;
    }
    else
        return null;
}

After Guard Clause code:

[HttpPost]
public Candidate AddUpdateCandidateDependents(Candidate newObject)
{
    if (newObject == null || newObject.ID == 0) return null;
    Candidate obj = newObject;
    obj.ID = GetCandidateDetailId();
    obj.Active_f = true;
    _context.CandidateDependents.Add(obj);
    _context.SaveChanges();
    return obj;
}

Please note we can however combine above two if statements, When you perform comparisons with nullable types, if the value of one of the nullable types is null and the other is not, all comparisons evaluate to false except for !=