Single Page Applications – Eine SPA-Anwendung

written by devangelist on April 27, 2013 in Allgemein and API and ASP.NET and ASP.NET MVC Web API and Javascript and jQuery and Knockout and MVVM with 2 comments

Eine SPA-Anwendung – Balsam für die Seele – oder ein wohltuendes Werkzeug für viele Entwickler. Es ist schon richtig, diese neuartigen Anwendungen als Hype zu bezeichnen, welche komplett ohne Postback und klassischem, kompletten Seitenwechsel auskommen, schick und performant sind, und neuerdings auch schnell und einfach geschrieben werden können. Es gibt ja mittlerweile die passenden Frameworks dazu, welche über Nacht aus Grund und Boden erwachen.

Eine Single Page Application ist, wie der Name schon sagt, eine App bestehend aus nur eine Seite. Damit ist im Normalfall eine Webseite gemeint, welche die benötigten Resourcen wie JavaScript und Stylesheet-Dateien, sowie den HTML Code bereits beim ersten Aufruf lädt. Das ein- und Ausblenden von Seiteninhalten oder das Laden und Speichern von Datensätzen, wird anschließend von JavaScript übernommen. Es fällt dabei sofort ins Auge, dass Ajax eine der Hauptrollen spielt und dabei ein großer Overhead an geladenen und gesendeten Daten entfällt.

Wer es bereits versucht hat, eine solche Anwendung mit JavaScript zu entwickeln, ohne passendem Framework wohlgemerkt (jQuery ausgeschlossen), der wird bestimmt irgendwann an seine Grenzen gestoßen sein. Spätestens dann, wenn die Anzahl an Codezeilen ins Unendliche steigt und der Code weder wart- noch testbar ist. Das Wors-Case Szenario, sozusagen, denn weder JavaScript noch Sie werden dabei eine Freude haben.

Allerdings gibt es bereits jetzt zahlreiche Anwendungen, welche von dieser neuen Möglichkeit Gebrauch machen und es gibt viele Wege, eine sogenannte SPA-Anwendung zu entwickeln. Eine davon zeigt uns unter anderem Microsoft seit dem VisualStudio Tools Update 2012.2, welches eine Projektvorlage für Single Page Applications mit Knockout (und WebAPI) beinhaltet und damit bekannt gibt, in welche Richtung sich die Webentwicklung im .NET-Stack bewegt.

Wie mächtig diese Kombination von Knockout und WebAPI letztendlich ist, zeigen viele Beispiele, wie unter anderem die Todo-App (BTW: Todo ist das neue Hello World) der Projektvorlage. Da es aber in Real-World Anwendungen nur (leider) selten um Todo-Anwendungen handelt, mit welche man sich tag-täglich beschäftigt, hilft diese Post-Serie die neuen Möglichkeiten von SPA erfolgreich in kleine, aber auch große Anwendungen zu integrieren.

Knockout und das MVVM-Pattern

Das Model View ViewModel-Pattern hat praktisch gesehen in Knockout eine fundamentale Funktion: Daten sollen an eine View gebunden werden, ohne irgendwelche DOM-Manipulationen (damit sind Funktionen gemeint, welche die HTML-Ausgabe verändern wie z.B. jquery.val oder jquery.html) direkt im Code des Viewmodels zu beherbergen. Das heißt, das ViewModel bleibt jQuery-frei. Eigentlich interessant, welche Begriffe sich in den letzten Jahren aus einem Fachjargon so entwickeln können, oder? Die durch dieses Pattern vermiedene Abhängigkeit ist von großem Vorteil, angesichts der Tatsache, dass häufig so gut wie jede DOM-Manipulation in großen Blöcken jQuery-spezifischen-Code geschrieben werden. Es kann natürlich auch jQuery verwendet werden, allerdings nicht an dieser Stelle oder nicht zu diesem Zweck, später dazu mehr.

Das Model

Das ist der einfache Teil: Das Model beinhaltet die Daten, es ist also nichts weiter als ein Datencontainer. Ein primitives JSON-Objekt reicht an dieser Stelle völlig aus. Etwas weiter und tiefer in JavaScript geschaut, könnte es sich auch um eine Konstruktor-Funktion handeln:

var todo = {
    id: 1,
    title: 'Zu erledigen 1',
    done: false
}

var Todo = function(id, title, done) {
    this.id = id;
    this.title = title;
    this.done = done;
}
var todo1 = new Todo(1, "Zu erledigen 1");

Eine dazu passende View, mit den in Knockout verwendeten Data-Bind-Attributen, sieht wie folgt aus:

<div>
    <span data-bind="text: title" />
    <input type="checkbox" data-bind="checked: done" />
</div>

Neben den verwendeten text und checked-Bindings, gibt es noch viele weitere mehr. Ziel soll jenes sein, in diesen Bindings die DOM-Manipulation zu kapseln und vom restlichen Code unabhängig zu machen.

