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 !=

LINQ: implementing Left Outer Join

Standard

Most of you are aware of the fact that there is no defacto way available for implementing outer join in LINQ, yes its true no keyword is associated with this functionality, however we can achieve that by using .DefaultIfEmpty() method. (We can also use extension method for our ease and customization but always remember KISS principle [Keep it simple stupid]

[HttpGet]
public JsonResult GetUserSavedDataState()
{
    CandidateInfoModels _userInfo = CurrentUser.GetCurrentUser();
    var currentState =
        (from personal in _context.CandidateDetails
            join job in _context.CandidatePaymentOptions on personal.ImportedCandidate_OID equals job.ImportedCandidate_OID
                into personalPaymentGroup
            from outer1 in personalPaymentGroup.Where(p => p.Active_f).DefaultIfEmpty()
            select new
            {
                Personal = personal.IsConfirmed == null ? false : personal.IsConfirmed,
                Job = outer1.IsConfirmed_f ?? false
            });
    return Json(currentState.Any() ? currentState.FirstOrDefault() : null, JsonRequestBehavior.AllowGet);
}

Usage of CONTAIN filter in LINQ queries

Standard

Contains scans a List. It searches for a specific element to see if that element occurs at least once in the collection. The C# List type has the useful Contains method that declaratively searches. It requires no explicit for-loop.

[HttpGet]
public IEnumerable<SelectListItem> GetBank(string countryName)
{
    List<SelectListItem> bankItems = new List<SelectListItem>();
    var lstLocations = _context.Locations.Where(x => x.Active_f == "A" && x.Country == countryName)
                                         .Select(x => x.Location_OID).ToList();
    if (lstLocations == null) return bankItems;
    bankItems.AddRange(_context.Banks.Where(e => e.Active_f && lstLocations.Contains(e.Location_OID))
                                     .Select(bank => new SelectListItem { Text = bank.BankName, Value = bank.Bank_OID.ToString() }));
    return bankItems;
}

WebAPI: Post Parameter Binding using [FromBody] with AngularJS HTTP services

Standard

Simple code example to consume asp.net WebAPI with AngularJS SPA application.

AngularJS Code:

return $http({
    method: 'POST',
    url: window.location.protocol + '//' + window.location.host + window.location.pathname + '/api/JobDetails/AddFeedback',
    data: JSON.stringify(objFeedback),
    cache: $templateCache
});

Key thing to note here is to convert your parameter content to JSON in POST request header.

WebAPI code:

[HttpPost]
public HttpResponseMessage AddFeedback(HttpRequestMessage request, [FromBody] object value)
{
    return value.ToString() != null ?
        request.CreateResponse(HttpStatusCode.OK) : request.CreateResponse(HttpStatusCode.BadRequest);
}

By default, Web API uses the following rules to bind parameters:

–  If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
–  For complex types, Web API tries to read the value from the message body, using a media-type formatter. This includes [FromBody], [FromUri], and [ModelBinder], or custom attributes.