Tuesday, October 20, 2015

Can't set the $pristine on constructor

Let's say you are using a directive that has problem with pristine, i.e., on first load the aForm is already dirty:

<form name="c.aForm">
<someDirectiveWithBugHere></someDirectiveWithBugHere>
</form>

<div>{{c.aForm.$pristine}}</div>




You can't set the aForm to pristine on constructor, as aForm is not yet defined while on constructor:
module App.UserEdit
{
    export class MainController
    {
        aForm: ng.IFormController;

        constructor(public Resources: InhouseLib.Resources)
        {
             this.aForm.$setPristine();

        }
    }
}

Setting the form to pristine while on constructor would give this result:
TypeError: Cannot read property '$setPristine' of undefined
    at new MainController

A work-around is to set the pristine state after the form is loaded, and by calling the expression (that will set the pristine to clean) on ng-init:
<form name="c.aForm">
<someDirectiveWithBugHere></someDirectiveWithBugHere>
</form>
<div ng-init="c.setClean()"></div>

<div>{{c.aForm.$pristine}}</div>

Remove the $setPristine from the controller and move it to setClean:
module App.UserEdit
{
    export class MainController
    {

        aForm: ng.IFormController;

        constructor(public Resources: InhouseLib.Resources)
        {
        }
    
        setClean(): void
        {
            this.aForm.$setPristine();
        }
    }
}


Or if you don't want the controller to have concerns on those stuff, you can initialize the form's state directly on html.

<div ng-init="c.aForm.$setPristine();"></div>

<div>{{c.aForm.$pristine}}</div>

Sunday, October 4, 2015

Prevent ui.router's ui-sref from reloading the page

If a ui-router link points the page where it resides, it will cause a page reload on the page(s) that are reloaded, and thus it will cause flickering or noticeable delay.

<a ui-sref="root.app.editAd({id: ad.itemId})">{{ad.title}}</a>

To improve that, just perform ajax on an ng-click and just fetch the data that are needed to populate the information on the page, and prevent the href from reloading the page by using $event.preventDefault(). The href will just be used for bookmark purposes or if the user want to open the link on another tab or window. We can use ui.router href method for translating the state to link.

<a 
ng-click="ctrl.AppWide.edit(a.itemId); $event.preventDefault()" 
title="Change Information" 
href="{{c.$state.href('root.app.editAd',{id: a.itemId})}}" 
 {{a.title}}
</a>


To change the url after performing an ajax operation, use $state.go and pass the parameter notify false to it, so it will just change the url and not do a partial page reload.

this.$state.go('root.app.editAd', {id: this.savedId}, {notify: false});


Note: As of the time of the time of this writing, the notify false has a bug, it invokes the originating controller twice and sometimes it rejects the link the user clicked on from going to the link's page. There's already a solution to that, but it's not yet on the latest release of angular ui.router, have to patch the ui.router manually.


Happy Coding!

Wednesday, September 16, 2015

Using modules in Angular way

You can use a TypeScript module as it is in an Angular app. Just load and it use it right away.

However, if you want it done in Angular way, it's better. It makes the code more testable and the module dependencies on your code will be more obvious.

Here's an example. /shared/utilities/StringLib.ts
///<reference path="../../typings/node/node.d.ts"/>
///<reference path="../../typings/angularjs/angular.d.ts"/>

var isNode = typeof exports !== 'undefined' && this.exports !== exports;

module utilities.StringLib {

    export function isNumeric(n: string) : boolean {
        return !isNaN(parseFloat(n)) && isFinite(<any>n);
    }

    export function replaceAll(src: string, search: string, replacement: string) : string {
        return src.split(search).join(replacement)
    }
}

if (isNode) {
    module.exports = utilities.StringLib;
}
else {
    angular.module('utilities.stringLib',[])
        .factory('stringLib', () => {
            return utilities.StringLib;
        });
}


Then on the ocLazyLoadProvider's config add this:
{
    name: 'utilities.stringLib.files',
    files: ['/shared/utilities/StringLib.js']
}



