wartbare web-anwendungen mit knockout.js und model-view...

30
Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel (MVVM) Malte Clasen

Upload: others

Post on 01-Jul-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Wartbare Web-Anwendungen

mit Knockout.js und

Model-View-ViewModel (MVVM)

Malte Clasen

Page 2: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Pro

ble

m: R

eze

pte

dito

r

Page 3: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Pro

ble

m: R

eze

pte

dito

r

Page 4: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Acceptance Tests

Feature: RecipeEditor

In order to edit a recipe As a registered user I want to be able to modify all properties of a recipe

Scenario: Add Category

Given I am on the "Streuselkuchen" recipe page

And I am in edit mode

When I click on the empty category field

And I type "Blechkuchen"

Then there should be 3 categories

And the title of the third category should be "Blechkuchen"

4

Page 5: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

HT

ML

-An

sic

ht

<h3>Kategorien</h3> <ul>

<li><a href="/KuchenUndTorten"> Kuchen und Torten</a></li> <li>Kleingebäck</li>

</ul> <h2>Zutaten</h2> <h3>Teig</h3> <table> <tbody> <tr class="ingredient">

<td class="amount">400</td> <td class="unit">g</td> <td class="name"><a href="/Mehl"> Mehl</a></td>

</tr> … </tbody> </table> <h3>Streusel</h3> <table>…</table>

<img class="photo" alt="recipe" src="/Content/Images/streuselkuchen.jpg"> <div>Foto von Malte</div> <h2>Zubereitung</h2> <h3>Teig</h3> <div>Mehl in eine Schüssel geben und eine Kuhle hineindrücken. Zucker, Hefe und <emph>lauwarmen</emph> Soja-Reis-Drink hineingeben. Mit einem Küchentuch zudecken und 15 Minuten lang gehen lassen. Margarine zerlassen und ebenfalls in die Kuhle geben. Von innenheraus mit dem restlichen Mehl verkneten. Nochmals 15 Minuten lang zugedeckt gehen lassen. Auf einem mit Backpapier ausgelegten Blech ausrollen und wieder 15 Minuten lang gehen lassen.</div> <h3>Streusel</h3> <div>Mehl, Zucker und Zimt vermischen, Margarine zerlassen und alles verkneten. Streusel auf dem ausgerollten, gegangenen Teig <strong>gleichmäßig</strong> verteilen und ca. 20 Minuten bei 180°C Umluft backen.</div>

<h1>Streuselkuchen</h1>

Page 6: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

ASP.NET-Editor

veränderbare Anzahl in Listen

„neue Kategorie“

zwei Code-Pfade,

Postback, …

6

Page 7: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Knockout: Prinzip

HTML

Template

ViewModel

7

JSON

Page 8: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

JSON

[AcceptVerbs(HttpVerbs.Get)]

public ActionResult Get()

{

return Json(_model, JsonRequestBehavior.AllowGet);

}

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult Edit(RecipeModel model)

{

return Json("gespeichert um " +

DateTime.Now.ToShortTimeString());

}

8

Page 9: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

ViewModel

function RecipeViewModel(initialData) {

var self = this;

self.data = ko.observable();

this.onSave = function () { … };

this.onRemoveComponent = function (model, event) {

self.data().Components.remove(model);

}

var mapping = { … };

self.data(ko.mapping.fromJS(initialData, mapping));

};

9

Page 10: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Observable

function QuantityModel(data) {

this.Amount = ko.observable(data.Amount);

this.Unit = ko.observable(data.Unit);

}

10

Page 11: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

ObservableArray

function ComponentModel(data) {

var self = this;

this.Title = ko.observable(data.Title);

this.Preparation = ko.observable(data.Preparation);

this.Ingredients = ko.observableArray();

this.Ingredients($.map(data.Ingredients, function (item) { return new RecipeIngredientModel(item); }));

}

11

Page 12: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: Text

<h1 data-bind="text: Title"></h1>

ergibt zur Laufzeit

<h1 data-bind="text: Title">Streuselkuchen</h1>

12

Page 13: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: With

function RecipeViewModel(initialData) {

var self = this;

self.data = ko.observable();

};

ko.applyBindings(recipeViewModel);

<form action="#" id="RecipeEditor" data-bind="with: data">

<h1 data-bind="text: Title"></h1>

</form>

13

Page 14: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: Attr

<ul data-bind="foreach: Categories">

<li><a data-bind="text:Name, attr: {href:Url}"></a></li>

</ul>

14

Page 15: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Virtual Elements

<img class="photo" alt="recipe" data-bind="attr: {src: Image.Url}" />

<div>

Foto von

<!--ko text: Image.Author-->

<!--/ko-->

</div>

15

Page 16: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: Foreach

<ul data-bind="foreach: Categories">

<li><a data-bind="text:Name, attr: {href:Url}"></a></li>

</ul>

16

Page 17: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: If

<ul data-bind="foreach: Categories">

<!-- ko if:!IsEmpty() -->

<li><a data-bind="text:Name, attr: {href:Url}"></a></li>

<!-- /ko -->

</ul>

function CategoryModel(data) {

var self = this;

this.Name = ko.observable(data.Name);

this.Url = ko.observable(data.Url);

this.IsEmpty = ko.computed(function () {

return (!self.Name()) && (!self.Url());

}, this);

}

17

Page 18: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Mapping, manuell

function ComponentModel(data) {

var self = this;

this.Title = ko.observable(data.Title);

this.Preparation = ko.observable(data.Preparation);

this.Ingredients = ko.observableArray();

this.Ingredients($.map(data.Ingredients, function (item)

{ return new RecipeIngredientModel(item); }));

}

18

Page 19: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Mapping, automatisch