Es fehlt nurmehr der magische Aufruf, der die Daten an die View bindet:

ko.applyBindings(todo);

Es ist klar zu erkennen, dass diese Variante nur in eine Richtung funktioniert, nämlich in jene, die Daten darzustellen. Bestünde der Titel des Todo-Items aus einer Textbox, würde sich der Inhalt des Models nicht verändern. Dafür bietet Knockout Observables, welche als Herzstück des Frameworks einen Two-Way-Effekt bieten, soll heißen bei Änderung in der View, werden diese auf das Model übertragen:

var Todo = function(id, title, done) {
    this.id = ko.observable(id);
    this.title = ko.observable(title);
    this.done = ko.observable(done);
}

Nun werden die Änderungen, welche in der UI vorgenommen werden, auf das Model übertragen. Allerdings fehlt noch jegliche Art von Interaktion, denn wann und wo ein neues Todo-Item erstellt wird, liegt noch in der Luft.
Möchte das Model mit der View oder umgekehrt interagieren, dann kann dies über das Viewmodel abgewickelt werden:

var ViewModel = function() {
    var self = this;
    self.todos = ko.observableArray();
    self.addTodo = function(id, title, done) {
        self.todos.push(new Todo(id, title,  done));
    }
}

Es gibt nun drei Komponenten: Das Model, als Datencontainer, dazu die View, die Daten darzustellen, und das ViewModel, welches die beiden miteinander verknüpft.

Das ViewModel erfüllt neben dem Zweck des data-binding auch jenen der Interaktion, das heißt also, dass beispielsweise über ein Event (wie ein Click) neue Todo-Items der Liste hinzugefügt werden können. Allerdings erst, wenn diese auch vom Server gespeichert worden sind. Der Erstell-Vorgang ist dabei relativ einfach, denn der Client sendet einen POST-Request an den Server (POST=Erstellen/Create), welcher bei erfolgreichem Erstellen (zum Beispiel in der Datenbank) eine Antwort mit einem Statuscode 201 (Created) sendet:

var ViewModel = function() {
  var self = this;
  self.todos = ko.observableArray();
  self.createTodo = function() {
    $.ajax({
      url: '/api/todo/',
      data: '{Title: "Neues Todo-Item"}',
      type: 'POST',
      contentType: 'application/json',
      statusCode: {
        201: function (result) {
          self.todos.push(new Todo(result.Id, result.Title));
        }
      }
    }
  });
}

JQuery bietet für die Rückgabe ein schickes StatusCode-Objekt, bei welchem auf 201 oder 500 (Server-Error) reagiert werden kann.
Serverseitig, sprich im Controller der WebAPI (kurze Einführung), sieht die Funktion zum Speichern wie folgt aus:

public class TodoController
{
    // POST api/Todo
    public HttpResponseMessage PostTodo(Todo todo)
    {
        if (ModelState.IsValid)
        {
            db.Todoes.Add(todo);
            db.SaveChanges();

            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, todo);
            response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = todo.Id }));
            return response;
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        }
    }
}

Auf erstem Blick erkennbar ist das ModelState. Dieses wird, so wie auch im MVC-Framework, das Model validieren und bei Fehlern diese an den Client zurückgeben. Falls der Request gültig ist, wird ein neuer Datensatz erstellt und als Antwort 201: Created an den Client gesendet, welcher anschließend seine UI aktualisieren kann. Konnte das Model nicht validiert werden, wird über Request.CreateErrorResponse(<statusCode>, ModelState) ein Fehler zurückgegeben, welcher wie oben beschrieben, in jQuery mit statusCode: { 500: function() {}} abgefangen werden kann.
Kleiner Punkt am Rande: Die Location im Header ist nichts anderes als die Angabe, unter welcher die neu erstellte Ressource erreichbar ist (Zum Beispiel: /api/todo/1 für das Todo mit der ID 1).

Fazit

Die Single Page Anwendungen sind mittlerweile Realität geworden und Microsoft zeigt, in welche Richtung es geht. Durch die Projektvorlage werden auch Neulinge animiert, diese Technik auszuprobieren – und wer weiß, vielleicht auch erfolgreich zu implementieren. Es gibt keine Bestätigung dafür, dass Knockout morgen noch da sein wird, denn wie oft gab es Technologien, die es schnell zur Spitze geschafft haben und ebenso schnell wieder vom Markt verschwunden sind? Das ist aber kein Grund zur Sorge, denn es ist ja nicht so, dass das Framework von heute auf morgen nicht mehr funktionieren würde. Außerdem gibt es auch Projektvorlagen für Backbone.js und sicherlich bald auch für andere Frameworks. Ich, als großer JavaScript-Fan erfreue mich natürlich dieser Entscheidung.

Single Page Applications – Eine SPA-Anwendung: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 5,00 von 5 Punkte, 7 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.