To use, indicate the module name above on the module's dependency array parameter. On the last element of the array, nest an array, put the name of the list of file(s) of the module(s) that are being lazy-loaded. ocLazyLoad makes this lazy-loading possible.


The typeof operator for utilities.StringLib enables the autocomplete functionality for TypeScript on your IDE.


Note, put the <any> typecast to make the TypeScript compiler stop complaining of incompatible parameter, this lazy-loading mechanism is an extension of ocLazyLoad to Angular, and is not included on Angular's TypeScript definition file.


///<reference path="../../../shared/utilities/StringLib.ts"/>
///<reference path="../../../typings/angularjs/angular.d.ts"/>

module App.Product {

    export class Controller {
    
        constructor(public stringLib: typeof utilities.StringLib) {
        
            console.log(this.stringLib.isNumeric("1234"));
            console.log(this.stringLib.isNumeric("1234b"));        
        }
    
    }

}    
    

angular
    .module('niceApp', <any>
        [
            'utilities.stringLib',

            [                
                'utilities.stringLib.files'                
            ]
        ])
    .controller('ProductController', [
        'stringLib',
        
        App.Product.Controller
    ]);


Tuesday, September 15, 2015

Cannot read property 'name' of undefined

While playing with ocLazyLoad, I got the following error:

TypeError: Cannot read property 'name' of undefined
    at ocLazyLoad.js:554
    at Object.forEach (angular.js:336)
    at Object._loadDependencies (ocLazyLoad.js:539)
    at loadNext (ocLazyLoad.js:642)



Can you spot the error?

// directives
'ngTagsInput',
'ui.pagedown',
'puElasticInput',
'ui.bootstrap'

