Material Design und Angular.JS

Seit Ende 2014 ist nun auch Google auf den Flat Design Zug aufgesprungen. Die Design Sprache verfolgt für Android die gleichen Ziele wie damals METRO für Windows 8. METRO war viel progressiver und verfolgte einen adaptiven Layout Ansatz. Material Design (wie auch nun Microsoft) folgen dem Responsive Design Paradigma, das ein universelles UI nutzt,

Sozusagen aus einem Irrtum heraus, habe ich mir zuerst das optisch recht gut gelungene LumX angesehen. Nachdem ich mich ohne nennenswerte Doku durch den Prototypen geplagt habe, war die Begeisterung auch schon wieder verflogen.

Im zweiten Anlauf teste ich das Google Framework Angular Material. Alles noch recht frisch und wenige Blog Beispiele im Web.

Visual Studios Pakte Manager Nuget hat auch ein passendes Angebot in der Version 0.8.3.

image

Auf der Hersteller Website wird allerdings schon 0.9.3 angeboten. Da diese Art von Frameworks (immerhin erfinden die das UI Rad alle neu) vor Bugs in der Regel nur so strotzen, besser das Neueste. Dazu nehme ich (über meinen Schatten springend) Bower als Paketmanager. Den kann man sich per Nuget installieren und dann in der Kommandozeile nutzen.

image

Konkret muss das Paket per bower install angular-material aus der Visual Studio Kommandozeile installiert werden.

image

Im Web Projekt werden dann eine Reihe von Verzeichnissen in Bower_Components erzeugt.

image

Um es vorweg zu nehmen. Im Laufe meiner etwas verzweifelten Recherchen zum Thema Icons bin ich erst nach Stunden darauf gekommen, das die Icons extra installiert werden müssen.

bower install material-design-icons

Das sind dann auch 86.000 Dateien mit rund 90 MB Download für Bower. Visual Studio stürzt bei mir ab wenn ich den Folder dem Projekt hinzufügen möchte. Eigentlich bietet Material.js eine Reihe von Standard Icons. Allerdings werden die nicht angezeigt. Es stimmen wohl die Pfade nicht.  Über einen IconProvider lässt sich jedes verwendete Icon einzeln konfigurieren ala

   1:   app.config(function ($mdIconProvider) {
   2:              $mdIconProvider
   3:              .icon('search', 'bower_components/material-design-icons/action/svg/production/ic_search_24px.svg');
   4:          });

Das es bei mir trotzdem nicht geklappt hat habe ich diesen Weg nicht weiter verfolgt.

Material hat ein relativ flexibles Gridlayout. CSS Klassen kommen relativ sparsam zum Einsatz. Im Wesentlichen stehen für die üblichen Anwendungsfälle eigene UI Controls, ausgeführt als Angular Direktiven zur Verfügung. Diese beginnen mit dem Präfix md.

Mein Beispiel benötigt einen Popup Dialog für das Erfassen eines neuen Tabellen Eintrages. Ein modaler Dialog erfolgt mit md-dialog. Der Button ist ein md-Button. Label und Textbox wird per md-input Container zusammen gefasst.

   1:  <md-dialog aria-label="Neuer Name">
   2:      <md-dialog-content >
   3:          <form name="Form1">
   4:                  <md-input-container flex>
   5:                      <label>Vorname</label>
   6:                      <input ng-model="kind.name" >
   7:                  </md-input-container>
   8:             </form>
   9:      </md-dialog-content>
  10:      <div class="md-actions" layout="row">
  11:          <md-button ng-click="close()"> Abbruch </md-button>
  12:          <md-button ng-click="close(kind)" class="md-primary"> Save </md-button>
  13:      </div>
  14:  </md-dialog>

Der modale Popup Dialog enthält einen Schatten und graut den Hintergrund aus. Das besondere sind die Eingabefelder, die mit dem Text aus dem Label vorbelegt werden.

image

Setzt der Benutzer den Fokus in das HTML Input Feld wird die Beschriftung animiert nach oben geschoben.

image

Sieht wirklich sehr schick aus.

Das Konzept findet sich konsistent auch in der Toolbar wieder. In diese wird eine Beschriftung (vornamen für Kind) und ein Search Textbox eingefügt. Das Icon sollte eigentlich per “search”, wie im auskommentierter Zeile 9 definiert werden. Die funktionierende Alternative erfordert den vollen Pfad, den Bower so erzeugt hat.

   1:  <body ng-app="App" layout="column" ng-controller="myController" >
   2:      <md-toolbar>
   3:          <div class="md-toolbar-tools">
   4:              <h1 class="md-headline">Vornamen fürs Kind</h1>
   5:              <span flex></span>
   6:              <md-input-container>
   7:                  <md-icon md-svg-src="bower_components/material-design-icons/action/svg/
production/ic_search_48px.svg"
style="display:inline-block;"></md-icon>
   8:                  <input ng-model="search" class="" placeholder="enter search">
   9:                  <!--<ng-md-icon icon="search"></ng-md-icon>-->
  10:              </md-input-container>
  11:          </div>
  12:      </md-toolbar>

 

image

Als nächstes wird der Button (+) erzeugt, um weitere Vornamen hinzufügen zu können. Die graue Umrandung der Namensliste wird mit dem Layout Control Card erzeugt. Ich bin mir nicht ganz sicher ob das der passende Einsatzzweck ist. 

Die Liste erhält interaktive Klickbare Einträge durch hinzufügen einer md Checkbox. Die ganze Zeile ist clickbar und löst eine Animation aus.

