AngularJs mit TypeScript und Resharper Templates


Seit gut 9 Monaten arbeite ich mit TypeScript und AngularJs zusammen und habe mir für die wichtigsten AngularJs Komponenten passende TypeScript Klassen gebaut. In Verbindung mit Resharper lässt sich damit sehr schnell eine funktionierende AngularJs Anwendung mit TypeScript aufsetzen, denn ich habe mir für die wichtigsten AngularJs Komponenten fertige Templates in Resharper hinterlegt. Denn auch hier gibt es wieder X Möglichkeiten eine Anwendung in TypeScript und AngularJs zu schreiben.

Es war mir aber erst mit der Benutzung von TypeScript Klassen möglich auch durchweg eine gute IntelliSense zu erhalten. Da die Implementierung von AngularJs mit TypeScript Klassen aber alles andere als intuitiv ist, war ich froh als ich für jede AngularJs Komponente die passende Klassen Implementierung erstellt hatte, dies war mir vor allem durch die gute Unterstützung der StackOverflow Community möglich.

Meine AngularJs Anwendungen bauen dabei auf folgende Komponenten auf:

  • Twitter Bootstrap 3.x (Styles und Modal)
  • AngularJs 1.4.x
  • Ui-Router
  • Angular-Ui (Modal)
  • AngularStrap für weitere Bootstrap Komponenten wie z.B. Date und Timepicker
  • aktuelle TypeScript Version die von Resharper unterstützt wird.

Ein komplettes sehr einfaches Beispiel, in dem ich alle folgenden Komponenten verwende findet Ihr auch auf meinem GitHub [AngularJsTypeScriptTemplates] Account, das Projekt ist als Visual Studio Solution hinterlegt. Außerdem findet Ihr dort auch meine TypeScript Live Templates für Resharper, diese lassen sich sehr einfach mit Resharper importieren und an eure Bedürfnisse anpassen. Im Folgenden findet Ihr einfach ein Beispiel für die jeweiligen AngularJs Komponenten umgesetzt in TypeScript.

Ich bin natürlich immer gern für Verbesserungsvorschläge offen und freue mich über Kommentare sollte ich etwas “falsch” machen oder wenn ich etwas besser umsetzen kann. Außerdem kann ich nur jedem empfehlen, einmal TypeScript auszuprobieren, ich könnte/möchte inzwischen nicht mehr ohne TypeScript arbeiten wollen, denn IntelliSense und Typsicherheit auch auf Clientseite sind einfach unbezahlbar.

1. Definieren der MainApp als Einstiegspunkt für die Anwendung

In der MainApp werden alle wichtigen Module registriert und z.b. die Routenkonfiguration aufgerufen.

module App {
    export class MainApp {
        static createApp(angular: ng.IAngularStatic) {
            //Alle Module definieren die wir verwenden.
            angular.module("app.main", [
                //Fremdanbieter Module
                "ui.router",
                "ui.bootstrap",
                //Module die mit TypeScript geschrieben wurden einbinden
                App.Controller.TodoModalCtrl.module.name,
                App.Controller.TodoCtrl.module.name,
                App.Services.TodoService.module.name,
                App.Services.ModalService.module.name,
                App.Directives.CtrlAsDirectiveName.module.name,
                App.Directives.DirectiveName.module.name,
                App.Filter.AddPrefixFilter.module.name,
            ])
            .config([
                "$stateProvider", "$urlRouterProvider", "$locationProvider", ($stateProvider: ng.ui.IStateProvider, $urlRouterProvider: ng.ui.IUrlRouterProvider, $locationProvider: ng.ILocationProvider) => {
                    return new Config.RouteConfig($stateProvider, $urlRouterProvider, $locationProvider);
                }
            ]);
        }
    }
}

//Unsere Anwendung intial aufrufen/starten
App.MainApp.createApp(angular);

/*Window Interface erweitern, damit wir auch hier auf z.b. eigene Properties zugreifen können die wir z.B: in alten JS dateien definiert haben */
interface Window {
    siteRoot: string;
}

2. Festlegen der Routen für Ui-Router

module App.Config {
    /**
     * Enthält die Routenconfigutation für unsere Anwendung.
    */
    export class RouteConfig {
        constructor(private $stateProvider: ng.ui.IStateProvider, private $urlRouterProvider: ng.ui.IUrlRouterProvider, private $locationProvider: ng.ILocationProvider) {
            this.init();
        }

        private init(): void {
            //$urlRouterProvider.when('/Home', '/home/index');
            this.$urlRouterProvider.otherwise("Todo");
            //$locationProvider.html5Mode(true);

            //Definieren der Routen die in unserer App zur Verfügung stehen.
            this.$stateProvider
                .state("Todo", {
                    url: "/Todo",
                    templateUrl: "Todo/TodoList"
                })
                .state("Todo.Overview", {
                    url: "/Overview",
                    templateUrl: "Todo/TodoOverview"
                });
        }
    }
}

3. Einrichten eines Controllers

Alle AngularJs Module die man registrieren kann, enthalten einen Bereich “Module Definition”, hier wird der Name des Moduls und der Typ festgelegt und z.B. in der MainApp als Abhängigkeit hinzugefügt. Achtung es wird die “ControllerAs ” Syntax verwendet, bei dem hier gezeigten Beispiel für einen Controller. Achtung beim Inject aufpassen, denn die Reihenfolge im Inject musst der Reihenfolge im Konstruktor der Klassen entsprechen.

module App.Controller {
    //import My = Sq.Internal.ProjectName.Gui.Web.Models;

    //Alle Variablen für unseren Controller im Interface definieren
    export interface ITodoCtrl {
        //Locals
        //viewModel: My.;
        //Functions
        init(): void;
    }

    //Unsere CtrlName Klasse erstellen
    export class TodoCtrl implements ITodoCtrl {
        private locals: CtrlNameLocalsModel = new CtrlNameLocalsModel();
        static $inject: string[] = [
            App.Services.TodoService.module.name,
            App.Services.ModalService.module.name,
        ];

        constructor(private todoService: Services.ITodoService,
                    private modalService: Services.IModalService) {
            this.init();
        }

        /**
        * Initialisieren der wichtigsten lokalen Variablen
        */
        init(): void {
            this.locals.persons = this.todoService.getPersonList();
        }

        public editPerson(personId: number): void {
            this.modalService.editPerson(personId).then(() => {
               //Do Something nachdem das Modal geschlossen wurde.
            });
        }


        //#region Angular Module Definition
        private static _module: ng.IModule;
        /**
         * Stellt das aktuelle Angular Modul für den "CtrlName" bereit.
         */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die abhängigen Module für diesen controller definieren, damit brauchen wir von außen nur den Controller einbinden
            //und müssen seine Abhängkeiten nicht wissen.
            this._module = angular.module('todoCtrl', []);
            this._module.controller('todoCtrl', TodoCtrl);
            return this._module;
        }
        //#endregion
    }

    class CtrlNameLocalsModel {
        persons: App.Person[] = [];
        name : string = "SquadWuschel";
    }
} 

4. Einrichten eines Services

Auch der AngularJs Service enthält wieder einen Bereich für Module Definitionen, mit dem man den Service entsprechend in der MainApp registrieren kann.

module App.Services {
    //import My = Internal.;

    export interface ITodoService {
        getPersonList(): App.Person[];
        getPerson(personId: number): Person;
    }

    /**
     * Beschreibung
     */
    export class TodoService implements ITodoService {
        private persons : App.Person[] = [];

        static $inject: string[] = [];

        constructor() {
            this.persons.push(new App.Person("SquadWuschel", 34, 1337));
            this.persons.push(new App.Person("Squadinator", 33, 7331));
            this.persons.push(new App.Person("SquadLeader", 3232312123.4454545, 3173));
        }

        public getPersonList(): App.Person[] {
            return this.persons;
        }