[
    'sharedState.appWide.files',
    'oitozero.ngSweetAlert.files',


Yeah, I just forgot the comma between 'ui.bootstrap' and the opening square bracket. Should be a syntax error, but due to javascript dynamic nature, it's not flagged as syntax error. Should explore JSLint.

Lazy-loading services with ocLazyLoad

While I'm converting a couchPotato-using Angular code to ocLazyLoad, I learned that ocLazyLoad can lazy load a service too.

On the controller, $ocLazyLoad and $injector must be passed, then get the lazy-loaded object via its name.

Root-Controller.ts:
module App.Root {

    export class Controller {

     
        constructor(
              public SweetAlert,
              public $state,
              
              public $ocLazyLoad,
              public $injector
         ) {

            this.$ocLazyLoad.load('/shared-state/AppWide.js').then(x => {
                var svc = this.$injector.get('someServiceNameHere');
                this.SweetAlert.swal(svc.title);
            });
        }

     

    }

}


angular
    .module('niceApp',<any> 
        [
            'oitozero.ngSweetAlert',   
                      
            [
                'oitozero.ngSweetAlert.files'
            ]
        ])
    .controller('RootController',
        [
            'SweetAlert',
            '$state',
            
            '$ocLazyLoad', 
            '$injector',
            
            App.Root.Controller
        ]);

AppWide.ts:
module SharedState
{
    export class AppWide
    {
        title : string;

        get isUploadVisible(): boolean {
            var theToken = this.$window.localStorage["theToken"];
            return theToken !== undefined;
        }

        constructor(public $window: ng.IWindowService) {
            this.title = "This must be changed";
        }


    }
}

angular.module('niceApp',[])
    .service('someServiceNameHere', 
             ['$window', SharedState.AppWide]);



The problem with the codes above, the controller has now a hard-dependency on $ocLazyLoad and $injector.

There's a nicer solution to the problem, just like SweetAlert, just wrap AppWide.ts to its own module, and make that new module a dependency module of niceApp module. Here's the configuration for AppWide.js:
$ocLazyLoadProvider.config({
            debug: true,
            modules: [
                {
                    name: 'oitozero.ngSweetAlert.files',
                    files: [
                        '/lib/angular-sweetalert/SweetAlert.js',
                        '/lib/sweetalert/dist/sweetalert.min.js',
                        '/lib/sweetAlert/dist/sweetalert.css'
                    ]
                },
                {
                    name: 'common.appWide.files',
                    files: [
                        '/shared-state/AppWide.js'
                    ]
                }
            ]
    });

AppWide.ts:
module SharedState
{
    export class AppWide
    {
        title : string;

        get isUploadVisible(): boolean {
            var theToken = this.$window.localStorage["theToken"];
            return theToken !== undefined;
        }

        constructor(public $window: ng.IWindowService) {
            this.title = "This must be changed";
        }


    }
}

angular.module('common.appWide',[])
    .service('someServiceNameHere', 
             ['$window', SharedState.AppWide]);


The controller is now free from $ocLazyLoad and $injector.

Root-Controller.ts:
module App.Root {

    export class Controller {

    
        constructor(
            public SweetAlert,
            public $state,
            
            public AppWide
        ) 
        {
            this.SweetAlert.swal(this.AppWide.title);
        }

    }

}


angular
    .module('niceApp',<any>
        [
            'oitozero.ngSweetAlert', 
            'common.appWide',
        
            [
                'oitozero.ngSweetAlert.files', 
                'common.appWide.files'
            ]
        ])
    .controller('RootController',
        [
            'SweetAlert',
            '$state',

            'someServiceNameHere', 

            App.Root.Controller
        ]);



Happy Lazy Loading!

Sunday, September 13, 2015

Boilerplate code for AngularJS, NodeJS, JWT and TypeScript

Pre-requisite:

JSON Web Token module:
npm install jwt-simple


Not pre-requisites. Makes UI development convenient.
* ui-router
* sweetAlert


For persistence of token even when the browser is refreshed, $window.sessionStorage would suffice, no need to use ng-storage. However, it's better to use $window.localStorage, $window.sessionStorage is for single tab only; if we open the same app in another tab, it won't be able to read the sessionStorage from the original tab. $window.localStorage can be accessed between tabs.


Authentication consist mainly of these parts:


Server-side:
* Auth.ts -- username and password authentication goes here
* CanBe.ts -- authorization goes here
* app.ts -- entry point of a node app

This is how an API can be authorized in nodejs's app.js. Insert the CanBe.accessedBy node RequestHandler before the API to be called, e.g.,

Without authorization:
app.get('/api/item', itemApi.search);


With authorization:
app.get('/api/item-secured', canBe.accessedBy(['rockstar']), itemApi.search);


Shared by server-side and client-side:
* ILoginDto.ts -- username and password transmitted from client-side to server-side
* ILoginResponseDto.ts -- Auth.ts's authentication response
* ITokenPayload.ts -- Not exactly used by client-side, if there's a need for the client-side to decode the middle part of the JWT, it goes in this data structure. The first part and middle part of JWT are publicly-available information, and as such, ITokenPayload should contain no sensitive information, e.g., password


Client-side:
* init.ts -- called by main.js. main.js is the conventional name for the app's entry point in a RequireJS-based apps.
* AuthService.ts -- called by login UI, to decouple the login controller if there will be changes in authentication mechanism
* HttpInterceptor.ts -- the money shot. this is where to make an angular application automatically send a header everytime an $http or $resource call happens



Auth.ts:
import express = require('express');

import jwt = require('jwt-simple');

export function verify(req: express.Request, res:express.Response, next: Function) : any {
    
    var loginDto = <shared.ILoginDto> req.body;
    
    var loginResponseDto = <shared.ILoginResponseDto> {};
    
    var isValidUser = dbAuthenticate(loginDto.username, loginDto.password);
    
    if (isValidUser) {
        
        var roles = dbGetUserRoles(loginDto.username);
        
        var tokenPayload = <shared.ITokenPayload> { username: loginDto.username, roles: roles };
    
        var payloadWithSignature = jwt.encode(tokenPayload, "safeguardThisSuperSecretKey");
    
        loginResponseDto.isUserValid = true;
        loginResponseDto.theAuthorizationToUse = "Bearer " + payloadWithSignature;
        
        res.json(loginResponseDto);            
    }
    else {
        loginResponseDto.isUserValid = false;
    
        res.status(401).json(loginResponseDto);    
    }    
}


function dbAuthenticate(username: string, password: string): any {
    
    if (username == "Thom" && password == "Yorke") {
        return true;
    }
    
    if (username == "Kurt" && password == "Cobain") {
        return true;
    }
    
    return false;
}


function dbGetUserRoles(username: string): string[] {
    
    if (username == "Thom") {
        return ["rockstar", "composer"];
    }
    
    if (username == "Kurt") {
        return ["composer"];
    }
    
    return [];
}


CanBe.ts:
import express = require('express');

import jwt = require('jwt-simple');


export function accessedBy(roles: string[]):any {
    accessedByBind.bind(roles);
}

function accessedByBind(roles:string[], req:express.Request, res:express.Response, next:Function): any {
    
    function unauthorized(): any {
        return res.sendStatus(401);
    }
    
    // === accessedByBind starts here ===
    
    var theAuthorizationToUse = req.headers["authorization"];
    
    if (theAuthorizationToUse === undefined) {
        return unauthorized();
    }
    
    var payloadWithSignature = theAuthorizationToUse.substr("Bearer ".length); 
    
    var tokenPayload = <shared.ITokenPayload> jwt.decode(payloadWithSignature, "safeguardThisSuperSecretKey");

    if (tokenPayload == undefined || tokenPayload.username == "") {
        return unauthorized();
    }
    
    if (tokenPayload.roles.filter(memberRole => roles.filter(allowedRole => memberRole == allowedRole).length > 0 ).length > 0) {
        return next();
    }
    
    return res.status(500).json({customError: "Unknown Error"});
    
}



ILoginDto.ts:
module shared {
    
    export interface ILoginDto {
        username: string;
        password: string;
    }
    
}

ILoginResponseDto.ts:
module shared {

    export interface ILoginResponseDto {
        isUserValid: boolean;
        theAuthorizationToUse: string;
    }
    
}

ITokenPayload.ts:
module shared {
    
    export interface ITokenPayload {
    
        username: string;
        roles: string[];
    
    }
}



init.js
// app initialization and dependency injections goes here

var angie:ng.IAngularStatic = require('angular');

var mod:ng.IModule = angie.module('niceApp', [
    'ui.router', 'ngResource', 'scs.couch-potato', 'ngFileUpload',
    'ngSanitize', 'ui.pagedown', 'ngTagsInput', 'puElasticInput', 'ui.bootstrap']);

mod.service('AuthService', ['$http', '$q', '$window', InhouseLib.AuthService]);
mod.factory('HttpInterceptor', ['$q', '$window', '$injector', 'TheSweetAlert', '$location', InhouseLib.HttpInterceptor]);

mod.config(['$httpProvider', ($httpProvider:ng.IHttpProvider) => {
    $httpProvider.interceptors.push('HttpInterceptor');
}]);


AuthService.ts:
module InhouseLib {

    export class AuthService {


        constructor(public $http:ng.IHttpService, public $q:ng.IQService, public $window: ng.IWindowService) {
        }

        verify(username:string, password:string):ng.IPromise<ng.IHttpPromiseCallbackArg<shared.ILoginResponseDto>> {

            var deferred = this.$q.defer();

            var u = <shared.ILoginDto>{};
            u.username = username;
            u.password = password;



            this.$http.post<shared.ILoginResponseDto>('/api/member/verify', u)
                .then(success => {

                    this.$window.localStorage["theToken"] = success.data.theAuthorizationToUse;

                    // This goes to then's success callback parameter
                    deferred.resolve(success);

                }, error => {

                    this.logout();

                    // this goes to then's error callback parameter
                    deferred.reject(error);

                });

            return deferred.promise;

        }

        logout(): void {
            delete this.$window.localStorage["theToken"];
        }

    }
}


HttpInterceptor.ts:
module InhouseLib {

    export function HttpInterceptor($q:ng.IQService,
                                    $window:ng.IWindowService,
                                    $injector:ng.auto.IInjectorService,
                                    TheSweetAlert,
                                    $location:ng.ILocationService)
    {

        return {
            request: (config):any => {
                config.headers = config.headers || {};

                var theToken = $window.localStorage["theToken"];

                if (theToken !== undefined) {
                    // token already include the word "Bearer "
                    config.headers.Authorization = theToken;
                }
                return config;
            },

            responseError: (response):any => {
                if (response.status === 401 || response.status === 403) {

                    // http://stackoverflow.com/questions/25495942/circular-dependency-found-http-templatefactory-view-state
                  
                    // ui-router's $state
                    // can use the $location.path(paramHere) to change the url. but since we are using ui-router
                    // it's better to use its $state component to route things around.
                    var $state:any = $injector.get('$state');

                    TheSweetAlert({title: "Oops", text: "Not allowed. Will redirect you to login page\n" +
                        "This is your current url: " + $location.path()}, () =>
                    {
                        $state.go('root.login');
                    });


                }
                return $q.reject(response);
            }
        };
    }// function HttpInterceptor

}

UPDATE:

Some code above has Cargo Culting in it, it uses $q.defer unnecessarily. Haven't properly learned the promise's fundamentals before :) Read: https://www.codelord.net/2015/09/24/%24q-dot-defer-youre-doing-it-wrong/

