Archiv der Kategorie: ASP.NET

Angular 2 mit webpack 2 mit ts, less, sass Loader für Anfänger in Visual Studio 2015 (Webpack Version – Beta 27)


Wäre die Welt nicht schön, wenn man sich nur mit Angular 2 beschäftigen könnte und nicht noch über das Deployment für den Live Betrieb nachdenken muss. Denn dann wäre die Standardinstallation von Angular 2 mit SystemJs völlig ausreichend. Wenn man allerdings seine erste kleine Angular 2 Anwendung erstellt hat, dann merkt man schnell das man sich noch mit dem Live Deployment beschäftigen muss. Denn wenn man die Anwendung startet dann dauert es bis zu 300++ Requests bevor die Seite komplett geladen ist und das ist nicht für Live geeignet.

Da ich bisher nur Angular 1 Seiten mit ASP.NET MVC umgesetzt habe, hatte ich bisher auch nicht viel Kontakt mit “import” Statements, gulp, grunt, webpack oder SystemJs – denn das hat bei Angular 1 alles Visual Studio für mich übernommen.

Jetzt stellte sich mir die Frage warum und weshalb brauche ich diesen neumodischen Kram eigentlich?

Bei Angular 2 kann ich hier leider nicht mehr auf Visual Studio zurückgreifen. Denn bei einer Umsetzung z.B. mit webpack muss klar unterscheiden zwischen dem Ordner “App” in dem wir die Anwendung schreiben (unsere TypeScript Dateien und HTML Templates liegen) und einem Ordner “wwwroot” in dem die “kompilierte” Anwendung (JavaScript Dateien) abgelegt wird, wenn diese fertig kompiliert wurde. Der Webserver ruft dann die “index.html” aus dem “wwwroot” Ordner auf.

Aber wie kompiliert man aus TypeScript Dateien, Less, Sass und HTML Templates eine richtige JavaScript Anwendung?

Mit SystemJs

Im Standardbeispiel von Angular 2 ist dies einfach gelöst, es gibt zum einen das direkte Übersetzen der TypeScript Dateien in JavaScript beim Speichern und SystemJs kümmert sich dann mit Hilfe der “systemjs.config.js” das alle passenden Dateien zur Laufzeit ausliefert werden und die Styles sind im besten Fall direkt auf der Seite eingebunden (300+ Requests).

Daher muss man sich um ein passendes “Bundling” der Dateien kümmern und dafür gibt es mehrere Möglichkeiten. Zum einen kann man gulp und grunt verwenden und die passenden Deployscripte schreiben oder man verwendet npm Scripts mit webpack 2 und schreibt dafür die Deployscripte. Ich habe mich für die zweite Variante entschieden, da es sich dabei aktuell wohl um die bevorzugte Variante zum Bereitstellen von Angular 2 handelt und da es auf der Angular 2 Seite zumindest ein Beispiel für die Umsetzung mit webpack 1 gibt. Ich beziehe mich aber im meinem Blogpost auf die aktuelle Variante webpack Beta 27.

Hinweis zu “npm Scripts”: In der “package.json” in der alle abhängigen npm Pakete für das Projekt angegeben sind, gibt es auch noch die Möglichkeit Kommandozeilen Befehle für npm abzulegen die mit “npm run <scriptname>” ausgeführt werden können, im folgenden Beispiel z.B. “npm run start” wird das “start” Script ausgeführt.

Mit webpack

Also was macht webpack eigentlich und wie funktioniert das ganze dann?

Wenn man webpack verwendet, gibt es wie schon beschrieben ein Verzeichnis in dem man seinen Quellcode hat z.B. “App” hier liegen alle TypeScript Dateien und HTML Templates. Auch unsere “index.html” in dieser sind weder styles noch script Tags enthalten, all das fügt webpack später beim Buildprozess automatisch in die “index.html” ein.

image

Und dann gibt es noch ein Verzeichnis für die Kompilierten Dateien  z.B. “wwwroot” hier liegen dann alle übersetzten und zusammengefassten JavaScript Dateien und Styles – der Ordner wird NICHT mit in die Quellcodeverwaltung eingecheckt.

image

Webpack kümmert sich dann um “alles” d.h. aufgrund der “import” Anweisungen in unseren Dateien weiß webpack genau welche Dateien wir alle benötigen und welche in einer Datei zusammengefasst werden sollen.

Webpack im “Detail”

Für die Implementierung von Angular 2 mit webpack 2 gibt es ein sehr gutes Github repository an dem ich mich orientiert habe. Im Folgenden gehe ich daher nicht auf alle Details ein, sondern beschreibe eher die Probleme auf die ich gestoßen bin. Diese könnt Ihr z.B. oft in den Kommentaren der Quellcode Dateien nachlesen.

Für Visual Studio gibt es eine sehr gute Task Runner Extension für webpack, mit dieser kann man die webpack Tasks direkt in Visual Studio starten vom einfachen Build bis hin zum Webpackserver (dazu weiter unten mehr) und man muss nicht immer eine npm Konsole öffnen.

Allgemeine “App” Einstellungen

Damit nicht der komplette JavaScript Code in einer Datei landet, splittet man diese am besten auf:

  • polyfills.ts – damit die Anwendung auch in älteren Browsern funktioniert muss auch als erstes im Browser geladen werden
  • vendors.ts – Angular Framework und abhängige Komponenten
  • boot.ts – der Root unserer Anwendung

Ich verwende z.B. noch Twitter Bootstrap als globalen Style mit Less und evtl. noch andere Styles z.B. in Sass oder css direkt. Auch die Styles sollen von webpack übersetzt, minifiziert und in der “index.html” automatisch eingebunden werden.

Denn webpack bindet “NUR” die Dateien in den Build Prozess ein, die auch über ein “import” Statement in einer der Dateien polyfills, venders oder boot eingebunden sind oder von diesen mit “import” eingebunden werden.

Damit meine Bootstrap Styles global verfügbar sind habe ich mich für das Einbinden von bootstrap in die vendors entschieden.

Denn wenn man die Styles direkt in einer Komponente als “styleUrls” einbindet, sind diese durch Angular 2, in den Standardeinstellungen nur in der eingebundenen Komponente gültig – ViewEncapsulation.

Ausnahmen für das Einbinden nicht direkt über “import” bilden die “templateUrl” und die “styleUrls”, diese werden von den webpack Loadern erkannt und dann entsprechend geladen.

Webpack Bereitstellungsmöglichkeiten

Die Task Runner Extension für die webpack Bereitstellung in Visual Studio, kann mit einem Rechtklick auf die “webpack.config.js” mit “Taskausführungs-Explorer” geöffnet werden.

image

Es gibt verschiedene Möglichkeiten, wie man die Dateien für den “wwwroot” Ordner von webpack erstellen lassen kann.

  • Manuell, sobald man eine Änderung durchgeführt hat und diese ausprobieren möchte, kann man einfach unter “Run” sein Projekt für die passende Umgebung neu bauen lassen

image

und die Ausgabe könnte dann folgendermaßen aussehen

image

  • Oder man kann einen Watcher ausführen lassen, der das Webverzeichnis der aktuellen Anwendung überwacht und sobald Änderungen an Dateien vorgenommen wurde, wird das Paket automatisch neu gebaut, hier muss dann nur noch mit F5 der Browser aktualisiert werden um die neuen Änderungen auch zu sehen
  • Dann gibt es noch einen einfachen node.js express Webserver der die aktualisierten Dateien bereitstellen kann. Hier gibt es zwei unterschiedliche Modus um die Seite zu aktualisieren.
  • COLD – die Seite wird automatisch komplett neu geladen (wie in meinem Beispiel)
    • HOT – es werden nur die Teile ersetzt die sich geändert haben (HOT wird auch HMR genannt und steht für “Hot Module Replacement”)