animated

 

   1:      <div layout="row" flex>
   2:          <md-content layout="column" flex class="md-padding">
   3:              <md-button class="md-fab md-primary md-button md-fab-bottom-right"
   4:                         aria-label="Neu" ng-click="add($event)">
   5:                  <md-icon md-svg-src="bower_components/material-design-icons/content/
svg/production/ic_add_48px.svg"
></md-icon>
   6:              </md-button>
   7:              <md-card flex-gt-sm="90" flex-gt-md="80">
   8:                  <md-card-content>
   9:                      <h2>Namen </h2>
  10:                      <md-list>
  11:                          <md-list-item ng-repeat="name in namen | filter:search" layout="row">
  12:                              <md-checkbox></md-checkbox>
  13:                              <p>{{ name }}</p>
  14:                              <md-divider></md-divider>
  15:                          </md-list-item>
  16:                      </md-list>
  17:                  </md-card-content>
  18:              </md-card>
  19:          </md-content>
  20:      </div>

Im JavaScript Logik Teil muss ngMaterial der DI eingebunden werden. Rein zu Demo Zwecken, wird ein ein Toast Benachrichtigung eingebaut. Das Objekt mdToast muss ebenfalls injiziert werden. Nicht ganz uninteressant, der Zugriff aufs UI aus einem Controller ohne MVX Bindung!

   1:  var app = angular.module('App', ['ngMaterial', 'ngAnimate', 'ngAria']);
   2:      
   3:   app.controller('myController', ['$scope', '$mdToast', '$mdDialog', function ($scope, $mdToast, $mdDialog) {
   4:              $scope.namen = ["hannes", "franz", "susi"];
   5:              $scope.save = function () {
   6:                  $mdToast.show({
   7:                      position: "bottom right",
   8:                      template: "<md-toast>gespeichert!</md-toast>"
   9:                  });
  10:              };

Fast spannender ist die kommunikation über zwei Controller zwischen modalen Dialog und Hauptview. In der Add Methode (Zeile 11) wird Show aufgerufen. Dabei wird die Controller Funktion (addController) und das HTML Template als Parameter übergeben. Über ein Promise wird asynchron der .then Zweig (Zeile 17) aufgerufen, wenn der Popup Dialog geschlossen wurde.  Da im Context und lokalen Scopes des Dialogs die Methode hide (Zeile 7) mit dem Scope des Formulares aufgerufen wird, findet so die erfassten Daten den Weg in den übergeordneten Scope wieder. Dann kann der neue Vornamen an die Liste der Namen angefügt werden.

   1:   function addController($scope, $mdDialog) {
   2:                  $scope.close = function (answer) {
   3:                      if (answer === null) {
   4:                          $mdDialog.cancel()
   5:                      }
   6:                      else {
   7:                          $mdDialog.hide(answer);
   8:                      };
   9:                  };
  10:              };
  11:              $scope.add = function (ev) {
  12:                  $mdDialog.show({
  13:                      controller: addController,
  14:                      templateUrl: 'addtemplate.html',
  15:                      targetEvent: ev,
  16:                  })
  17:                  .then(function (kind) {
  18:                      //speichern;
  19:                      $scope.namen.push(kind.name);
  20:   
  21:                  }, function () {
  22:                      // fehler;
  23:                  });

Auch Angular Material steht am Anfang. Rein subjektiv hat es sich an der ein oder anderen Stelle manchmal sehr langsam angefühlt. Der Lösungsansatz per Direktiven Controls abzubilden ist relativ nahe an zb Web Forms Steuerelementen, nur eben rein am Client. Wobei schon der nächste Trend in Sicht ist, diese wieder am Server rendern zu lassen. Auffallend ist, das Microsoft alles tut, um diese Art von Web Entwicklung auch aus Visual Studio zu ermöglichen.

Webforms Client Validation

Ein Schulungsteilnehmer eines Web Forms Kurses vor zig Jahren, schreibt mir heute eine Mail mit folgender Einleitung

lange nicht mehr gesehen, wie geht’s denn so?

Ich habe ein kleines Problemchen,

Dabei war ein Stück ASP.NET Code, das so geschätzt vor 10 Jahren entstanden ist. Es muss heute um eine bestimmte Client Funktionalität ergänzt werden. Da der VB.NET Code sagen wir mal nicht mehr ganz State of the Art ist, habe ich das Problem prototypisch nachgebaut und auch gelöst.

Ein EingabeFeld wird mit einem Validator Control auf vorhandene Eingabewerte geprüft. Um einen Postback im Fehlerfalle zu verhindern, soll die Client Seitige JavaScript Validierung benutzt werden.

   1:  <form id="form1" runat="server">
   2:          <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
   3:          <div>
   4:              <asp:TextBox runat="server" ID="text1"></asp:TextBox>
   5:              <asp:RequiredFieldValidator ID="RequiredFieldValidator1" ControlToValidate="text1" runat="server"
   6:                  EnableClientScript="True"
   7:                  ErrorMessage="RequiredFieldValidator"></asp:RequiredFieldValidator>
   8:   
   9:              <asp:Button runat="server" Text="Button" OnClick="Unnamed1_Click"

Leider Pustekuchen. Die Validierung wird so weiter am Server ausgeführt.

Der Grund liegt in einer Änderung der JavaScript Library der ASP.NET Validator Controls mit der Version 4.5. Es wird nun auf JQuery gesetzt, statt auf die Microsoft Webforms JavaScript Bibliothek. Eigentlich ne gute Sache, wenn Jquery auch eingebunden ist. Falls das nicht geschehen soll kann man den alten Mode per Web.Config erzwingen.

   1:  <appSettings>
   2:      <add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />
   3:    </appSettings>

image

Will man nun beim Click des Buttons zusätzliches JavaScript ausführen, stoßt man auf einen Konflikt. Die Client seitige validierung funktioniert nicht mehr und es wird wieder ein Postback zum Server ausgeführt.

Ein Blick in den gerenderten HTML Code offenbart das Problem. Ohne wird das OnClick Event von ASP.NET Page Life Cycle genutzt,

   1:      <input type="submit" name="ctl02" value="Button" 
   2:  onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(
   3:  &quot;ctl02&quot;, &quot;&quot;, true, &quot;&quot;, &quot;&quot;, false, false))" />
   4:   

In der ASPX Seite steht nun aber im Button zusätzliches JavaScript. In dem Fall um einen Meldung ala Wirklich Löschen? einzublenden.

Abhilfe schafft der manuelle Aufruf der client-seitigen Validierung per ClientValidate.

   1:   function YesNo() {
   2:   
   3:              if (Page_ClientValidate()) {
   4:                  if (confirm("YesNo?")) { return true; };
   5:             }
   6:              return false;
   7:    };

mehrere Blend Effekte kombinieren

Viele Jahre war ich der Meinung, das sich Effekte (Effects) innerhalb eines XAML UI Elements nicht kombinieren lassen. Ein blöder Fehler in einer Blend Schulung belehrte mich eines Besseren.

Effekte, genauer gesagt Pixel Shader Effects, erlauben die Manipulation eines beliebigen Objektes auf der Grafikkarte. Diese werden in einer eigenen Programmiersprache HLSL geschrieben. In den Anfängen lieferte Microsoft nur zwei Effekte mit, Blur und Drop Shadow. In der Zwischenzeit sind es für WPF 15. Ein Effekt wird per Drag&Drop auf das UI Element gezogen und taucht dann im XAML als Unterelement auf.

   1:  <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Stroke="Black" VerticalAlignment="Top" Width="100" Margin="250,90,0,0">
   2:              <Rectangle.Effect>
   3:                  <DropShadowEffect/>
   4:              </Rectangle.Effect>
   5:  </Rectangle>

Im Blend Editor ist dieser Effekt untergeordnet

image

Auch die Eigenschaften wie Schattenfarbe (color) oder die Richtung (Direction) sind per Property Dialog änderbar.

image

Das Ergebnis im Design View.

image

Problem ist, wenn man versucht einen weiteren und damit zweiten Effekt auf das Rechteck zu ziehen. Damit wird der Erste Effekt entfernt.

Durch Zufall habe ich einen Effect auf das Layoutroot Grid Container Control gezogen. Alle darunter liegenden Child Controls erbten diesen Effekt.

image

Damit lassen sich somit auch Effekte kombinieren. Hier der DropShadow Effect und der ColorToneEffect auf dem Rectangle.

image

Allerdings endet dieser XAML Trick jäh, wenn man dem Parent Container (hier Layoutroot) einen Background Brush verpasst. Dann wird der Effekt nur mehr auf das Parent Grid angewandt. Abhilfe schafft ein zusätzlicher Layout Container in der Hierarchie.

Übrigens Expression Blend ist seit der Version 5 gratis zum download und heißt nun Blend für Visual Studio.

Internet Explorer Logo Historie

Allen Unkenrufen zum Trotz hat der Internet Explorer noch immer einen Marktanteil von über 50% – weltweit. Da konnte EU und das ganze Web Hipster Gezeter fundamental kaum etwas ändern. Von seinem Spitzen Marktanteil von 95% von 2003 ist der Redmonder Browser allerdings weit entfernt. Microsofts Strategie scheint ja nun zu sein, seine Gegner durch intensive Umarmung zu erwürgen. Bauernopfer ist der hauseigene Browser. Der muss weg und mit ihn der Name und das Logo. Der neue Name lautet Edge und über das Logo wird heftig gestritten. Ein kurzer Exkurs über die Entwicklung des Logo und Icons im historischen IT Kontext.

Internet Explorer 1.0 and 2.0 logo1995 fiel der Startschuss für den so genannten Browser Krieg. Das erste Mal wurde eindrucksvoll demonstriert, das man mit gratis nachhaltig ein wirtschaftlich ertragreich funktionierendes Marktsegment auslöschen kann. Es war die Epoche von Word Art.

Microsoft Internet Explorer 2 logoMit Windows 95 wurde die Welt bunter und grafischer. Mit ihm auch der Internet Explorer 2, der nun nicht mehr extra per Plus Paket installiert werden muss. Die Epoche würde ich Bold Text nennen. Dabei ähnelt das moderne Microsoft Logo dem damaligen frappierend.

File:Internet Explorer 3 logo and wordmark.png

Es war 96, als die 3er Version den Markt erreicht. Das stilisierte Welt e mit Mondumlaufbahn wurde eingeführt. Zugleich war dies mit CSS Support sozusagen der erste grafische Browser.

Schon 97 kommt Version 4 optisch an NT 4 angepasst. Wer sich an die Back Office Produkt Familie erinnert, wird den Stil wieder erkennen. Grafisch stand damals die Welt still. Namentlich waren wir in der .0 Zeit.

Nahezu unverändert erscheint 2 Jahre später 99, die Version 5 des Explorers. Internet Explorer 5 logo
Schriftarten und Stil weiterhin Arial Bold. Die Zeit aus der erkennbar auch das ppedv Logo stammt. Der fette Schriftstil ist mutmaßlich dem damalig üblichen Druckermodellen geschuldet. Erst 2002 wurde der Farblaser erschwinglich.

 

File:Internet Explorer logo 6.png2001 begann die erfolgreiche Epoche von Windows XP und der 6er. Farbverläufe mit seichten 3D Effekt verbunden mit leichteren Schrifttypen prägen den Zeitgeist. Das e hat eine Franse um den Schatten des Orbits darzustellen.

 

Internet Explorer 7 Logo.pngEs folgt 2006 die Zeit von Vista. Ein neuer Berufsstand wird geschaffen- der Icon Designer.  Auflösungen bis 64x64 Pixel verbunden mit unendlichen Farbpaletten erlauben es auf dem Kalender Icon den Geburtstag des Designers zu markieren. Das 7er Explorer e mit 3D Glass Effect und goldener Mond Orbit mit Erdschatten.

 

Nun kann man sagen, bewährtes soll man nicht verändern und so ist erst 2009 die nächste Version 8 am Start. Es ist die Zeit von Windows 7. Gesamt eher mit kleineren Design Evolutionen.

 

Bild in Originalgröße anzeigenLangsam wird die Designer Welt wieder flacher. Sozusagen in den Schockwellen der geplatzten DotCom Blase 2011 reduziert sich auch Grafik wieder auf das Wesentliche.

Das Earth e des Internet Explorer 9 nutzt eine Konturlinie. Der Blauton wird leichter und tendiert zu seinen ursprünglichen Note. Der Orbit ist nun einfarbig.

Internet Explorer 10 start screen tile.svgPlötzlich wird die Welt flach. Flat Design ist in aller Munde. Getrieben von Smartphones die unter allen Lichtverhältnissen lesbare Darstellung bieten müssen. Kontrast ist die Antwort. Schwarz/Weiss oder auch Blau/Weiss. Für 3D oder Farbspielereien bleibt kein Raum. Das IE 10 Logo selbst ist stark an den Ursprüngen orientiert. 2012 wurde für Windows 8 unter dem Namen METRO diese Design Sprache eingeführt. In der Taskleiste  wird als Icon nach wie vor die 9er Version verwendet.

imageAuch die 11er Version von 2013 hält sich mit seiner Design Sprache an den Vorgänger. Ebenso wie den Vorgänger gibt es diesen als nun “modern” App bezeichneten oder normale Desktop Version.

imageDie Taskbar Icons der identen Versionen unterscheiden sich aber erheblich.

Die App Variante ist App typisch komplett im Flat Design gehalten und mit einem dunkleren Blauton kontrastreicher

 

 

Da die gesamte Welt, außer ein kleines gallisches Dorf, dem Redmonder Browser den Looser Stempel aufdrückt, haben sich die Redmonder Produkt Manager zur Flucht nach vorne entschlossen. Der IE wird eingestampft und mit ihm sein Logo. Projekt Spartan wird final Microsoft Edge. Wir sind nun in der 0. Zeit angelangt.

Bild in Originalgröße anzeigen

Durchaus praktisch bleibt so das e als stilgebendes Element erhalten. Auf den ersten unbelasteten Blick erkenne ich einen grinsenden Rotzlöffel (=frecher Junge). Andere sehen Hammer und Sichel. Aus Designer Sicht ist die Linie in den Logos deutlich zu erkennen und wunderbar gelöst. Die Erde ohne den Orbit aber mit Schatten als Flat. Allerdings nur erkennbar, wenn man die Historie der Internet Explorers kennt. Und hier ist die große Schwachstelle des Logos. Zu dem Zeitpunkt an dem man auch nur einem Menschen erklären muss, was es darstellen soll, hat das Icon seinen Berechtigung verloren.

OPTIONS statt PUT oder POST

HTML Apps sind im Grunde relativ unsicher. Der Benutzer kann beliebigen Code laden und sieht nicht was im Hintergrund geschieht. Gerade die Callbacks werden so zum Risiko, mit dem jedes Framework anders umgeht. Angular nutzt einen sogenannten Preflight bei Cross Domain aufrufen.

Wenn ein Ajax Callback zu einer anderen Domain zeigt, als der HTML Source Code geladen wurde, spricht man vom einem Cross Domain Request, kurz CORS (Cross Origin Request). Auf das Thema bin ich gestoßen, als ich in einem AngularJS Training einen externen northwind Odata REST Service aufrufen wollte. Nachdem der REST Service einen Fehler liefert, zeigt der Netzwerk Trace statt dem erwarteten POST oder PUT HTTP Kommando im Fiddler einen OPTION Request.

Auf Recherche findet sich, das dieser Request ein W3C Standard (Empfehlung) ist. Der Web Service hat damit die Chance, einen Request abzulehnen, wenn ein Set von Bedingungen nicht erfüllt ist. Man nennt diesen OPTIONS Request preflight.

options1

Da der Odata Developer Service kein CORS unterstützt, wird ein eigener REST Service auf Azure deployed. Aber auch ASP.NET Web Api muss erst für CORS vorbereitet werden. Das notwendige Nuget Paket ist Microsoft.AspNet.WebApi.Cors

options2

In der Startup Logik (hier WebapiConfig.vb) wird Cors aktiviert

   1:  Public Sub Register(ByVal config As HttpConfiguration)
   2:          ' Web API configuration and services
   3:          config.EnableCors()

 

Auf jedem Controller oder Controllermethode können per Attribut Cors Regeln definiert werden.

  • Origin-DNS Herkunft des Callbacks
  • Headers-gültige Header
  • Methods- HTTP Verben durch Komma getrennt

Im Code Beispiel wird durch einen Stern einfach alles erlaubt und damit jede Security ausgehebelt.

   1:   <EnableCors("*", "*", "*")>
   2:      Public Class CustomersController
   3:          Inherits ApiController

Der Aufruf des REST Services durch einen AngularJS $ Resource Call, ruft erst per HTTP GET die Daten ab. Im nächsten Schritt drückt der Benutzer im Dialog auf speichern und sollte damit den HTTP PUT bewirken. Durch den Preflight wird aber zunächst der OPTIONS Aufruf ausgeführt, den unsere Web API nun korrekt beantwortet, so das im dritten Schritt der finale PUT Aufruf erfolgen kann.

options3

Für den Client ist die Information aus dem HTTP Header des OPTIONS Request essentiell. Damit teilt der Service mit, das er für den folgenden PUT auch bereit ist.

options4

Der REST Service ist nun für Test Zugriffe bereit. Wer für Tests diesen nutzen will, das Schema

api / Datenbankname / Customers. http://northwindrest.azurewebsites.net/api/ppedv/customers

Der Datenbankname muss frei gewählt werden und erzeugt eine persönliche Kopie der Northwind Daten, die auch geändert und ergänzt werden kann. Delete ist aktuell nicht implementiert. Auch die anderen Tabellen wie Orders sind (noch) nicht als Controller auscodiert.

Wer  noch mehr zum Thema wissen will, den empfehle ich den MSDN Blog Artikel. Dazu bieten wir einen 2 tägigen ASP.NET Web Api Kurs an.

CRUD mit ASP.NET Web API und AngularJS

In Zuge einer Vorbereitung eines Vortrages, wollte ich den nötigen JavaScript Code auf ein Minimum reduzieren. Dabei habe ich mich beinahe in ngResource verliebt. ASP.NET Web API und Angular sind mit $resource ein fast perfektes Paar.

ngResource kapselt das $http Objekt und stellt die REST typischen Methoden nativ zur Verfügung.

Laut Doku

{ 'get':    {method:'GET'},
  'save':   {method:'POST'},
  'query':  {method:'GET', isArray:true},
  'remove': {method:'DELETE'},
  'delete': {method:'DELETE'} };

Es fehlt in der Auflistung das Gegenstück zum SQL Update, typischerweise mit HTTP PUT oder eher selten mit PATCH ausgeführt. Glücklicherweise lässt sich im $resource Objekt nicht nur der Web API HTTP Endpunkt, sondern auch eine zusätzliche Update Methode deklarieren.

   1:  App.factory('myFactory', function ($resource) {
   2:              return $resource('/api/customers/:id', {}, {
   3:                  'update': { method: 'PUT' }
   4:                              });
   5:   
   6:          });

Es ist zwar nicht nötig $resource in einen Angular Service (factory) zu kapseln, erleichtert aber die spätere Nutzung. Um einen einfache Liste aller Customer zu bekommen reicht es den Aufruf myFactory.query() zuzuweisen. Folgender prototypisch aber funktioneller Code Beispiel ,liest einen Datensatz per REST Call, aktualisiert diesen per ID und Objekt und fügt einen neuen Satz ein.

   1:   App.controller('myController', function ($scope, myFactory) {
   2:              $scope.vm = myFactory.get({ id: 'ALFKI' });
   3:   
   4:              $scope.save = function (vm) {
   5:                  myFactory.update({ id: 'ALFKI' }, vm);
   6:              };
   7:              $scope.insert = function (vm) {
   8:                  myFactory.save(vm);
   9:              };
  10:   
  11:   
  12:          });

Der ASP.NET Web API Controller besteht dabei aus den den Standard Methoden, generiert aus einem EF Model.

Ich führe für ppedv AngularJS Trainings durch.

Single Page Applications und Azure Video Walkthrough

Mit fünfzehn kurzen Videos erhält man gesamt fünfsiebzig Minuten eine Einführung in die Welt der Web Apps auf Azure mit Visual Studio 2013.

Teil 1 2:39
Web Projekt in Visual Studio anlegen
Teil 2 2:53
Nuget Paket Manager
Teil 3 2:58
SQL Data Tools in Visual Studio 2013
Teil 4 2:40
ORM Model mit Entity Framework 6.1 erstellen
Teil 5 3:11
REST Service mit ASP.NET Web API
Teil 6 4:32
Bootstrap responsive Design
Teil 7 2:59
Bootstrap Formular Designen
Teil 8 2:26
Einführung in AngularJS
Teil 9 7:10
Angular JS Module, Service und $Ressource HTTP Client
Teil 10 8:48
AngularJS Controller und Listen
Teil 11 7:50
AngularJS Controller und Listen (second try)
Teil 12 8:32
Angular JS Formular speichern und Datum per Filter formatieren
Teil 13 5:19
publish einer Website zu Azure Web App und SQL Azure
Teil 14 6:02
Azure publish Settings aus Visual Studio managen
Teil 15 6:23
Signalr Hub Methode aus ASP.NET Web Api aufrufen
Teil 16 5:22
SignalR AngularJS Client registrieren und Daten empfangen

Die Videos sind stark komprimiert. Da Vimeo nur 10 Videos pro Tag erlaubt, folgt der Rest später.

Azure Blob Storage lokal nutzen und deploy

Wer Azure Websites Apps entwickelt, stolpert schnell mal über den Anwendungsfall Datei Upload. Dabei stellt sich die Frage, wohin mit den Daten? Ein Bild Upload ins Dateisystem ist zwar möglich, aber nicht im Sinne moderner Architektur. Dafür sieht Microsoft Windows Azure schon von Anfang an den Blob Storage vor.

Wer für die Cloud entwickelt sollte das Windows Azure SDK für .net mit dem Web Plattform Installer installieren. Damit erhält man mehr Funktion für Visual Studio 2013 und einige Tools. Unter anderem den Windows Azure Storage Emulator. Damit lassen BLOB Services auch lokal entwickeln. Ein UI sucht man dafür vergeblich. Lediglich auf der Kommandozeile lässt sich der Storage Emulator starten und administrieren.

WAStorageEmulator.exe init

azure11

Nach dem  das Azure SKD (hier 2.5.1) installiert wurde enthält Windows Azure Storage Explorer für Visual Studio 2013 eine Reihe neuer aufklappbarer Einträge.

azure9

Der Bereich Development beinhaltet den lokalen Azure Storage. Für die Nutzung wird in der Datei web.config ein Connection String angelegt der folgenden Eintrag enthält.

   1:    <connectionStrings>
   2:      <add name="fotosblob" connectionString="UseDevelopmentStorage=true" />

Intern verwendet der Service eine SQL Datenbank. Unser .NET Code soll es dem Benutzer ermöglichen Bilder per Web Browser hochzuladen. Benötigt wird noch die Azure Client Bibliothek WindowsAzure.Storage,, die dem Visual Studio Web Projekt per Nuget hinzugefügt wird.

 azure7

Das HTML5 Frontend enthält ein Input Type File Element. Per Post wird der Upload von einer oder mehreren Dateien durchgeführt.

Die bereits im Azure Blob gespeicherten Objekte wird per Liste angezeigt. Nicht ganz elegant aber funktionell direkt mit dem CloudBlockBlob Typ.

   1:   <form id="form1" runat="server" method="post" enctype="multipart/form-data">
   2:          <div>
   3:              <input type="file" id="File1" name="File1" runat="server" />
   4:              <br>
   5:              <input type="submit" id="Submit1" value="Upload" runat="server" />
   6:              <hr />
   7:   
   8:    <asp:ListView ID="ListView1" runat="server"
   9:          ItemType="Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob"
  10:          SelectMethod="ListView1_GetData">
  11:          <ItemTemplate>
  12:                      <%#Item.Uri%>
  13:                      <br />
  14:          </ItemTemplate>
  15:    </asp:ListView>
  16:  </div>

Wenn nach einem Request im Input Element Dateien vorhanden sind, werden diese im Blob abgelegt. Dazu benötigt man noch ein wenig konzeptionelles Background Wissen. In Zeile 2 wird ein Storage Objekt erstellt. Aus diesem eine Art Proxy Client um mit dem Dienst zu sprechen.

Ein Azure Storage kann 0-n Container enhalten. In Zeile 4 wird der Foto Container identifiziert und bei Bedarf auch erzeugt. Um die hochgeladenden Bilder für jedermann per URL abrufbar zu halten, dient Zeile 6. Ab dann wird das BLOB Objekt per Referenz auf das Upload Objekt erzeugt und gefüllt.

   1:  If IsNothing(Request.Files("File1")) = False Then
   2:     Dim storageAccount = CloudStorageAccount.Parse(
ConfigurationManager.ConnectionStrings("fotosblob").ConnectionString)
   3:     Dim blobClient = storageAccount.CreateCloudBlobClient()
   4:     Dim container = blobClient.GetContainerReference("meinefotos")
   5:     container.CreateIfNotExists()
   6:     container.SetPermissions(New BlobContainerPermissions With
{.PublicAccess = BlobContainerPublicAccessType.Blob})
   7:     Dim blockBlob = container.GetBlockBlobReference(
Path.GetFileName(File1.PostedFile.FileName()))
   8:     blockBlob.UploadFromStream(File1.PostedFile.InputStream)

Theoretisch sind wir nun fertig. Die Images lassen sich im Visual Studio Server Explorer abrufen aus der Liste der Blob Elemente.

image

In der Praxis wird man eher eine Anwendung diesen Job machen lassen.  Der Code ist bis Zeile 5 ident mit dem vorigen VB.NET Azure Code Beispiel. Refactoring! Für Demo Zwecke ist ein funktioneller Code Block durchaus sinnvoll. In Zeile 6 wird eine Referenz auf den Blob Container gezogen. Danach recht verkürzt per LINQ die erhaltenen Objekte direkt in eine Liste von BlockBlob Objekten gecastet.

   1:   Public Function ListView1_GetData() As List(Of CloudBlockBlob)
   2:          Dim storageAccount = CloudStorageAccount.Parse(
ConfigurationManager.ConnectionStrings("fotosblob").ConnectionString)
   3:          Dim blobClient = storageAccount.CreateCloudBlobClient()
   4:          Dim container = blobClient.GetContainerReference("meinefotos")
   5:          container.CreateIfNotExists()
   6:          Dim blockBlob = container.GetBlockBlobReference("meinefotos")
   7:          Dim q = container.ListBlobs().OfType(Of CloudBlockBlob)()
   8:          Return q.ToList
   9:  End Function

Dieses Beispiel ist mit Vorsicht zu genießen, da es verschiedene Types geben kann (CloudBlockBlob, CloudPageBlob, CloudBlobDirectory) Wer mehr dazu Wissen will findet in diesem Microsoft Blog C# Sample Code.

Im letztem Schritt wird die Frage behandelt wie aus der Web APP eine Azure Web App wird. Bevor es zum Publish bzw Deploy der Code Daten kommen kann, wird ein Azure Storage Account angelegt.

azure8

Wei zuvor schon direkt aus dem Visual Studio Server Explorer mit der Context Menü Option Create Storage Account. In den Eigenschaften (Propertys) findet sich dann der Connection String ohne den Key. Per Klick auf dei drei Punkte ganz rechts des Connection Strings öffnet sich ein weiterer Dialog.

azure10

Aus diesem kopiert man den Connection String und ergänzt die Datei Web.release.config. Sollte die nicht vorhanden sein, auf die Datei mit der Endung pubxml (im Verzeichnis PublishProfiles) rechts klick und  Add Config Transform auswählen.

   1:   <connectionStrings>
   2:        <add name="fotosblob" 
   3:          connectionString="DefaultEndpointsProtocol=https;
AccountName=ppedvdemo;AccountKey=dP6Cu"
   4:          xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
   5:      </connectionStrings>

Beim Publish der Web App wird dann von msbuild der Connection String automatisch angepasst.

Ergänzend sei der Azure Deploy Blog Artikel empfohlen.

Natürlich bieten wir das Thema Azure Development auch als Training an.

ASP.NET Datenbank Website deploy nach Azure

Microsoft macht es einem ziemlich leicht Azure zu nutzen. Kompliziert ist höchstens aus den unzähligen Optionen zu wählen. Mit Visual Studio 2013 lässt sich eine SQL Server basierte Website mit allem Pipapo nach Azure publishen ohne das Azure Portal zu benötigen.

Eine übliche Website im ASP.NET Umfeld besteht aus einem HTML Part, Code per ASPX oder MVC Controller, einem EF Datenmodell und einer SQL Datenbank. Dabei spielt es keine Rolle ob das Datenmodell per Code First oder direkt in der Datenbank erstellt worden ist. Die bei Code Frist nötigen und durchaus nützlichen EF Migrations erzeugen einen mehr Aufwand, der in mini Projekten nicht zu rechtfertigen ist.

Mein ASP.NET Webforms Demo Projekt beginnt deshalb lokal und mit einer Datenbank. Im Verzeichnis APP_DATA wird eine SQL Server Database (database1.mdf) angelegt. Per Doppelklick wird diese dynamisch im SQL Express Attached und kann im SQL Server Object Explorer direkt in Visual Studio bearbeitet werden. Es wird eine Tabelle mit dem Namen table und Felder angelegt. Außerdem können dort direkt Daten erfasst werden. 

Anschließend wird aus den Daten ein Entity Framework Model erzeugt. Darin finden sich auch die Klassen für die Daten Objekte.

Meine Web Forms VB.NET Datenbank Anwendung ist supersimpel.

   1:  <h1>Hello World</h1>
   2:  <asp:ListView ID="ListView1" runat="server" 
ItemType="TestDBAzure.Table" SelectMethod="ListView1_GetData">
   3:              <ItemTemplate>
   4:                  <%#Item.Name%>
   5:              </ItemTemplate>
   6:   </asp:ListView>

Die Daten werden in einer Art Viewmodel aus dem EF Model geladen.

   1:   Public Function ListView1_GetData() As IQueryable(Of TestDBAzure.Table)
   2:          Dim ef As New Model1
   3:          Return ef.Table
   4:      End Function

Nun kommt der spannende Teil. Wie kommt meine Web Anwendung auf den Webserver? Wer diese selbst hostet auf z.B. einem IIS benötigt folgende Schritte

  • Web im IIS anlegen
  • DNS Namen konfigurieren
  • Verzeichnis auf Server per FTP kopieren
  • FTP Zugang konfigurieren
  • SQL Datenbank anlegen
  • SQL User anlegen
  • SQL Datenbank per SQL Skript anlegen
  • Connection Strings in Web.config ändern
  • Debug Option in Web.Config deaktivieren

Vermutlich wurde sogar die ein oder andere Option in der Auflistung vergessen.

Mit Visual Studio klappt das viel einfacher. Keine Admin Aufgaben am Webserver, keine Config Änderungen. Vorausgesetzt man setzt auf die Dienste Azure Web Apps (vormals Web Sites) und SQL Azure.

Im Visual Studio 2013 Web Projekt wird per Rechtsklick der Publish Assistent gestartet.

azure1

Dafür ist natürlich eine Microsoft ID und ein Azure Konto nötig. Die Basic Web App wird von Microsoft mit einem Übertragungslimit kostenfrei angeboten.

In diesem Dialog kann man auch die nötige Datenbank samt SQL Azure erzeugen lassen.

Der Publishing Dienst ist so clever, das er automatisch die Connection Strings in der Web.Config austauscht um statt der lokalen Datenbank die Web Datenbank zu verwenden. Die nötigen Einstellungen dazu werden im Assistenten vorgenommen.

Azure2

Dieser Schritt ist Segen und Fluch zugleich. Es existieren zwei unterschiedliche Versionen der Datei Web.config. Die lokale und eine Remote Version. Im Server Explorer (aus Visual Studio) finden sich im Bereich Azure – Web Sites – Files – eine Möglichkeit die Remote Dateien direkt zu öffnen und sogar zu editieren.

azure4

Die Einstellungen dazu werden in einem Publishing Profil abgelegt. Dieses basiert auf XML und endet auf pubxml.

image

Diese Deployment Deklaration ist mächtig und erlaubt es auch zusätzliche Inhalte per Publis zu deployen. XML typisch findet sich die Regeln für die Connections Strings auf leicht im Code.

   1:   <ItemGroup>
   2:      <MSDeployParameterValue Include="$(DeployParameterPrefix)DefaultConnection-Web.config Connection String">
   3:        <ParameterValue>Data Source=tcp:lm7utauvto.database.windows.net,1433;
   4:  Initial Catalog=ppedvdemo1_db;User Id=ppedv@lm7utauvto;Password=seherGeheim</ParameterValue>
   5:      </MSDeployParameterValue>

Mit Rechtsllick auf diese pubxml Datei im Visual Studio Solution Explorer öffnet sich ein weiteres Stück Redmonder Magie- “Add Config Transform” erzeugt für die debug und release Varianten der Web.Config Transformatonslogik in einer Schema Definitions Sprache. Wer für Release den Debug Switch entfernen möchte findet in der Datei Web.Release.Config folgende Anweisung.

   1:   <system.web>
   2:      <compilation xdt:Transform="RemoveAttributes(debug)" />

Beim ersten Deploy werden alle Assemblies kopiert und die leere Datenbank auf Azure angelegt. Dies dauert ein wenig länger und kann ganz gut im Visual Studio Output Window verfolgt werden.

Dabei fällt auf, das auch die .mdf Datei aus dem APP_DATA Verzeichnis kopiert wird. Da dies unnötig ist, kann man das in den Publish Web Settings auch deaktivieren.

image

Nun sollen die Daten aus der Azure Datenbank direkt editiert werden.  Im Server Explorer erscheint die neue SQL Azure Datenbank. Zwischen mir und Azure steht nun nur noch die Azure Firewall. Diese beschränkt den Zugriff auf bestimmte IP Adressen.  Um von zuhause den SQL Manager zu nutzen muss die dabei genutzte IP Adresse meines Providers beim Azure Dienst freigegeben werden. Auch das macht Visual Studio quasi von alleine.

Azure3

Das Einfügen von neuen Datensätzen geschieht dann auch ohne zusätzliche Software- nur Visual Studio.

azure5

Weitere Einstellungen, wie die Wahl des genutzten .NET Frameworks lassen sich in den Einstellungen der Azrure Web Site einrichten. Auch hier führt eine Rechtsklick auf einen weiteren Settings Dialog.

azure6

Dessen Möglichkeiten erinnern an die Management Console des IIS Webservers. So kann man das Logging dort aktivieren und auch die Logfiles sichten.

Jedes weiter Publishing erfordert lediglich einen Rechtsllick auf das Projekt oder auf einen einzelne Datei.

HTML5 Input Type Number als Ganzzahl

Eigentlich wollte ich diesen Blog Post irgendwie ala “die wundersamen Abenteuer des Robinson Crusoe” nennen. Aber Google und SEO verbietet das. Worum geht es? Wieder einmal um was banales in HTML5. Benutzer sollen nur Integer Zahlen eingeben können.

imageNun hat dieser JavaScript Stack an und für sich schon Probleme mit der Tatsache, das die IT Welt nicht einfach nur Number ist. HTML5 erbt diese “Errungenschaft”.

Mit dem Input Type Number lassen sich Benutzereingaben auf Zahlen einschränken. Anhand von Ergänzenden Attributen wie Min und Max lässt sich der Bereich ins positive begrenzen. Zusätzlich dient das Attribut Step dazu auf Ganzzahlen zu beschränken. Step=1 sollte also nur Integer Werte abbilden, was übrigens auch der Default Wert ist.

Soweit der Standard, der wenig überraschend je Browser unterschiedlich implementiert ist.

Zum Test dient folgender ASP.NET Webforms und VB.NET Code.

   1:  <Script runat="server">
   2:          Protected Sub ButtonClick(sender As Object, e As EventArgs) 
   3:              Label1.Text = TextBox1.Text
   4:          End Sub
   5:      </Script>
   6:  <body>
   7:      <form id="form1" runat="server">
   8:      <div>
   9:          <asp:TextBox ID="TextBox1" runat="server" 
  10:              min="0"
  11:              step="1"
  12:              TextMode="Number"></asp:TextBox>
  13:          <asp:Button ID="Button1" runat="server" Text="Button"  OnClick="buttonclick"/>
  14:          <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
  15:      </div>
  16:      </form>

 

Der Chrome Browser versucht mit einem Doppelplfeil einen sanften Hinweis auf die erwarteten Eingaben zu geben.

html5-b

Wenn das Formular ohne novalidate definiert ist, wird der Browser eine Fehlermeldung ausgeben. Falls andere Validierungsframeworks wie aus JQuery eingesetzt werden, wird novalidate im FORM Element gesetzt. Allerdings kann der Benutzer dann Komma und Tausender Trennzeichen Punkt eingeben.

image

Den absoluten Burner (O-Ton Carmen Geiss) liefert der Internet Explorer 11.

html5-a

Ist schon seltsam genug, aber nun treiben wir das auf die Spitze. Im Codebehind wird der Rückgabewert per CInt in einen Integer umgewandelt.

IE 11 rundet erwartungsgemäß auf

image

Chrome ändert die Dimension, was einigermaßen überraschende Konsequenzen haben kann

image

Chrome macht aus dem Komma beim Post einen Tausender Trennpunkt.

Im Ergebnis ist der Input Type Number kaum zu gebrauchen.  Vermutlich sinnvoller ist der Regular Expression unterstützende Typ Text in Verbindung mit Pattern (\d*).

HTML5 ist eben ein fluent Standard.

Training, Schulung, April Aktion

Month List