Thursday, September 10, 2015

Avoid multiple NOTs in conditions

TL;DR
Write code like how you are speaking in a conversation.


What's the flaw in this code?

if (e.which === 8 && e.target.nodeName !== "INPUT" || e.target.nodeName !== ")
    e.preventDefault();
}


How to express condition without being confused on when to use || or && operator?


First off, let's correct the most fundamental error on the logic above. && operator has higher precedence than || operator, or we can say && operator higher stickiness than || operator. The above is interpreted as:

if 
(

    (e.which === 8 && e.target.nodeName !== "INPUT")
 
    || 

    e.target.nodeName !== "SELECT"

)
{ 
    e.preventDefault();
}


To correct the incorrect unparenthesized code, use parenthesis:

if ( e.which === 8 && (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") ) { 
    e.preventDefault();
}


Or since the code is not too indented, just nest the condition:

if (e.which === 8) {

    if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") { 
         e.preventDefault();
    }

}


Now that we get the harm out of the way, let's fix the second problem:

if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") {

}


What's wrong with the code above?

Yes, it always evaluates to true regardless of the value of e.target.nodeName.

This is where most developers are having a hard time when formulating a condition. The best way to express a logic is to express it like how you will say it in actual conversation. For example, if you want to advise your kid that if someone visits and he is not John, Paul, George, Ringo, then shoo him way. You will not say in English, "if he is not John, not Paul, not George, not Ringo, then shoo him away."

You will not repeat the NOT when you actually talk, instead you'll say: "if he is not John, Paul, George, Ringo, then shoo them away." You will only say NOT once. However, if you feel multiple NOTs is readable, you have to change the OR to AND to make the logic correct:

if e.target.nodeName ≠ "INPUT" and e.target.nodeName ≠ "SELECT" then
    disable navigation

But really, you won't say that in real life, you'll only say NOT once.

To code a correct logic, think of how you will write the condition if you will refactor or make a code shortcut. If you will move the condition to a function, you'll write it like this:

if not inputEditable(e.target.nodeName) then
    disable navigation


Now with that refactored code, the code writes itself:

inputEditable(nodeName) 

    if nodeName = "INPUT" or nodeName = "SELECT" then
        return true
    else
        return false

Here's the version when the condition above is inlined in if:

if not ( e.targetNodeName = "INPUT" or e.target.nodeName = "SELECT" ) then
    disable navigation


See? No more multiple NOTs, and the best thing is, the code is correct.


Just write things with one NOT, the right logic will write itself.



Happy Coding!