Writing Less TypeScript Code in Angular Apps

I have been working on a number of Angular apps in the past 2 years, since I first heard about Angular and started studying the technology.  I wrote my first TypeScript app using TypeScript 0.4, which also used Angular about 1.5 years ago.  I blogged about my experiences previously quite a few times.  Here is typical controller creation code.

 angular.module('app.contacts.contactsController', ['app.core.services.utilities'])
        .controller('app.contacts.contactsController', ['http', 'utilities', '$scope', '$location',
            function (http: IHttp, utilities: IUtilities, $scope: IContactScope, $location: ng.ILocationService) {
                return new ContactsController(http, utilities, $scope, $location);
            }]);

I still advocate for use of TypeScript when writing client heavy web applications.  I feel that TypeScript enhances developer experience and reduces number of errors that developers make.  I include myself in this camp as well.  Speaking from personal experience, I make fewer mistakes when coding in TypeScript vs.  JavaScript.  I recently switched to Controller As syntax, and I tried to use this new approach as an opportunity to create better patterns when writing Angular code using TypeScript.  I also wanted to explore different patterns when structuring the application and reducing the number of Angular modules, which I ended up with quite a few. 

I stick to the idea that I want to structure my code in user modular fashion, grouping my code in folders by user functionality groups.  For example, if one module deals with “Contacts”, here is my folders are laid out.

image

My root code folder is called app.  Then I have contacts folder, divided into controllers, models and services, and potentially directives.  Under controllers I have two controllers, one for each screen.  I put all models into a  single file, though I could further subdivide them.  I also tend to create fewer services than controllers, since they do a lot less work.  For example, my contacts service gets all contracts and one contact, thus it has two methods, one for each of the controllers.  My ContactsModule file just contains the module definition.

Each of TypeScript files that defines controllers and services contains a single TypeScript class.  I assume that everyone will bundle those files eventually, so I stick with one _thing_ per file.  For example, here is how my service looks.

module App.Contacts.Services {
    import IPerson = App.Contacts.Models.IPerson;

    export class ContactsService {

        getPeople(callback: (model: IPerson[]) => void): void {
            this.$http.get<IPerson[]>(this.globals.appUrl + "api/people")
                .success((result) => { callback(result); })
                .error(()=> { alert("Error"); });
        }

        getPerson(id:any, callback: (model: IPerson) => void): void {
            this.$http.get<IPerson>(this.globals.appUrl + "api/people/" + id)
                .success((result) => { callback(result); })
                .error(() => { alert("Error"); });
        }

        constructor(private $http: ng.IHttpService, private globals: App.IGlobals) {

        }
    }
} 

I define TypeScript module, using the same rules I follow in C# namespaces.  Then I export a single class, making it visible outside of this file.  After long consideration I decided to stop writing interfaces for services, as well as controllers.  The main reason is that interfaces result in me defining each method twice, creating more work and more maintenance.  I could not also just export an interface and keep the class private because I could not easily write Jasmine / JavaScript tests without an entity being public.  So, no interfaces for me from now on.  I also started to be more consistent when defining TypeScript classes, making sure that resulting JavaScript puts all methods on prototype.  The code you see above results in exactly that.  I also do not specifically define a “property” to hold $http injectable, simply using private keyword in the constructor instead. 

I also changed how I define the service in the Angular module.  Here is how this code looks.

module App.Contacts {

    angular.module("contacts", [])
        .service("contactsService",
        [
            "$http", "globalConstants", App.Contacts.Services.ContactsService
        ])
        .controller("contactsController",
        [
            "contactsService", "$state", App.Contacts.Controllers.ContactsListController
        ])
        .controller("contactsEditController",
        [
            "contactsService", "$stateParams", App.Contacts.Controllers.ContactsEditController
        ]);
} 

Instead of manually creating an instance of a controller or a service, I simply point to my TypeScript class.  This code works like magic!  The only thing is that you have to create services using service, not factory method.  I put some carriage returns into this code specifically for blogging to keep horizontal scrolling down.  You can put each controller method on the same line with the array of injectables.

if you want to take a look at one of the controllers, here is how list controller looks.

module App.Contacts.Controllers {
    import IPerson = App.Contacts.Models.IPerson;

    export class ContactsListController {
        model: IPerson[];
        viewPerson(person: IPerson): void {
            this.$state.go("person", { id: person.PersonId });
        }
        constructor(
            private contactsService: App.Contacts.Services.ContactsService,
            private $state: ng.ui.IStateService) {

            contactsService.getPeople((result) => { this.model = result; });
        }
    }
} 

In the constructor I specify my injectables that were defined in the module’s above.  I also tend to hide the fact that services use http from controllers by defining my service’s methods to accept callbacks that take the data, instead of promises.  I find that this approach gives me more separation between controllers and services that are used to provide data to them.

In summary, my philosophy is that less code means fewer bugs.  Hence I constantly look for ways to write less code.

Let me know what you think.  You can download sample project here.

Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *