Knockout mit ASP.NET MVC (Teil1)

written by devangelist on Oktober 24, 2012 in Allgemein and ASP.NET and ASP.NET MVC and Javascript and Knockout and MVVM with one Comment

Data-Binding mit Javascript – Bitte jetzt nicht an Silverlight denken. Data-Binding ist vom Prinzip her etwas sehr cooles. Dabei geht es um das Binden der Daten und das muss vor allem schnell und sauber passieren. Die Erinnerung an ASP.NET Webforms, welche im Code-Behind die Databind() Befehle ausgeführt haben, einmal im Init, im Load oder sogar im PreRender, also ganz nach Geschmack, haben einen dunklen Schatten hinterlassen. Das Ganze wurde mit ASP.NET MVC doch um einiges vereinfacht, denn dort gibt es kein klassisches Databind.  Nun sind wir bestimmt alle der Meinung, ein solch ausgefeiltes Konzept habe eine zweite Chance verdient und genau deshalb lassen wir uns auf die Knockout-Bibliothek ein.

Bevor der eigentliche praktische Teil startet, eine kurze FAQ: Was hat Knockout mit ASP.NET MVC zu tun? Antwort: So dämlich es auch klingt, wir brauchen einen Proxy von A nach B, vom Server zum Client und genau dafür bietet das Framework eine solide Grundlage. Natürlich kann auch ab .NET 4.5 die WebAPI eingesetzt werden (sollte sie sogar auch!), aber wir wollen nicht die WebAPI erklären (dies in einem anderen Post), sondern das MVVM mit Knockout. Begonnen wird mit einer kleinen Einführung zu Knockout und zum neuen Pattern:

Das Model und die View, oder das Model-View-View-Model (MVVM).

So wie bei fast jedem Pattern welches ein M im Kürzel hat, wird auch bei Knockout ein Model benötigt, welches im Javascript-Dialekt ausgedrückt nichts anderes als ein JSON Objekt ist.

 var myViewModel = {
 name: 'Max',
 age: 27
 };

Die passende View dazu, welche den Namen und das Alter ausgeben soll, wird durch data-bind Attribute im HTML angegeben:

The name is <span data-bind="text: personName"></span>

Hinweis: Sogenannte data-Attribute sind HTML5 kompatibel und verursachen somit keine validierungs-Fehler.

Das Binden der Daten erfolgt mit folgendem Befehl:

ko.applyBindings(myViewModel);

Die eben verwendete Möglichkeit einen Text zu binden wird als text-binding bezeichnet. Neben text gibt es noch viele weitere wie value, if, visible, style, … bis hin zu eigenen – später dazu mehr.

Das Maß aller Dinge – (K)ein Grid:

Eine erfreuliche Nachricht: Komplexe Grids mit hunderten Funktionen sind Sache von Gestern. Hier reicht erst mal eine einfache foreach, welche über ein Array iteriert und die einzelnen Datensätze bindet:

<table>
    <thead>
        <tr><th>First name</th><th>Age</th></tr>
    </thead>
    <tbody data-bind="foreach: persons">
        <tr>
            <td data-bind="text: name"></td>
            <td data-bind="text: age"></td>
        </tr>
    </tbody>
</table>

 

ko.applyBindings({
    persons: [
        { name: 'Max', age: 22 },
        { name: 'Otto', age: 30 },
        { name: 'Robert', age: 25 }
    ]
});

In Zeile 7 (inner scope des foreach-binding) wird wiederum das text-binding verwendet. Möchte man auf das Array, also eine Ebene höher zurückgreifen, geht dies mit $parent. Alternativ kann mit $root auf das Haupt-Model und mit $data auf den aktuellen Kontext zurückgegriffen werden.

firstName -> "Max"
$data.firstName -> "Max"
$parent.count() -> 3
$root.people.count() -> 3

Es gibt jetzt also ein ViewModel und eine View, welche wie der Name schon sagt der Ausgabe dient und keinerlei Logik beinhalten sollten. Was noch fehlt ist das eigentliche Model, sprich der Ort, wo die Business Logik behandelt wird. Die Daten stehen in real-World Anwendungen nicht statisch in den Javascript-Dateien, sondern werden mit Ajax geladen und genau dann wird eine Anwendung schmackhaft, welche vom Knockout Framework Gebrauch macht. Die Rede ist dann von einer Single-Page-Application, eine Anwendung bestehend aus nur einer Seite.

Angenommen die Daten des Grids im eben gezeigten Beispiel sollen dynamisch geladen werden, wird das Model so aussehen:

function GridModel() {
    var self = this; // Zugriff auf das Model zu vereinfachen.
    self.items = ko.observableArray(); // In diesem Array steckt die Magie, gleich mehr dazu.
    self.loadData = function() {
        $.get('/Controller/Action/', function(result) {
            self.items(result);
        });
    }
}

Das Model besteht aus einer Eigenschaft items und einer Funktion loadData. An dieser Stelle angelangt kommt die wohl am kniffligste Angelegenheit in Knockout: Die Observables.

Das WTF der Observables

Wie der Name schon sagt, ein Observer hat etwas mit beobachten zu tun (nachzulesen im Post: Observer Pattern). Stellen Sie sich vor, sie Updaten ein Objekt in Javascript und die UI aktualisiert sich nebenbei. Automatisch, ohne zusätzlichen Code. Das klingt nach einer ganzen Menge Code-Zeilen weniger. Und Sie wissen: Jede Zeile Code die gelöscht werden kann, ist eine gute Zeile!
Also, ein Objekt, ein sogenanntes Observable oder ObservableArray wird aktualisiert items(neuerWert) und die UI, in welcher dieses als data-bind Attribut angegeben wird (data-bind=”foreach: items”) wird im Hintergrund angepasst.

Besser verständlich durch das Grid-Beispiel: In Zeile 6 wird die items-Eigenschaft mit dem vom Server erhaltenen Wert aktualisiert. An der Stelle passt Knockout alles innerhalb des Bindings an. Und zwar genau dann, wenn die loadItems Funktion Aufgerufen wird. Wo dieser Funktionsaufruf stattfindet ist egal. Per Hand, oder mit einem Button – wo immer ein Neuladen der Daten benötigt wird. Interessant wird dies natürlich mit Übergabe von Parametern, um zum Beispiel ein Paginated-Grid zu erstellen. Fast schon zu einfach: $root.loadData(<Seite>). Damit wird eine mächtige Interaktion im HTML ermöglicht. Manche mögen dies zwar eher skeptisch betrachten, für andere allerdings eine große Erleichterung.

Dies ist nur ein kleiner Teil der Möglichkeiten, welches das Framework bietet. Mehr dazu gibt es im nächsten Post, wo Observables näher erklärt und ihre Funktionen detailliert veranschaulicht werden: observables-deep-dive.

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (No Ratings Yet)
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.