Gotchas or Special Cases?

I’ve now been working in AngularTypeScript for a while, a few things have cropped up that are probably worth mentioning. Some things are just for clarity, others are because they had me confused for a while.

So without further adieu, my laundry list:

Structuring the main angular app class

I’ve decided that I like using the constructor, rather than instantiating the object and then calling a method. Mostly this is because in TypeScript, the arguments to the constructor aren’t defined in interfaces so it can vary naturally, and because it’s a bit less typing for the same result. Here’s the app I’m currently working on:

module AngularApp {
   // define how this application assembles.

   class QueryMain {
        serviceModule:ng.IModule;
        appModule:ng.IModule;

        constructor(angular:ng.IAngularStatic,
                        queryServicePtr:Function,
                        queryDirectivePtr:Function,
                        glNetDirectivePtr:Function,
                        infoDialogDirectivePtr:Function,
                        queryControllerPtr:Function){

            this.serviceModule = angular.module('phpConnection', []);
            this.serviceModule.service('QueryService', ['$http', queryServicePtr]);

            this.appModule = angular.module('rssApp', ['phpConnection', 'ngSanitize']);
            this.appModule.controller('MainCtrl', ['QueryService', '$timeout','$rootScope', queryControllerPtr]);
            this.appModule.directive('ngFeedPanel', ['$timeout','$rootScope', queryDirectivePtr]);
            this.appModule.directive('ngInfoDialog', ['$timeout','$rootScope', infoDialogDirectivePtr]);
            this.appModule.directive('ngNetworkWebgl', ['$timeout','$rootScope', glNetDirectivePtr]);
        }
   }


   // instantiate Angular with the components defined in the other files. Note
    // that weird things may happen with transclusion?

    new QueryMain(
        angular,
        PhpConnectionService.UrlAccess,
        new RssAppDirectives.ngFeedPanel().ctor,
        new WGLA2_dirtv.ngNetworkWebgl().ctor,
        new WGLA2_dirtv.ngInfoDialog().ctor,
        RssControllersModule.RssController
    )
}

There are really three patterns to note here. The first is that everything is that everything that the class QueryMain uses is passed in. No globals. And going along with that, please note that directives (and factories) have to be instantiated, so we pass in a pointer to a ctor() function, rather than the class as a whole. The last thing to mention is that there is no chaining. The modules are declared and then the components are added on individually rather than module().directive.controller() etc. The reason for this is that if you happen to declare, for example, a service that is needed by some later item. That service will not be visible if it’s chained with the declaration of the item. Line by line declarations appear to have more predictable results.

The wonderfulness of interfaces and a revisit of fatArrow = ():notation => {}

There seem to be times when it’s nicer to use fat arrow notation. In my experience, this pops up the most often in directives, though it can show up in promises as well. I’ve had some odd experiences where it seemed that ‘this’ doesn’t survive scope/closure changes into a called method. I can’t find any examples where I wasn’t able to make it work with conventional notation, but it’s worth covering anyway.

Since I’ve been working more with directives recently, we’ll use one of the directives used in the above code as an example. This one has had parts stripped out for clarity. By the way, as with every other class that is an Angular/Typescript component this extends my ATSBase class, which is covered here. :

export class ngFeedPanel extends NovettaUtils.ATSBase {
    private myDirective:ng.IDirective;
    private cobj:RssControllersModule.ICallbackPointers;

    constructor() {
        super();
    }
    private linkFn (scope:any, element:any, attrs:any) {
        this.cobj = scope.callbackObj;

        scope.nlpQuery = ():void => {
            var mobj:RssControllersModule.IDataResponse = scope.messageObj;
            this.cobj.setQueryString("");
            this.cobj.nlpQuery(mobj);
        };
    }

    public ctor(timeout:ng.ITimeoutService, rootscope:ng.IScope):ng.IDirective {

        if (!this.myDirective) {
            this.myDirective = {
                templateUrl: 'directives/rssFeed.html',
                restrict: 'AE',
                scope: {
                    messageObj: '=',
                    callbackObj: '='
                },
                link: this.linkFn
            }
        }
        return this.myDirective;
    }
}

If you’ve read any earlier posts, you’ll know that I like pointers. The ctor() method is used as a function pointer in the main app, and in turn the link: element of the ng.IDirective object points to the linkFn() method in this class.

Unlike interfaces for other languages, I’ve found interfaces most useful for handling the pattern of:

myObj = {
    thing1 : "thing1",
    thing2 : "thing2",
    thing3 : "thing3",
    thing4 : "thing4"
};

I hate those things. I’ll make some typo and not notice until the browser crashes in a test. And because there is often no context, I’ll look at the broken code and not see the problem (thingOne, not thing1, or something like that)

Typescript makes sure that doesn’t happen. This interface:

export interface ICallbackPointers{
    setQueryString : Function;
    addToQueryString : Function;
    rssQuery : Function;
    nlpQuery : Function;
}

Gets declared as a typed object:

export class RssController extends WGLA2_ctrl.Network3DCtrl{
   public callbacks:ICallbackPointers;
   ...

Instanced in the constructor (function pointers!):

this.callbacks = {
    setQueryString : this.setQueryString,
    addToQueryString : this.addToQueryString,
    rssQuery : this.googleNewsSubmit,
    nlpQuery : this.nlpQuerySubmit
};

Passed through the html:

<div class="resultsWrapper">
    <div ng-repeat="item in mc.itemDataArray track by $index">
        <ng-feed-panel message-obj="item" callback-obj="mc.callbacks"></ng-feed-panel>
    </div>
</div>

And used in the directive (also shown above as part of the linkFn()):

scope.nlpQuery = ():void => {
    var mobj:RssControllersModule.IDataResponse = scope.messageObj;
    this.cobj.setQueryString("");
    this.cobj.nlpQuery(mobj);
};

Never a chance for a mistake, but with all the power of ad-hoc object creation. Very cool.

Fat Arrow has been more mysterious. In the following I show two sets of code that use a service to get data from a source. In the fist example all the code is contained within a single class that extends ATSBase, which creates a fat arrow alias for each method. Nonetheless, without fat arrow, ‘this’ does not track back to the class:

public promiseCaller():void{
   this.promise = this.service.getQueries();
   this.promise.then(this.processData, this.errorData);
}

public processData = (data:any):void => {
   // 'this' is out of scope without fat arrow
   console.log("got data");
};

public errorData = (data:any):void => {
   // 'this' is out of scope without fat arrow
   alert("error getting data");
};

However, in the version shown below, ‘this’ is preservedwithin goodUserQuery() and errorResponse.

private goodUserQuery (response:any) {console.log("got data");}
private errorResponse (response:any) {alert("error getting data");}
public promiseCaller():void{
    this.queryService.submit(qstr, this.goodUserQuery, this.errorResponse);
}

It’s even preserved in the call to the queryService.submit call that takes place in a different service class, part of which is shown below:

public submit(query:string, goodResponse:any, errorResponse:any):any {
   return this.httpService(query).then(goodResponse, errorResponse);
}

So that’s odd.

The safe pattern appears to be that for small methods that I’m unlikely to extend, I’ll use fat arrow. Wrapping methods will extend just fine and will use the fat arrow functions inside. But I wouldn’t be able to extend the inner methods. Mostly, I think that’s fine and more likely to keep me out of trouble then accidentally typing ‘this’ when I should’ve typed ‘self’. So you basically have the choice:

scope.handleButtonPressFat = (strVal:string):void => {
   this.handleButtonPress(scope, strVal);
};
scope.handleButtonPress = function(strVal:string):void {
   self.handleButtonPress(scope, strVal);
};

But if you find yourself in a function that has been called out of a promise and closure isn’t working the way you think it should, try seeing if it can be fixed by using fat arrow.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s