        public getPerson(personId: number): Person {
            var person: Person;

            this.getPersonList().forEach(value => {
                if (value.id === personId) {
                    person = value;
                }
            });

            return person;
        }

        //#region Angular Module Definition
        private static _module: ng.IModule;
        /**
        * Stellt das aktuelle Angular Modul für "ServiceName" bereit.
        */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die Abhängigen Module für diesen Service definieren.
            this._module = angular.module('TodoService', []);
            this._module.service('TodoService', TodoService);
            return this._module;
        }
        //#endregion
    }
} 

5. Einrichten einer ControllerAs Direktive

Auch in der Definition der Direktive gibt es wieder eine Modul Definition und alle bekannten Properties die zu einer Direktive gehören.

module App.Directives {

    /* 
	* Definition der "Scope" Variablen für die CtrlAsDirectiveName Directive
	*/
    export interface ICtrlAsDirectiveNameScope {
        sqTitle: string;
        isDeleted: boolean;
        model: string;
    }

    /*
	* Beschreibung
	*
	* Verwendung: 
	*  <div></div>
	*/
    export class CtrlAsDirectiveName implements ng.IDirective {
        public restrict: string = "A";
        public replcae: boolean = true;
        public require = "ngModel";
        //public templateUrl: string = 'ScriptsApp/directives/templates/CtrlAsDirectiveName.directives.html';
        public template: string = '<a class="btn btn-default" title="{{ctrl.sqTitle}}">Test Counter: {{ctrl.counter}}</a>';
        public scope = {}

        public controller = CtrlAsDirectiveNameCtrl;
        public controllerAs = "ctrl";
        public bindToController = {
            sqTitle: "=", 
            isDeleted: "=",
            model : "=ngModel"
        }

        //constructor(private $interval: angular.IIntervalService) { }
        constructor() { }

        public link = ($scope: any, element: JQuery, attrs: ng.IAttributes, model: ng.INgModelController) =&gt; {

        }


        //#region Angular Module Definition
        private static _module: ng.IModule;
        /**
        * Stellt die Angular Module für CtrlAsDirectiveName bereit.
        */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die abhängigen Module für unsere Direktive definieren.
            this._module = angular.module('ctrlAsDirectiveName', []);
            //this._module.directive('CtrlAsDirectiveName', ["$interval", ($interval: angular.IIntervalService) =&gt; { return new CtrlAsDirectiveName($interval); }]);
            this._module.directive('ctrlAsDirectiveName', [() =&gt; { return new CtrlAsDirectiveName(); }]);
            return this._module;
        }
        //#endregion
    }

    /*
	* Implementierung unseres CtrlAsDirectiveName Controllers.
	*/
    export class CtrlAsDirectiveNameCtrl implements ICtrlAsDirectiveNameScope {
        public sqTitle: string;
        public isDeleted: boolean;
        public model: string;
        public counter : number;

        static $inject = [];

        constructor() {
            this.init();
        }

        init(): void {
            this.counter = 0;
        }

        /*
		* Eine Funktion die z.B: über ein btn Click des Templates aufgerufen werden kann
		*/
        public doSomethingBtnClick(): void {
            //Zugriff auf die "Scope" Variable mit "this"
            this.sqTitle = "Blubb";
            this.counter++;
        }
    }
}

6. Einrichten eines Filters

module App.Filter {

    /*
     * Beschreibung 
     * 
     * Verwendung: 
     *
     */
    export class AddPrefixFilter {
     
        static $inject: string[] = ["$sce"];
       
        static filter($sce: ng.ISCEService) {
            return (value, text) => {
                return text + ' ' + value;
            }
        }
      
        //#region Angular Module Definition
        private static _module: ng.IModule;

        /**
        * Stellt die Angular Module für AddPrefixFilter bereit.
        */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die abhängigen Module für unsere Direktive definieren.
            this._module = angular.module('addPrefix', []);
            this._module.filter('addPrefix', App.Filter.AddPrefixFilter.filter);
            return this._module;
        }
        //#endregion
    }
}
Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s