Die “webpack.config.js”

Wenn man webpack einsetzt, dann muss SystemJs komplett aus dem Projekt entfernt werden, denn webpack übernimmt dann das Bereitstellen von Development Paketen und den Live (Production) Paketen.

Damit die Visual Studio Extension für webpack auch funktioniert, muss die Datei “webpack.config.js” im Root Verzeichnis der Webseite abgelegt werden.

Da es unterschiedliche Konfigurationen gibt, für Allgemeine Einstellungen, Produktionseinstellungen, Liveeinstellungen und Testeinstellungen wird die Konfiguration in mehrere Dateien aufgeteilt und in meinem Beispiel im Verzeichnis “Config” abgelegt. In der “web.config.js” wird nur ausgewertet welcher Wert als NODE_ENV übergeben wurde über den Aufruf des Kommandozeilenbefehls z.B. für development über den Webpack Taskplaner

cmd /c SET NODE_ENV=development&& webpack -d –color

und es wird dann die passende “webpack.dev.js” Datei aufgerufen.

Die jeweilige webpack Konfiguration, setzt sich dabei aus zwei Dateien zusammen, einer Datei die speziell für die jeweils aufgerufene Umgebung z.B. development (dev) => “webpack.dev.js” die Werte enthält und einer allgemeinen Konfiguration “webpack.common.js”, die für alle Bereitstellungsszenarien immer die gleichen Werte beinhaltet. Diese beiden Konfigurationsdateien werden dann mit Hilfe einer JavaScript Bibliothek von Webpack “webpackMerge” zusammengeführt.

Die “webpack.common.js”

In der Konfiguration wird angegeben welche Startdateien es gibt, in unserem Falle drei “polyfills”, “vendors” und “boot”. Dann gibt es die “module” Sektion in denen die Loader verwendet werden, die wir auch in der package.json entsprechend in den dev-dependencies angeben müssen. Da sich webpack 2 aktuell noch in einer Beta befindet, hatte ich auch entsprechende Probleme die Loader für Less und Sass zum Laufen zu bekommen (siehe Kommentar über dem jeweiligen Loader).

ACTHUNG: Bei den Style Loadern gibt es jeweils einen Loader für die „StyleUrls“ in den Componenten und dann gibt es noch einen Loader für die globalen Styles die direkt per „import“ eingebunden werden, denn beide brauchen unterschiedliche Loader. Bei den jeweiligen Loadern sind die Verzeichnise für die diese zuständig sind per „include“ und „exclude“ festgelegt.

Um die HTML Dateien kümmert sich der “angular2-template-loader” wichtig ist das diese in der “templateUrl” in der Component mit einem relativen Pfad angegeben werden und mit einem “./” starten wie z.B. “./app.component.html”. Was bedeutet die HTML Datei befindet sich im gleichen Verzeichnis wie die Komponente. Gleiches gilt für die “styleUrls”.

ACHTUNG: KEINE ES6 Anführungszeichen verwenden bei den URL Angaben, hier wird dann einfach nur der String ausgeben “./app.component.html” und NICHT das HTML Template.

Wenn ich das ganze richtig verstanden habe, dann kümmert sich der “raw-loader” um die HTML Dateien die der “angular2-template-loader” entsprechend “bereitgestellt” hat.

HINWEIS: Die HTML Dateien die in der Component angegeben werden, werden mit in die bereitgestellten JavaScript Dateien von webpack eingebettet und es gibt keine Extra Dateien dafür im Ausgabeordner. Die Styles werden dank des „ExtractTextPlugin“ in einer Extra Datei abgelegt.

Dann gibt es noch die Plugins, welche teilweise direkt von webpack bereitgestellt werden oder extra in den dev-dependencies mit angegeben werden.

Die Plugins kümmern sich z.B. darum das in der “index.html” auch die richtigen Scripte, Styles und base Url eingebunden werden (HtmlWebpackPlugin und ExtractTextPlugin). Denn im Produktionsmodus wird noch ein Hash mit an den Dateinamen der jeweiligen JavaScript oder Style Datei angefügt, damit nicht ausversehen alte Daten geladen werden. Aus diesem Grund können wir auch nicht direkt die Styles und Scripte in unserer “index.html” verlinken, sondern müssen diese von webpack einfügen lassen.

HINWEIS: Ich habe mich aktuell dazu entschieden bei den DevDependencies immer nur noch die Direkte Version zu verwenden ohne „^“ oder „~“ vor der Versionsnummer, denn wenn Ihr pech habt wie ich, dann wird z.B. TypeScript von Version 2.0.10 auf 2.1.14 angehoben und plötzlich funktioniert das Deployment nicht mehr da es Probleme mit dem „awesome-typescript-loader“ gibt. Vor allem sucht man den Fehler erst einmal bei sich selbst, bevor man daran denkt das node evtl. eine neuere Version gezogen haben könnte die nicht vollständig kompatibel zu allen anderen Paketen ist.

Die “webpack.dev.js”

Die speziellen dev Einstellungen für unsere Anwendung.

Wichtig ist hier z.B. der Wert für “output.publicPath”, denn der hier angegebene Wert wird als Basispfad vor alle Script und Style Tags in der “index.html” eingefügt. (siehe Kommentare in der beigefügten dev Konfiguration)

Daraus ergibt sich dann z.B. die folgende “wwwroot\index.html” wenn das ganze erfolgreich kompiliert wurde.

Hier kann man gut erkennen, das wir aktuell die Daten vom webpack Server holen, der auf Port 3000 lauscht, auch das können wir entsprechend in der “webpack.dev.js” einstellen.

Webpack Zusammenspiel mit Visual Studio

Ich habe zwar aktuell keine Backend Funktionen in .NET implementiert, aber Visual Studio dient aktuell nur noch als Backend um z.B. Daten aus der .NET Welt im Browser anzuzeigen. Im Development Modus laufen am Ende zwei Webserver. Einer im IIS der unsere Anwendung hostet und die “index.html” aus dem wwwroot aufruft und der webpack webserver der die aktuellen Scripte und Styles bereitstellt.

Im Zusammenspiel mit der folgenden RouteConfig

und dem einfachen HomeController für den Index Aufruf.

Wird kein “Views” Ordner mehr benötigt.

