Tuesday, January 12, 2016

First gulp is the best gulp

var gulp = require('gulp');
var ts = require('gulp-typescript');

var tsProject = ts.createProject('theApp/tsconfig.json');


gulp.task('typings', function () {
    return gulp.src('theApp/**/*.d.ts')
        .pipe(gulp.dest('./xdeploy'));
});



gulp.task('genjs', ['typings'], function () {
    return tsProject.src('theApp/**/*.ts')
        .pipe(ts(tsProject))
        .pipe(gulp.dest('./xdeploy'));
});

gulp.task('copyjs',  ['genjs'], function () {
    return gulp.src('theApp/**/*.js')
        .pipe(gulp.dest('./xdeploy'));
});

gulp.task('copyjson',  ['copyjs'], function () {
    return gulp.src('theApp/**/*.json')
        .pipe(gulp.dest('./xdeploy'));
});



gulp.task('default', ['copyjson'], function () {
    return gulp.src('theApp/node_modules/**/*', { 'base' : 'theApp/node_modules' })
        .pipe(gulp.dest('./xdeploy/node_modules'));
});


Friday, October 23, 2015

Alignment problem of Firefox that's non-existent on Chrome

Given this html:

<span class="form-search"></span>
<input class="search-query form-control" style="width: 200px" placeholder="Live search"/>


And CSS:

span.form-search {
    position: relative;       
}
span.form-search:before {
    display: block;
    width: 14px;
    height: 14px;
    content: "\e003";
    font-family: 'Glyphicons Halflings';
    background-position: -48px 0;
    position: absolute;
    top:8px;
    left:8px;
    opacity: .5;
    z-index: 1000;
}
input.search-query {
    padding-left:26px;
}

Live code: https://jsfiddle.net/L1pb9km3/

The above display perfectly ok with Chrome, but not on Firefox.

Chrome:





Firefox:






The work-around to make it work on Firefox is simple, just add a float: left to the span.form-search

span.form-search {
    position: relative;   
    float: left;    
}

Live code: https://jsfiddle.net/axjk3xss/

Yes it's easy to land on comet than to properly align stuff on web using CSS.

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!