function RecipeViewModel(initialData) {

var self = this;

self.data = ko.observable();

var mapping = {

'Components': {

create: function (options) {

return new ComponentModel(options.data);

}

}

};

self.data(ko.mapping.fromJS(initialData, mapping));

}

19

Page 20: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Mapping, zu JSON

this.onSave = function () {

$.ajax("/Recipe/Edit", {

data: ko.mapping.toJSON(self.data),

type: "post",

contentType: "application/json",

dataType: "json",

success: function (result) { ShowLogMessage(result); }

})

.error(function (e, jqxhr, settings, exception) {

console.log(exception);

});

};

20

Page 21: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

HT

ML

-Ed

ito

r

<h3>Kategorien</h3> <ul>

<li><input type="text"></li> <li><input type="text"></li>

</ul> <h2>Zutaten</h2> <div class="toolbar edit">

<button type="button"> <img src="/list-remove.png">

</button> </div> <h3 contenteditable="true">Teig</h3> <table> <tbody> <tr class="ingredient">

<td class="amount"><input type="number"></td> <td class="unit"><input type=“text"></td></td> <td class="name"><input type=“text"></td>

</tr> … </tbody> </table> <h3 contenteditable="true">Streusel</h3> <table>…</table>

<img class="photo" alt="recipe" src="/Content/Images/streuselkuchen.jpg"> <div>Foto von Malte</div> <h2>Zubereitung</h2> <h3 contenteditable="true">Teig</h3> <div contenteditable="true">Mehl in eine Schüssel geben und eine Kuhle hineindrücken. Zucker, Hefe und <emph>lauwarmen</emph> Soja-Reis-Drink hineingeben. Mit einem Küchentuch zudecken und 15 Minuten lang gehen lassen. Margarine zerlassen und ebenfalls in die Kuhle geben. Von innenheraus mit dem restlichen Mehl verkneten. Nochmals 15 Minuten lang zugedeckt gehen lassen. Auf einem mit Backpapier ausgelegten Blech ausrollen und wieder 15 Minuten lang gehen lassen.</div> <h3 contenteditable="true">Streusel</h3> <div contenteditable="true">Mehl, Zucker und Zimt vermischen, Margarine zerlassen und alles verkneten. Streusel auf dem ausgerollten, gegangenen Teig <strong>gleichmäßig</strong> verteilen und ca. 20 Minuten bei 180°C Umluft backen.</div>

<h1 contenteditable="true">Streuselkuchen</h1>

Page 22: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

HTML-Editor

<div contenteditable="true" data-bind="html: Preparation, event: {change: onUpdatePreparation}">Mehl in eine Schüssel geben …

function ComponentModel(data) {

var self = this;

this.Preparation = ko.observable(data.Preparation);

this.onUpdatePreparation = function(model, event) {

self.Preparation($(event.target).html());

};

}

22

Page 23: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Event: Click

<button type="button" data-bind="event: { click: $root.onRemoveComponent }">

<img alt="list-remove" src="list-remove.png" />

</button>

function RecipeViewModel(initialData) {

var self = this;

self.data = ko.observable();

this.onRemoveComponent = function (model, event) {

self.data().Components.remove(model);

}

}

23

Page 24: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Binding: Value

<ul data-bind="foreach: Categories">

<li><input type="text" data-bind="value:Name"/></li>

</ul>

function CategoryModel(data) {

var self = this;

this.Name = ko.observable(data.Name); }

24

Page 25: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Event: Change

<ul data-bind="foreach: Categories">

<li><input type="text" data-bind="value:Name, event:{change: $root.onChangeCategory}" /></li>

</ul>

this.onChangeCategory = function (model, event) {

if (model.Name()) {

} else {

self.data().Categories.remove(model);

}

}

25

Page 26: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Computed

function ComponentModel(data) {

var self = this;

this.Ingredients = ko.observableArray();

this.LastIngredient = ko.computed(function () {

if (self.Ingredients().length === 0)

return null;

return self.Ingredients()[self.Ingredients().length-1];

}, this);

}

26

Page 27: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Subscribe

this.LastIngredientSubscription = null;

this.LastIngredient.subscribe(function (newValue) {

if (self.LastIngredientSubscription !== null) {

self.LastIngredientSubscription.dispose();

}

var currentLastIngredient = newValue;

if (currentLastIngredient === null)

currentLastIngredient = self.AddEmptyIngredient();

if (!currentLastIngredient.IsEmpty())

currentLastIngredient = self.AddEmptyIngredient();

self.LastIngredientSubscription = currentLastIngredient.IsEmpty.subscribe(

function (newValue) { if (!newValue) { self.AddEmptyIngredient(); } });

});

27

Page 28: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Rahmenwerk

@Scripts.Render("~/Scripts/recipe")

<script type="text/javascript">

InitRecipeViewModel(@Html.Raw(Model.Json));

</script>

var recipeViewModel;

function InitRecipeViewModel(initialData) {

recipeViewModel = new RecipeViewModel(initialData);

}

function InitRecipeEditor() {

ko.applyBindings(recipeViewModel);

ShowRecipeEditor();

}

28

Page 29: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Übersicht

• Grundlage: HTML-Ansicht

• Knockout:

– Model: Service

– View: Template

– ViewModel: Script

• Observable, ObservableArray, Compute, Subscribe

• Binding

– Inhalte: Text, Attr

– Struktur: With, Foreach, If

– Events: Click

29

Page 30: Wartbare Web-Anwendungen mit Knockout.js und Model-View ...malteclasen.de/blog/wp-content/uploads/2013/03/Wartbare_Webanw… · Wartbare Web-Anwendungen mit Knockout.js und Model-View-ViewModel

Vielen Dank für Ihre

Aufmerksamkeit.

[email protected]

www.adesso.de | malteclasen.de/blog