Beispiel “installieren”

  1. Webpack Taskrunner Extension für VS 2015 installieren und VS neustarten
  2. Das Projekt von GitHub laden und in VS 2015 öffnen (Achtung hier sind noch mehr Projekte enthalten, bitte darauf achten, das diese nicht mit gebaut werden sondern nur “Angular2MitWebpack2”.
  3. das Verzeichnis mit einer NPM Konsole öffnen und mit Hilfe von “npm install” alle Pakete installieren lassen
  4. Den Taskrunner öffnen und einmal das “Run – Development” ausführen lassen und danach dann “serve – Cold” starten
  5. Die Webseite starten, diese sollte jetzt funktionieren.

Quellen:

https://webpack.js.org/

https://github.com/AngularClass/angular2-webpack-starter

https://offering.solutions/articles/asp-net/how-to-set-up-angular-2-and-webpack-in-visual-studio-with-asp-net-core/

Angular 4 “Hello World” mit Visual Studio 2015 Update 3, ASP.NET 4 und TypeScript (aktualisiert für Angular 4.0.3 Release)


Da es nicht jedem Entwickler vergönnt ist immer auf die neuesten Technologien zurückgreifen wie z.B. ASP.NET Core, dachte ich mir ich versuche es mal mit dem guten alten ASP.NET 4 und Angular 2. Aber das hat gedauert bis ich Angular 2 bei mir zum “Laufen” bekommen habe, denn es gibt für Angular 2 und den zugehörigen Abhängigkeiten kein NuGet Paket mehr. Denn leider scheint NuGet für viele JavaScript Bibliotheken an Bedeutung zu verlieren. Damit muss man auch in Visual Studio auf npm (Node Package Manager) und Konsorten zurückgreifen.

1. Erstellen einer ASP.NET 4 Anwendung

Als erstes erstellen man eine ASP.NET 4 Anwendung im Visual Studio und geht auf “File –> New –> Project” und wählt die “ASP.NET Web Application” aus.

image

Dann eine MVC Anwendung anlegen ohne Authentifizierung (enthält einfach weniger Code und die Authentifizierung wird in unserem Beispiel nicht benötigt).

image

2. NPM Konfiguration zu unserem Projekt hinzufügen

Da es leider kein NuGet Paket für Angular 2 gibt, muss man eine “package.json” im Root der Anwendung anlegen.

image

imageimage

welche Packages benötigt werden, findet man in der aktuellen Angular 2 Dokumentation, denn dabei handelt es sich nicht nur um eine einfache Angular 2 JavaScript Datei, sondern hier sind noch weitere Komponenten notwendig. Warum und weshalb kann man auch in der Dokumentation nachlesen. Aktuell sieht die Json Datei dann z.B. folgendermaßen aus

ACHTUNG: Ich habe zu den Abhängigkeiten die auf der Angular Seite angezeigt werden, noch die aktuelle “gulp” Version hinzugefügt (Warum erläutere ich weiter unten)

ACHTUNG: Wenn man von der Beta auf die RC Version aktualisiert, würde ich empfehlen den node_modules Ordner vorher einmal zu löschen, da Angular jetzt in einem anderen Order zu finden ist und sich sehr viel strukturell geändert hat.

ACHTUNG: Ich gebe inzwischen die Versionsnummern nur noch ohne „~“ und „^“ davor an. Denn damit hatte ich in der letzten Zeit vor allem bei den devDependencies immer wieder Probleme, das hier Pakete veröffentlicht wurden so das dass Projekt am ende nicht mehr funktioniert hat und wenn man „~“ und „^“ weglässt kann man sich „sicher“ sein das man genau die Pakete wieder bekommt und das alles noch funktioniert wie bisher.

Wenn man jetzt mit der rechten Maustaste auf die package.json geht, gibt es dort einen Punkt “Restore Packages”, mit diesem werden wie bei NuGet alle Pakete heruntergeladen für unsere package.json. In ein Verzeichnis mit dem Namen “node_modules”. Das Verzeichnis wird NICHT unserem Projekt hinzugefügt und kann nur angezeigt werden, wenn man versteckte Dateien einblendet.

image

Wenn Ihr glück habt, werden alle Dateien heruntergeladen. Das sieht man, wenn man die Output Konsole öffnet und “Bower/npm” als Ausgabe anzeigen lässt, wenn hier keine Fehler auftreten ist alles OK.

image

Bei mir kam es hier aber zu einem Fehler, da Visual Studio seine eigene Installation von npm mit sich bringt, diese aber zum Herunterladen der Komponenten “veraltet” war, musste ich die neuste Version von npm installieren und dann im Visual Studio eintragen. Dafür geht man in die QuickLaunch box rechts oben und tippt “External Web Tools” ein und wählt den Eintrag aus. (siehe auch Visual Studio 2013/2015 bereitgestellte nodejs Version veraltet)

image

In “External Web Tools” dann den Pfad zum aktuellen nodejs Verzeichnis hinzufügen und den Eintrag an den Anfang verschieben. Dann am besten Visual Studio noch einmal neu starten und versuchen das “Restore Packages” noch einmal auszuführen, diesmal hoffentlich ohne Fehler.

image

HINWEIS: Mit dem Update auf Angular 2 RC1 kam es bei mir zu einem weiteren Fehler und im Output Fenster für “Bower/npm” stand z.B. folgende Fehlermeldung:

npm ERR! 404 '@angular/http' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it

Ab der RC1 von Angular 2 ist wichtig das man mindestens NodeJs 6.x installiert hat und NPM mindestens Version 3.8.9 installiert hat, denn nur dann werden die neuen Packages erkannt und können heruntergeladen werden. Leider geht bei mir zumindest die IntelliSense für die Erkennung der Versionsnummern im package.json für die Angular Pakete mit dem “@” davor nicht mehr.

3. Wozu ist überhaupt dieses “gulp” da?

Wir haben zwar unsere Module alle heruntergeladen die in der package.json eingetragen waren, aber diese liegen in einem eigenen Verzeichnis “node_modules” in dem noch viel mehr Dateien liegen. Mit gulp kann man eine einen “Task” erstellen der z.B. vor jedem starten des Projekts alle für uns wichtigen JavaScript oder Stylesheet Dateien in den lokalen Scripts oder Styles Ordner kopiert oder temporäre Dateien löscht oder Minifizierungen durchführt. Dafür muss im Root unserer Webseite eine “gulpfile.js” angelegt werden mit dem folgenden Inhalt.

Wenn man jetzt mit der rechten Maustaste auf die “gulpfile.js” klickt, gibt es einen Punkt “Task Runner Explorer”, diesen bitte anklicken.

image

und es öffnet sich ein neues “Fenster”. Wenn dies noch “leer” aussieht, dann am besten Visual Studio neu starten.

image

Nach dem Neustart sollte das ganze folgendermaßen aussehen

image

Mit der rechten Maustaste auf “moveToScripts” kann man das ganze auch direkt einmal ausführen und wenn das Ausführen erfolgreich war, können wir in unseren Scripts Ordner gehen und hier alle Dateien anzeigen lassen, denn diese wurden nicht dem Projekt hinzugefügt, sondern nur in den Ordner kopiert.

image

image

Ich habe diese dann in das Projekt eingebunden mit “Include In Project”

image

Update Angualar 2.1.1: In der neuesten Version, kopiere ich die Dateien aktuell nicht mehr per Gulp ins lokale Scripts Verzeichnise, sondern verweise direkt auf die Pfade im node_modules ordner, da dieser sowieso vorhanden sein muss und dies erspart uns vorerst etwas Konfigurationsaufwand. Für den Produktivmodus wäre es aber mit sicherheit besser diese per Bundle auszuliefern.

3.1 TypeScript Einstellungen (veraltet ab VS 2015 Update 3)

Am besten ist es wenn man die neuste Version von TypeScript installiert hat, denn bei mir gab es erst ab Version 1.8 keine Probleme mehr, obwohl eigentlich 1.5 ausreichen sollte. Dann müssen wir eine erste TypeScript Datei in unserem Projekt hinzufügen, am besten unter: “ScriptsApp/app.modules.ts”.

scriptappordner

Jetzt öffnen wir die Einstellungen (Properties) der Webseite und schauen uns die “TypeScript Build” Einstellungen an, jetzt sollten die Einstellungen auch in der csproj generiert sein.

image

Ich habe es leider nicht hinbekommen in der alten Visual Studio Version 2015 Update 2 TypeScript über eine “tsconfig” zu konfigurieren in der Neuesten Version von VS 2015 Update 3 ist dies auch mögloch über die tsconfig (weiter unten eine genauer Erläuterung dazu).

Da Angular 2 mehrere “experimentelle TypeScript Features” verwendet  die erst noch aktiviert werden müssen. Muss man die “csproj” Datei der Webseite bearbeiten, denn die Features die man benötigt, können nicht in den Einstellungen der Webseite eingestellt werden. Dafür muss die Webseite entladen werden mit “Unload Project”.

image

Danach kann man wieder mit der rechten Maustaste auf das entladene Projekt gehen und es bearbeiten mit “Edit …csproj”. Und in der csproj am besten nach “TypeScriptTarget” suchen und schon sieht man einen Block in dem einige TypeScript Einstellungen vorgenommen werden können.

image

Ich konnte mein Projekt mit Angular 2 erst erstellen, nach dem ich den folgenden Block mit Einstellungen für TypeScript eingefügt habe und alle anderen TypeScript Settings damit überschrieben habe.

sonst kam beim Erstellen der TypeScript Dateien immer der Fehler “module not found” und das Projekt konnte nicht kompiliert werden. Jetzt kann man die csproj wieder schließen und unser Projekt wieder laden.

TypeDefinitions für Angular 2 brauchen wir dem Projekt keine hinzufügen, diese scheint er sich über den “node_modules” Ordner (obwohl dieser ausgeblendet ist) bereits selbst zu suchen, denn dort sind bereits alle wichtigen TypeDefinitions mit enthalten (hierbei handelt es sich aber nur um meine aktuellen Beobachtungen, wenn hier jemand genaueres weiß warum das funktioniert, freue ich mich über einen Kommentar).

3.2 TypeScript Einstellungen über tsconfig.json

Ab Visual Studio 2015 Update 3 ist es auch möglich in älteren MVC Projekten auf die tsconfig.json zurückzugreifen und nicht mehr wie in 3.1  beschrieben über die Projekteinstellungen. Dafür muss im Root der Webseite, da wo auch die „package.json“ liegt eine „tsconfig.json“ angelegt werden mit dem folgenden Inhalt.

Wenn alles geklappt hat und man die Projekteigenschaften aufruft für die „TypeScript-Build“ Einstellungen, sollte hier  alles deaktiviert sein und angeben sein das mindetens eine „tsconfig.json“ gefunden wurde in der jetzt die Einstellungen konfiguriert werden.

typescriptbuildsettings

4.Typings installieren für Visual Studio (Veraltet ab TypeScript Version 2.0+)

Damit Visual Studio auch alle Typen erkennt, muss man noch die passende index.d.ts Datei hinzufügen. Und am einfachsten installiert man diese mit der Package Installer Extension, hier muss nur „Typings“ ausgewählt werden und „es6-shim“ eingetragen werden und schon werden die passenden Typings installiert.

image.png

Der Pfad zur index.d.ts Datei wird dann in der boot.ts entsprechend angegeben. Die von NPM hinzugefügten Dateien werden NICHT dem Projekt hinzugefügt.

image

4.1 Typings in Visual Studio ab TypeScript 2.0+

Sobald man TypeScript 2.0 oder höher verwendet, kann man auf die Typings „verzichten“ und diese werden direkt in der tsconfig.json eingestellt. Hier kommt bei „compileroptions“ das Property „lib“ hinzu. Bei lib handelt es sich um ein Enum in dem man die Umgebung angeben kann die in den TS Dateien bekannt gemacht werden soll, damit z.B: Promises erkannt werden. Wenn man zusätzlich auch noch bestimmte Typen benötigt die man über npm installiert hat wie z.B. jasmine mit „@types/jasmine“ dann gibtes noch ein neues Property „types“ hier kann man dann die customtypes noch angeben die in den TS Dateien bekannt sein sollen.

Daher wird auch in der „boot.ts“ kein Verweis mehr auf die „index.d.ts“ benötigt und kann entfernt werden. Ebenso kann die „typings.json“ und der Ordner „typings“ entfernt werden.

ACHTUNG es reicht nicht aus den Ordner „typings“ aus dem Projekt auszuschließen dieser muss auch im Filesystem entfernt werden, sonst kommt es zu Fehlern, das z.B. Promise bereits bekannt ist.

5. Angular 2 “Hello World App”

So jetzt nachdem wir unser Projekt entsprechend vorbereitet haben, können wir auch noch unsere Angular 2 Anwendung schreiben.

Dazu erstellen wir noch zwei TypeScript Dateien „boot.ts“ und „app.module.ts“ auch direkt im „ScriptsApp“ Verzeichnis.

scriptappordner

Der Inhalt der boot.ts sieht folgendermaßen aus.

HINWEIS: “./” vor mainApp gibt an das im aktuellen Verzeichnis nach der Datei gesucht werden soll.

Die „boot.ts“ Datei gilt als Einstieg in unsere Anwendung und ruft die „app.module.ts“ Datei auf in der alle wichtigen globalen Module, Componenten und Services definiert sind und die dann auch unsere Anwendung die „app.component“ enthält.

für die app.component.ts sieht der Code folgendermaßen aus und “my-app” ist der Selector mit dem wir unsere neue Komponente entsprechend anzeigen können.

ACHTUNG: Wenn man von Angular 2 Beta zur RC Updated müssen auch alle Pfade in den imports aller Projektdateien angepasst werden und aus “angular2” wird “@angular” z.B. aus “angular2/core” wird “@angular/core”.

Dann habe ich die _Layout.cshtml etwas aufgeräumt und nur noch die wichtigsten Einträge hinzugefügt, wie die Verweise auf unsere JavaScript Dateien und das Einrichten von SystemJs um unsere Dateien zu laden sowie unser neues Tag “my-app”.

Angular 2 selbst wird in den Scripts seit der RC nicht mehr direkt verlinkt, sondern dafür wird jetzt die SystemJs Konfiguration verwendet.

6. SystemJs konfigurieren

Damit Angular auch weiß wo er welche Module für Angular 2 zu finden sind, muss SystemJS noch konfiguriert werden. Ich habe dafür eine Datei mit dem Namen “systemjs.config.js” im Scripts Verzeichnis angelegt. In dieser Datei werden alle verwenden Angular 2 Module definiert und es wird angegeben wo diese zu finden sind. Ich habe diese im node_modules Ordner gelassen, denn im Beispiel auf der Angular 2 Seite wird es ebenfalls so gehandhabt. Mir ist hier aufgefallen, das die „systemjs.config.js“ jedes mal „anders“ aussieht und aufgeräumter wirkt, daher wenn Ihr Angular aktualisiert prüft auch nach ob Ihr evtl. die „systemjs.config.js“ mit anpassen müsst.

Hinweis: Für die Produktive Bereitstellung einer Angular 2 Anwendung sollte man z.B. webpack 2 verwenden, hier werden alle wichtigen Bibliotheken in eine JavaScript Datei zusammengefasst und nicht wie bei SystemJs 300+ Requests beim Aufrufen der Seite erzeugt. Ich habe dafür den folgenden Artikel erstellt „Angular 2 mit webpack 2 mit ts, less, sass Loader für Anfänger in Visual Studio 2015

Wenn man jetzt die Webseite startet, dann sollte das ganze funktionieren und im Browser z.B. folgendermaßen aussehen.

image

Wenn nichts angezeigt wird, am besten die Entwicklerkonsole öffnen und schauen was für ein Fehler angezeigt wird, diese sind wie ich finde inzwischen doch recht aussagekräftig, zumindest im Vergleich zu NG1.

HINWEIS: Es muss Resharper ab Version 2016.2 verwendet werden, da sonst die Componenten nicht erkannt werden oder die neuen Angular Tags im HTML nicht erkannt und werden und dann rot unterstrichen sind.

HINWEIS: Wenn Ihr „automatisch“ aus euren .NET WebAPI oder Controller Aufrufen einen Angular 2 Service erstellen wollt, dann versucht doch einmal den ProxyGenerator. Der besteht aus einem T4 Template mit dem Ihr voll typisierte Angular 2 Services erstellen könnt direkt aus den .NET Klassen.

Quelle:

http://www.mithunvp.com/angular-2-in-asp-net-5-typescript-visual-studio-2015/

https://yakovfain.com/2016/05/06/starting-an-angular-2-rc-1-project/

Das zugehörige Projekt auf meiner GitHub Seite sollte voll funktionsfähig sein. Aber bitte daran denken das man die passenden NPM Befehle noch ausführen muss für die Typings und den Package Restore für die package.json.

https://github.com/squadwuschel/Angular2Introduction/tree/master/Angular2HelloWorld

Alternativ gibt es noch eine etwas erweiterte Einführung in der auch der Ui-Router eingebunden ist und man sieht wie man einen Service einbindet.

https://github.com/squadwuschel/Angular2Introduction/tree/master/Angular2Introduction/Angular2Introduction

Updated from Angular 2.0 RC1 to RC2

Für das Update von RC1 auf RC2 habe ich lediglich die „package.json“ aktualisiert und die „systemjs.config.js“ an das Beispiel vom Angular Quickstart angepasst und schon sollte alles wieder funktionieren.

Updated from Angular 2.0 RC2 to RC3

Einfach nur die „package.json“ angepasst und die Pakete aktualisiert.

Updated from Angular 2.0 RC3 to RC4

Einfach nur die „package.json“ angepasst und die Pakete aktualisiert.

Updated from Angular 2.0 RC4 to RC5

Einfach nur die „package.json“ angepasst und die Pakete aktualisiert. Außerdem wurde das Beispiel um eine „appModule.ts“ Datei erweitert in der jetzt per „ngModule“ Decorator Globale Importe für das gesamte Projekt definiert werden können wie HTTP_PROVIDERS oder FormsModule für „(ngModule)“. Die Datei ist jetzt auch in der offiziellen Hello World Anwendung von Angular 2.0 vorhanden und ich habe mein Beispiel nur entsprechend angepasst.

Updated from Angular 2.0 RC5 auf Angular 2.0.0 Release

Die „package.json“ angepasst und die Pakete aktualisiert. Außerdem habe ich die Typings aktualisiert und es gibt jetzt keine „browser.d.ts“ Datei mehr, sondern eine „index.d.ts“ auf die für die passendne Typings verwiesen werden muss. Wenn man bereits ein größeres Beispiel hatte welches z.B. HTTP_PROVIDERS verwendete, kommt es zu einem Fehler denn diese gibt es nicht mehr, sondern nur noch das „HttpModule“. Außerdem gibt es in „@component“ nicht mehr den Verweis auf „directives“ die darin verwendet werden, sondern diese werden jetzt alle in ngModule eingetragen. Das Beispiel auf meiner GitHub Seite verwendet die ngModules bereits und definiert auch dort die passenden Components.

Update from Angular 2.0.0 auf 2.1.1 (31.10.2016)

Ich habe die tsconfig.json hinzugefügt um eine alternative für die TypeScript Kompillierung für Visual Studio 2015 ab Update 3 und älteren MVC Projekten zu zeigen. Außerdem habe ich eine komplet Neue Angular 2 Hello World Anwendung auf Github hinzugefügt.

Update (07.12.2016) – Es gibt es jetzt einen neuen Artikel der die Verwendung von Angular 2 mit Webpack 2 beschreibt und nicht mit SystemJs.

Update (28.02.2017) – package.json angepasst auf Angular 2.4.0 und eine neue Möglichkeit Typings in Visual Studio zu verwalten hinzugefügt – denn ab TypeScript 2.0 wird kein „typings“ Ordner mehr benötigt in Visual Studio.

Update (16.05.2017) – package.json angepasst auf Angular 4.0.3

ASP.NET MVC Custom Error Handler Attribute und Ajax Calls


Normalerweise, fange ich die meisten Fehler direkt im Code ab und habe in jedem Ajax Request ein erweitertes Response Model was immer von einer Basisklasse ableitet in dem ich Nachrichten oder Fehlermeldungen mit übergeben kann.

Sobald also bei einem Response Meldungen enthalten sind, dann werden diese z.B. von AngularJs per Interceptor ausgewertet und im UI angezeigt. Das klappt auch sehr gut so lange man ein “aufgeblähtes” Model zurückgeben möchte. Wenn ich aber nur Daten für ein Typeahead benötige, was nur eine List<string> zurückgeben soll, dann möchte ich hier nicht eine Extra Klasse anlegen die nur ein Property für die List<string> enthält, nur damit ich eine Fehlermeldung mit zurückgeben kann. Für diese Fälle habe ich jetzt den Custom Error Handler für mich entdeckt.

Der Custom Error Handler wird aufgerufen, sobald eine Exception auftritt die nicht abgefangen wird und bis zum Controller Aufruf durchgereicht wird. Hier wird die Exception dann abgefangen und man kann den Ajax Call entsprechend ändern der zurückgegeben werden soll und den Fehler zurückgeben z.B. in der von mir bevorzugten Struktur, damit der Interceptor den Fehler auch abfangen und anzeigen kann.

Code Custom Error Handler:

/// <summary>
/// Custom Error Handler der über der passenden Controller Funktion verwendet wird um unbehandelte Ausnahmen abzufangen.
/// </summary>
public class CustomErrorHandlerAttribute : HandleErrorAttribute
{
    public bool IsAjaxCall { get; set; }

    public override void OnException(ExceptionContext context)
    {
        if (IsAjaxCall)
        {
            context.ExceptionHandled = true;
            var jsonResult = new JsonResult();
            jsonResult.Data = new {Message = context.Exception.Message , MessageType = "Error"};
            jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
            jsonResult.ExecuteResult(context);
        }
        else
        {
            // if not an ajax request, continue with logic implemented by MVC -&gt; html error page
            base.OnException(context);
        }
    }
}

Beispiel Controller Methode die einen Fehler verursacht:

[CustomErrorHandler(IsAjaxCall = true)]
public JsonResult AddJsEntryOnly(Person person)
{
    throw  new Exception("Beim Laden der Daten ist ein Fehler aufgetreten!");
    return Json(person, JsonRequestBehavior.AllowGet);
}

Wenn ich den Rückgabewert (Ajax Call)  jetzt im Browser in der Konsole ausgeben lasse steht da:

image

Visual Studio 2015 ausgewählte Features


Da Visual Studio 2015 vor kurzem sein erstes großes Update erhalten hat, habe ich mich jetzt auch “getraut” darauf umzusteigen vom VS 2013 auf VS 2015 und ich muss sagen ich möchte es nicht mehr missen. Denn es gibt doch ein paar sehr gute Neuerungen für die sich der Umstieg lohnt, wie ich finde.

Das gute ist, das die alten Solution Files vom VS 2013 auch im VS 2015 noch problemlos geöffnet werden können und man diese danach auch wieder mit VS 2013 öffnen kann, damit kann man bei Bedarf beide Versionen parallel betreiben ohne größere Probleme.

Im Folgenden ein paar Verbesserungen im Visual Studio 2015 die ich aktuelle nicht mehr vermissen möchte.

1. Anzeige der Ausführungszeit zwischen zwei Haltepunkten

Wenn man früher wissen wollte wie lange die Ausführung zwischen zwei Haltepunkten gedauert hat, dann muss man eine Stopwatch einbauen, diese Zeiten sind nun endlich vorbei, denn ab VS 2015 zeigt der Debugger automatisch die Zeit an die zwischen zwei Haltepunkten vergangen ist, direkt hinter dem aktuellen Haltepunkt.

image

oder auch beim durchgehen nach jedem Step (F10)

image

2. Überarbeitetes NuGet Fenster

Auch das aktualisieren und Abrufen von NuGet Paketen wurde komplett überarbeitet und wird jetzt nicht mehr in einem Popup angezeigt, sondern ist direkt in VS integriert. Es lassen sich jetzt auch einzelne Pakete deinstallieren die noch Abhängigkeiten besitzen ohne diese extra zu deinstallieren. Außerdem sieht man ob es im Projekt gleiche Pakete mit einer unterschiedlichen Versionsnummer gibt. Man kann auswählen welche Version man installieren möchte und muss nicht immer nur auf die neueste upgraden, um nur ein paar der Neuerungen der neuen NuGet Version anzusprechen.

image

3. Quellcode im Debugmodus ändern

Leider war es bisher nicht möglich in Webapplikation, beim Debuggen den Quellcode zu ändern und die Seite dann einfach weiterlaufen zu lassen. In VS 2013 musste das Webprojekt dafür noch gestoppt werden. Mit VS 2015 ist es endlich möglich auch beim Debuggen den Code on the fly zu ändern.

4. Code Highlighting im HTML Editor von AngularJs Elementen

Es gibt im HTML Editor zwar immer noch kein IntelliSense für AngularJs Objekte aber für die nativen AngularJs Direktiven wird der Inhalt der Attribute jetzt in einer anderen Farbe dargestellt, damit erkennt man in großen HTML Blöcken jetzt wesentlich leichter die AngularJs “Code Snippets” in den Direktiven.

image

Leider funktioniert das nur bei den nativen AngularJs Kürzeln. Bisher habe ich leider noch nicht herausgefunden ob man das ganze auch für seine eigenen Direktiven wie “my-app” im obigen Screenshot einstellen kann.

5. Debuggen von Lambda Ausdrücken

Endlich kann man Lambda Ausdrücke auch im Quickwatch auswerten und den Inhalt anzeigen lassen.

image

Im Watch kann man diese dann entsprechend anzeigen lassen

image

Oder im “Immediate Window” kann man damit jetzt auch rumspielen

image

6. Verbesserte Tooltips im Code für const und Enums

Auch die Anzeige der Tooltips vom VS 2015 wurde verbessert, so sieht man jetzt z.B. im Tooltip zu einer Kontanten oder einem Enum den Inhalt der Konstanten oder den Wert des ausgewählten Enums.

image

image

7. Speichern des aktuellen Window Layouts

Wer kennt das leidige Problem nicht, man hat einen Laptop auf Arbeit und mehrere Monitore daran angeschlossen und sein VS schön auf alle Monitore verteilt und dann loggt man sich von daheim per Remotedesktop auf Arbeit ein und alles ist im “Ars….”, damit ist jetzt Schluss. Denn mit VS 2015 kann man sein aktuelles Fensterlayout speichern.

image

und ganz einfach wieder laden per Shortcut oder Menüpunkt

image

8. Erweitertes CodeLens ab VS 2015 Professional

Leider ist CodeLens noch nicht in der Community Edition angekommen, aber wer die Professional sein Eigen nennen darf und noch eine aktuelle Quellcodeverwaltung verwendet, der kann sich über eine erweiterte CodeLens Funktionalität freuen.

image

Denn man sieht im CodeLens jetzt auch wer die Funktion als letztes bearbeitet hat und wie viele Änderungen daran insgesamt vorgenommen wurden. Wenn man auf die Änderungen klickt, geht ein kleines OnScreen Fenster mit dem Verlauf dazu auf.

ASP.NET MVC HandleUnknownAction zum Zurückgeben des aufgerufenen Views verwenden


In vielen meiner MVC Projekten, in denen ich auf AngularJS für das UI setze, verwende ich immer noch die Standard MVC Views mit “Layout = null”, um das HTML Template auch .NET Seitig noch manipulieren zu können. Dabei gibt es dann eine Menge Controller Funktionen die einfach nur ein ActionResult mit dem zugehörigen View zurückgeben z.B.:

public class TodoController : Controller
{
   #region Views
   public ActionResult TodoOverview()
   {
       return View();
   }
   #endregion

   #region Modals
   public ActionResult TodoEditModal()
   {
       return View();
   }
   #endregion
}

Dabei handelt es sich immer wieder um den gleichen Boilerplate Code und zum Glück gibt es auch hier eine Möglichkeit dies mit ASP.NET MVC zu optimieren und nur noch eine Funktion “HandleUnknownAction” zu verwenden.

public class TodoController : Controller
{
    protected override void HandleUnknownAction(string actionName)
    {
       try
        {
           this.View(actionName).ExecuteResult(this.ControllerContext);
         }
         catch (System.Exception)
         {
            this.View("404").ExecuteResult(this.ControllerContext);
          }
     }
}

Diese Überschriebene Funktion wird immer aufgerufen, wenn zum zugehörigen Controller Aufruf keine passende Action im aktuellen Controller gefunden werden kann und versucht dann automatisch den übergebenen Actionnamen auf einen existierenden View zu Mappen.

Wenn es hier noch andere oder bessere Lösungen gibt freue ich mich über einen Kommentar, aber aktuell habe ich nichts besseres finden können.

Quelle:

http://stephenwalther.com/archive/2008/07/21/asp-net-mvc-tip-22-return-a-view-without-creating-a-controller-action

Eine AngularJs 1.x SPA mit TypeScript 1.4 erstellen


In meinem letzten Eintrag habe ich bereits einen kurzen Einblick in die Verwendung von TypeScript und Visual Studio gegeben. Da ich in einem Großteil meiner Projekte AngularJs verwende hat mich ebenfalls interessiert, wie man mit TypeScript 1.4 und AngularJs 1.x eine Anwendung schreibt und in der Anwendung z.B. “alte” JavaScript Direktiven verwendet.

Ich verwende in meiner Anwendung die folgenden Komponenten:

  • ASP.NET MVC zum Bereitstellen der AJAX Funktionen (Daten) und Views
  • AngularJs 1.3
  • Ui-Router
  • TypeLite zum erstellen von TypeScript Interfaces für ausgewählte .NET Klassen die mit einem passenden Attribut gekennzeichnet werden
  • TypeScript 1.4

Die Anwendung selbst ist in unterschiedliche “Komponenten” (Dateien) aufgeteilt, welche sich im “ScriptsApp” Ordner im Root der Webseite befinden. Um meine Anwendung besser zu strukturieren verwende ich in meinem Beispiel ebenfalls TypeScript “module” was einem Namespace in .NET ähnelt bzw. entsprechen soll und der Namespace entspricht hier prinzipiell der Ordnerstruktur.

image

1. Main App

Der zentrale Ausgangspunkt ist die “app.main.ts” Datei, in der die zentrale Moduldefinition abgelegt wird und die Anwendung Initial aufgerufen wird. Wichtig ohne die Zeile “App.MainApp.createApp(angular)” – würde hier “nichts” passieren denn die App würde nie gestartet/erstellt.

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",
                "mgcrea.ngStrap.datepicker",
                //Eigene Module einbinden
                "deafaultModal.directives",
                //Module die mit TypeScript geschrieben wurden einbinden
                Views.MainAppCtrl.module.name,
                Views.Todo.TodoOverviewCtrl.module.name,
                Views.Todo.TodoEditModalCtrl.module.name,
                Views.Shared.TodoModalService.module.name,
                Views.Shared.TodoListenService.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);

