Jetzt Knockout.js lernen: Mapping

Im letzten Teil meiner Serie zu Knockout.js beschäftigen wir uns mit dem Mapping Plugin. In einigen der letzten Beispiele wurde mit JSON gearbeitet und so Daten simuliert, die vom Server stammen. Damit diese an eine View gebunden wurde, musste das ViewModel (welches Observables enthält) manuell aufgefüllt werden. Das Mapping Plugin bietet hier eine Unterstützung, die in diesem Anwendungsfall sehr viel Aufwand spart. Dieser Artikel zeigt wie es funktioniert.

Voraussetzungen

Damit das Mapping-Plugin verwendet werden kann, muss es via github bezogen werden. Hier der Direktlink zur aktuellsten Version. Das Script ist dann natürlich entsprechend einzubinden.

Grundlagen / Wiederholung

In den letzten Teilen dieser Serie wurden alle ViewModels manuell erstellt und sahen – mehr oder weniger – so aus:

var MyViewModel = function() {

    this.title = ko.observable();    

    this.author = ko.observable();

    this.pages = ko.observable();

}

Die Daten wurden dann so gesetzt:

myViewModel.title('Ein Buch');

myViewModel.author('Ein Autor');

myViewModel.pages(500);

So musste das für jede Variable vorgenommen werden. Ein recht großer Aufwand, vor allem, wenn die Struktur komplexer wird.

Knockout.mapping kann diesen Aufwand für uns stark minimieren.

Beispiel

Nun aber zu einem Beispiel an Hand dessen wir uns ansehen, wie knockout.mapping funktioniert. Dazu benötigen wir eine View, die mit einer Schaltfläche ausgestattet ist. Bei einem Klick darauf werden JSON-Daten vom Server geladen und an die View gebunden. Hier vorerst die View:

<div id="control">

    <input type="button" data-bind="click: loadData" value="Load data"></input>

</div>

 

<div id="data">

    <h2>Loaded Data</h2>

    <p>

        <span data-bind="text: userName"></span><br/>

        <span data-bind="text: tweetCount"></span>

    </p>

</div>

Soweit noch nichts Neues, aber wenden wir uns der JavaScript-Seite zu.

$(function(){

 

var OverallViewModel = function() {

 

    this.loadData = function() {

        $.getJSON('dl.dropbox.com/u/30654117/Demos/JavaScript/Knockout.js/Mapping/data/data.json', function(data) {

            var viewModel = ko.mapping.fromJS(data);

            ko.applyBindings(viewModel, document.getElementById('data'));

        });

    };

}

 

var firstViewModel = new OverallViewModel();

 

ko.applyBindings(firstViewModel, document.getElementById('control'));

 

});

Hier wird ein ViewModel namens OverallViewModel definiert und an das DIV mit der Id control gebunden. Darin enthalten ist die Funktion loadData, welche bei einem Klick auf die Schaltfläche ausgelöst wird.

Darin wird im ersten Schritt das JSON vom Server bezogen. Im Anschluss wird ko.mapping.fromJS mit den Daten als Parameter aufgerufen. Das Ergebnis ist ein ViewModel, welches die entsprechenden Eigenschaften, welche im JSON definiert sind (als Observables) besitzt.

Der Vollständigkeit halber hier das JSON:

{

    "userName": "Norbert Eder",

    "tweetCount": 10000

}

Das resultierende ViewModel wird nun via ko.applyBindings an das DIV mit der Id data gebunden und die Daten kommen sofort zur Anzeige. Das einfache Beispiel ist fertig. Aber das Plugin kann noch mehr.

Ins Mapping eingreifen

Meine erste Frage – und das wird vermutlich jedem so gehen, war:

So ein einfaches ViewModel ist ganz nett, aber was, wenn ich Funktionen, berechnete Eigenschaften etc. benötige?

Aber auch hierfür gibt es eine entsprechende Lösung.

Erweitern wir obiges Beispiel ein wenig. Zu den rudimentären Tweet-Informationen sollen nun einzelne Tweets hinzugefügt und angezeigt werden. Jeder Eintrag soll mit einer Schaltfläche bestückt werden, mit deren Hilfe ein Retweet durchgeführt werden kann. Auch das sollte nun recht einfach gehen, haben wir uns doch bereits mit Templates beschäftigt. Die Herausforderung ist jedoch, die Tweets mit einem eigenen ViewModel zu versehen, welches eine entsprechende Funktion retweet anbietet, um eben diese Aktion ausführen zu können.

Zuerst aber ein Blick auf die Daten, zwecks Übersicht der durchgeführten Änderungen:

{

    "userName": "Norbert Eder",

    "tweetCount": 10000,

    "tweets": [

        {

            "content": "Ein Testtweet der mit Sicherheit 140 Zeichen nicht überschreitet."

        },

        {

            "content": "Knockout.js ist schon eine sehr feine Sache!"

        },

        {

            "content": "Bin schon gespannt auf Backbone.js. Soll auch nett sein."

        }

    ]

}

Nun muss die View umgebaut und mit einem Template versehen werden, auch noch nicht spektakulär:

<div id="control2">

    <input type="button" data-bind="click: loadData" value="Load data"></input>

</div>

 

<div id="data2">

    <h2>Loaded Data</h2>

    <p>

        <span data-bind="text: userName"></span><br/>

        <span data-bind="text: tweetCount"></span>

    </p>

    <div data-bind="template: { name: 'tweet-template', foreach: tweets }"></div>

</div>    

 

<script type="text/html" id="tweet-template">

    <p data-bind="text: content"/>

    <input type="button" value="Retweet" data-bind="click: retweet"/></input>

</script>

Interessant ist das ViewModel, oder besser gesagt, sind die beiden ViewModels, da für die Anzeige eines einzelnen Tweets ein weiteres ViewModel (nennen wir es TweetViewModel) definiert werden muss. Immerhin möchten wir eine Funktion unterbringen. Darin sehen wir nun auch bereits den ersten Trick:

var TweetViewModel = function(data) {

    ko.mapping.fromJS(data, {}, this);

 

    this.retweet = function() {

        alert("retweeted");

    };

}

Die Funktion retweet ist einfach und zeigt hier lediglich einen Alert an. Zumindest kann so die Funktionalität getestet werden. Interessanter ist, dass das ViewModel Daten als Parameter übergeben bekommt und diese via ko.mapping.fromJS auf sich selbst appliziert. Damit werden für die in den Daten enthaltenen Eigenschaften korrespondierende Observables am ViewModel erzeugt. Erster Teil erledigt.

Nun muss noch ein Weg gefunden werden, eine Instanz von TweetViewModel pro Item zu erzeugen. Dazu muss das Haupt-ViewModel ein wenig adaptiert werden.

Hinweis: Dieses wurde mit einem anderen Namen als im einfachen Beispiel zu sehen, da es im gleichen Beispiel zu finden ist, Download siehe weiter unten.

Sehen wir es uns an:

var AnotherViewModel = function() {

 

    this.loadData = function() {

        $.getJSON('dl.dropbox.com/u/30654117/Demos/JavaScript/Knockout.js/Mapping/data/data2.json', function(data) {

            var viewModel = ko.mapping.fromJS(data, mapping);

            ko.applyBindings(viewModel, document.getElementById('data2'));

        });

    };    

}

Abgesehen davon, dass die Daten nun von einer anderen Quelle bezogen werden, sticht diese Änderung ins Auge:

var viewModel = ko.mapping.fromJS(data, mapping);

Es wird ein neuer Parameter übergeben. Eine Konfiguration für das Mapping.

var mapping =  {

    'tweets':  {

        create: function(options) {

            return new TweetViewModel(options.data);

        }

    }

}

Darin wird ein create-Callback definiert, der sich auf das Array tweets bezieht (siehe Daten weiter oben) und für alle Einträge aus dem tweets-Array (siehe Daten) aufgerufen. Als Parameter werden options übergeben. Diese enthalten:

  • data: Ein JavaScript-Objekt mit den Daten
  • parent: Das Eltern-Objekt oder das Array zu dem die Daten gehören

Schlussendlich definiert die Zeile

return new TweetViewModel(options.data);

dass eine neue Instanz von TweetViewModel instanziiert werden soll. Die Daten werden als Parameter übergeben und innerhalb des ViewModel weiterverarbeitet (siehe weiter oben).

Das wunderschön gestaltete Ergebnis:

Knockout.js Mapping Plugin Demo

Fazit

Diese beiden Beispiele zeigen sehr schön, dass man sich mit Hilfe des Mapping Plugins jede Menge Schreibarbeit – und damit durchaus auch Fehler – sparen kann. Durch das mögliche Eingreifen in die Erstellung der ViewModels sind alle notwendigen Szenarien vorstellbar und keine Grenzen gesetzt.

Download / Showcase

Getestet werden können die beschriebenen Beispiele hier. Gleich untenstehend der Download des gesamten Paketes:

Download Demo

Feedback

Gerne nehme ich jegliches Feedback zu diesem Beitrag, als auch zur gesamten Serie zu Knockout.js entgegen. Hat dir die Serie geholfen? Hast du Anmerkungen, Anregungen? Fehlt ein Teil, sprich, wurde ein wichtiges Thema nicht besprochen? Teile es mir doch einfach mit und verfasse gleich einen Kommentar.

Jetzt Knockout.js lernen: Die Serie

Veröffentlicht von Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Cookie-Einstellungen
Auf dieser Website werden Cookie verwendet. Diese werden für den Betrieb der Website benötigt oder helfen uns dabei, die Website zu verbessern.
Alle Cookies zulassen
Auswahl speichern
Individuelle Einstellungen
Individuelle Einstellungen
Dies ist eine Übersicht aller Cookies, die auf der Website verwendet werden. Sie haben die Möglichkeit, individuelle Cookie-Einstellungen vorzunehmen. Geben Sie einzelnen Cookies oder ganzen Gruppen Ihre Einwilligung. Essentielle Cookies lassen sich nicht deaktivieren.
Speichern
Abbrechen
Essenziell (1)
Essenzielle Cookies werden für die grundlegende Funktionalität der Website benötigt.
Cookies anzeigen