Wie man bereits bei der Moduldefinition erkennen kann stellt es keine Probleme dar TypeScript Komponenten mit “alten” JavaScript Komponenten zu verwenden. Ich habe z.B. das Modal von ui-bootstrap problemlos verwenden können. Wichtig ist nur das die aus dem TypeScript erstellten JavaScript Dateien in der richtigen Reihenfolge eingebunden werden z.B. in der “BundleConfig”.

bundles.Add(new ScriptBundle("~/bundles/mainApp")
    .IncludeDirectory("~/ScriptsApp/directives", "*.js", true)
    .Include(
            "~/ScriptsApp/Views/routeConfig.js",
            "~/ScriptsApp/Views/mainCtrl.js",
            "~/ScriptsApp/Services/todoPSrv.js",
            "~/ScriptsApp/Views/Shared/todoListenService.js",
            "~/ScriptsApp/Views/Shared/todoModalService.js",
            "~/ScriptsApp/Views/Todo/todooverviewctrl.js",
            "~/ScriptsApp/Views/Todo/todoEditModalCtrl.js",
            "~/ScriptsApp/Views/app.main.js"
            ));

  2. Controller

Damit man auch das gesamte Potential von TypeScript ausnutzen kann, definiere ich für jeden Controller/Service noch ein passendes Interface über die bereitgestellten Funktionen und Member für diesen Controller.

//Alle Variablen für unseren Controller im Interface definieren
export interface ITodoOverviewCtrl {
    //Locals
    searchModel: My.ITodoOverviewSearchModel;
    resultModel: My.ITodoOverviewResultModel;
    initModel: My.ITodoOverviewInitModel;
    locals: LocalsModel;
    //Functions
    init(): void;
    search(): void;
    deleteEntry(id: number): void;
    toggleNewEntry(): void;
    getPrioString(prio: MvcTypeScript.Helper.Prioritaet): string;
    listenService: App.Views.Shared.ITodoListenService;
}

Der Controller selbst bindet dieses Interface ein und implementiert die entsprechenden Methoden und Member. Damit man die Dependency Injection von AngularJs verwenden kann, benötigt man nur “$inject” und die entsprechenden Modulnamen die Injected werden sollen, dabei ist wie immer die Reihenfolge ausschlaggebend, denn die muss der im constructor entsprechen, wie im Standard AngularJs ohne TypeScript.

 

//Unsere TodoController Klasse erstellen
export class TodoOverviewCtrl implements ITodoOverviewCtrl {
      searchModel: My.ITodoOverviewSearchModel;
      resultModel: My.ITodoOverviewResultModel;
      initModel: My.ITodoOverviewInitModel;
      listenService: App.Views.Shared.ITodoListenService;
      locals : LocalsModel;
      static $inject = [App.Services.TodoPService.module.name, App.Views.Shared.TodoModalService.module.name, App.Views.Shared.TodoListenService.module.name];
      constructor(private todoSrv: App.Services.ITodoPService, private modalFactory: App.Views.Shared.ITodoModalService, listenService: App.Views.Shared.ITodoListenService) {
          this.listenService = listenService;
          this.init();
      }
 ...
}

In jedem AngularJs Modul (Controller, Service, Directive, …) gibt es eine Moduldefinition die das Modul selbst definiert und im “$inject” greift man nur noch auf den “module.name” zu um den Namen des jeweiligen Moduls abzurufen, d.h. der Name muss nur noch einmal “ausgeschrieben” werden.

private static _module: ng.IModule;
/**
 * Stellt das aktuelle Angular Modul für den "todoOverviewCtrl" 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('todoOverviewCtrl', [Services.TodoPService.module.name]);

    this._module.controller('todoOverviewCtrl', TodoOverviewCtrl);
    return this._module;
}

den kompletten Controller findet man in GitHub.

3. Service

Im Service gebe ich die passenden Datentypen aus .NET zurück, für die ich vorher mit TypeLite das passende TypeScript Interface erstellt habe. Auch im Service der einen Teil eines .NET Controllers abbildet stelle ich ein passendes Interface über die Methoden und Rückgabewerte des Services zur Verfügung. Der Service enthält ebenfalls wieder eine passende Modul Definition.

module App.Services {
   export interface ITodoPService {
        initTodoOverviewInitModel(): ng.IPromise;
        initTodoOverviewSearchModel(): ng.IPromise;
        todoOverviewResultModel(searchModel: MvcTypeScript.Models.Todo.ITodoOverviewSearchModel): ng.IPromise;
        initTodoCreateViewModel(): ng.IPromise;
        addOrUpdateTodoItem(createItem: MvcTypeScript.Models.Todo.ITodoCreateViewModel): ng.IPromise;
        deleteTodoEntry(todoItemId: number): void;
        loadTodoItem(todoItemId: number): ng.IPromise;
        initTodoListenViewModel(): ng.IPromise;
   }

    export class TodoPService implements ITodoPService {
        static $inject = ['$http'];
        constructor(private $http: ng.IHttpService) { }

        initTodoOverviewInitModel(): ng.IPromise {
            return this.$http.get('Todo/InitTodoOverviewInitModel').then((response: ng.IHttpPromiseCallbackArg): MvcTypeScript.Models.Todo.ITodoOverviewInitModel
                => { return response.data; });
        }

       ...

        initTodoListenViewModel(): ng.IPromise {
            return this.$http.get('Todo/InitTodoListenViewModel').then((response: ng.IHttpPromiseCallbackArg): MvcTypeScript.Models.Todo.ITodoListenViewModel
                => { return response.data; });
        }

        //#region Angular Module Definition
        private static _module: ng.IModule;
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }
            this._module = angular.module('todoPSrv', []);
            this._module.service('todoPSrv', TodoPService);
            return this._module;
        }
        //#endregion
    }
}

Das gesamte Beispiel kann man sich in GitHub anschauen. Dabei handelt es sich um einen ersten Entwurf. Über Kommentare was die Umsetzung angeht würde ich mir freuen, wenn schon jemand Erfahrungen damit sammeln konnte.

Die folgenden Links haben mir ebenfalls weitergeholfen um meine erste AngularJS Anwendung in TypeScript zu erstellen:

https://www.youtube.com/watch?v=32VyeinxbbI

http://kwilson.me.uk/blog/writing-cleaner-angularjs-with-typescript-and-controlleras/

http://dotnetbyexample.blogspot.de/2014/07/angularjs-typescript-setting-up-basic.html

http://sirarsalih.com/2014/01/28/when-two-forces-meet-angularjs-typescript/

http://www.dotnetcurry.com/showarticle.aspx?ID=1016

TypeScript 1.4+ mit Visual Studio 2013


Bisher hatte ich mir TypeScript noch nicht genauer angeschaut, aber mit der Ankündigung das Angular 2.0 auf TypeScript setzt, habe ich auch TypeScript ausprobiert, vor allem die Einbindung in Visual Studio 2013. Prinzipiell muss ich sagen ich bin begeistert, es gibt zwar hier und da noch ein paar Ecken und Kanten aber es macht durchaus Spaß. Daher im Folgenden ein paar Punkte die mir beim ersten Ausprobieren aufgefallen sind.

1. TypeScript (1.4) für Visual Studio 2013

Damit man TypeScript in Visual Studio verwenden kann, muss man zuerst die TypeScript-Erweiterung für Visual Studio installieren und die aktuellste Version für alle unterstützten Visual Studio Versionen findet man unter “download” auf der TypeScript Homepage:

http://www.typescriptlang.org/

Zusätzlich empfiehlt es sich noch die aktuellste Version von Web Essentials zu installieren und die aktuellste Version von Resharper ist auch sehr nützlich, TypeScript 1.4+ wird aber erst ab Resharper 9.x+ unterstützt. Mit der Verwendung von Resharper stehen lediglich bessere Refactoring Methoden zur Verfügung.

Ab diesem Zeitpunkt erhält man die volle Unterstützung für TypeScript in Visual Studio, d.h. wenn man sich die Eigenschaften eines WebProjekts anschaut, gibt es einen neuen Reiter “TypeScript Build”, in dem man entsprechende Einstellungen für TypeScript vornehmen kann.

image

Eine gute Einführung für TypeScript findet man direkt auf der TypeScript Webseite und das folgende Video von der build Konferenz ist ebenfalls sehr hilfreich.

Außerdem kann man für bereits bestehende JavaScript Bibliotheken Typendefinitionen für TypeScript bereitstellen, ohne das die jeweilige Bibliothek in in TypeScript implementiert sein muss. Für diese Typdefinitionen gibt es eine GitHubseite DefinitelyTyped auf der man zu vielen aktuellen Libraries die Typdefinitionen findet. Man kann aber auch in Visual Studio mit der rechten Maustaste auf eine JavaScript Bibliothek gehen und dort “Search for TypeScript Typings…” auswählen und es öffnet sich NuGet welches nach den passenden Typdefinition sucht.

image

2. Debuggen von TypeScript in Visual Studio

Wenn man eine Webanwendung mit TypeScript startet und den IE als Browser zum Debuggen verwendet, dann kann man einfach Haltepunkte im VS setzen, genau wie in C# und der Compiler hält einfach an der passenden Stelle im TypeScript Code an. Ich musste bei mir zumindest keine weiteren Einstellungen dafür vornehmen.

image

ACHTUNG: Hier scheint es hin und wieder noch Probleme mit dem Debugger zu geben, denn es kommt zu Fehlermeldungen obwohl eigentlich alles “ok” sein sollte. Hier wurde evtl. der TypeScript Code noch nicht richtig in JavaScript übersetzt bzw. VS arbeitet mit einem veralteten Codestand. Hier hilft es einfach mal das Debuggen zu beenden und die Webanwendung noch einmal komplett neu zu starten.

Außerdem kann es vorkommen das im VS 2013 Update4 nicht mehr reagiert, wenn man TypeScript debuggen möchte. Diesen Fehler konnte ich nur “beseitigen”, in dem ich den ASP.NET Debugger in den Projekteinstellungen deaktivierte. Das Problem ist wohl bekannt und soll mit dem nächsten Update behoben werden.

image

3. Wie wird aus meiner TypeScript Datei eine JavaScript Datei

Visual Studio erstellt automatisch z.B. nach dem Speichern die passende JavaScript und Mapping Datei. Wenn man die Web Essentials installiert hat, dann ist der Codeeditor zweigeteilt und man kann auf der linken Seite den TypeScript Code eingeben und rechts sieht man den resultierenden JavaScript Code.

Das die JavaScript Datei direkt beim Speichern erstellt werden soll, kann man in den “TypeScript Build” Einstellungen in den Projekteigenschaften einstellen.

Man kann ebenfalls einstellen, das alle TypeScript Dateien in eine einzige JavaScript Datei ausgeben werden unter “output” –> “Compine JavaScript output into file”. Beim automatischen zusammenführen aller TypeScript Dateien in eine JavaScript Datei, wird ebenfalls noch eine “_references.ts” Datei im Rootverzeichnis des Webprojekts benötigt in der alle TypeScript Dateien eingebunden werden die zusammengeführt werden sollen und hier ist vor allem die Reihenfolge sehr wichtig, damit später auch die Reihenfolge des Codes in der Ausgabedatei stimmt. Die Dateien können einfach per Drag und Drop in die “_references.ts” Datei gezogen werden.

/// <reference path="viewmodel/AddressViewModel.ts" />
/// <reference path="viewmodel/ProductViewModel.ts" />

ACHTUNG: Hier hatte ich diverse Probleme das die Dateien trotzdem nicht in der richtigen Reihenfolge eingebunden wurden. Außerdem gibt es auch noch einen “Bug” im VS. Daher habe ich mich vorerst für die Standardeinstellungen entschieden und hier wird für jede TypeScript Datei eine JavaScript Datei angelegt, welche aber im Projekt selbst nicht sichtbar sind. Ich füge die Dateien dann wie gewohnt in der “BundleConfig.cs” zusammen.

4. Kann man in TypeScript auch auf C# Klassen/Properties “zugreifen”?

Damit man z.B. auf die Properties (IntelliSense) von C# Klassen auch in TypeScript zugreifen kann, wenn man z.B. weiß welchen Typ das jeweilige JSON Result beinhaltet, dann habe ich hier bisher nur ein “Tool” gefunden, mit dem dies möglich ist.

Dabei handelt es sich um ein T4 Template “TypeLite” welches per NuGet eingebunden werden kann. Wenn man TypeLite installiert hat, wird im Scripts Verzeichnis die Datei “TypeLite.Net4.tt” angelegt. Ich musste in dieser Datei noch die Pfade für die “TypeLite.dll” und “TypeLite.Net4.dll” nachtragen.

image

<#@ assembly name="$(SolutionDir)packages\TypeLite.1.1.2.0\lib\net4\TypeLite.dll" #>
<#@ assembly name="$(SolutionDir)packages\TypeLite.1.1.2.0\lib\net4\TypeLite.Net4.dll" #>

Außerdem habe ich noch einen Formatter hinzugefügt der allen generierten Codeklassen noch ein “I” für Interface hinzufügt. (ACHTUNG TypeLite erstellt nur TypeScript Interfaces und keine TypeScript Klassen. Daher auch der zusätzliche Formatter für das “I”, damit zumindest die Namensgebung stimmt.)

<# var ts = TypeScript.Definitions()
		.WithReference("Enums.ts")
		.WithFormatter((type, f) => "I" + ((TypeLite.TsModels.TsClass)type).Name)
		.ForLoadedAssemblies();
#>

Damit jetzt auch die passenden TypeScript Interfaces generiert werden, muss man nur in seinen .NET Klassen das Attribut “TsClass” verwenden über der jeweiligen Klasse für die ein TypeScript Interface erstellt werden soll.

Dann das aktuelle Projekt erst kompilieren und dann mit der rechten Maustaste auf der “tt” Datei “Run Custom Tool” ausführen und jetzt erst werden die passenden TypeScript Interfaces generiert, dies muss auch nach jeder Klassenänderung wieder ausgeführt werden.

image

Aus der .NET Klasse:

[TsClass]
public class TodoCreateViewModel
{
    public TodoCreateViewModel()
    {
    }

    public int Id { get; set; }

    public bool IsActive { get; set; }

    public string Description { get; set; }

    public DateTime DoDate { get; set; }

    public string Creator { get; set; }

    public Prioritaet Prioritaet { get; set; }
}

Ergibt das TypeScript Interface, welches in der “TypeLite.Net4.d.ts” Datei abgelegt wird.

interface ITodoCreateViewModel {
	Id: number;
	IsActive: boolean;
	Description: string;
	DoDate: Date;
	Creator: string;
	Prioritaet: MvcTypeScript.Helper.Prioritaet;
}

und auf dieses Interface kann man jetzt Problemlos in seinen eigenen TypeScript Dateien zugreifen und erhält quasi IntelliSense für seine .NET Klassen.

ACHTUNG: TypeLite erstellt für eine gekennzeichnete Klasse für jeden enthaltenen Typ oder Basisklasse jeweils ein Interface, d.h. man muss nicht über jede seiner Klassen das Attribut setzen,, man muss aber auch aufpassen das man z.B. über einem MVC Controller selbst das Attribut besser nicht verwendet, denn hier werden dann eine Menge Interfaces erstellt.