Benutzer:MovGP0/Knockout

  MovGP0    Über mich    Hilfen    Artikel    Weblinks    Literatur    Zitate    Notizen    Programmierung    MSCert    Physik   


Tutorial

Introduction

function AppViewModel() {    // OBSERVABLE VARIABLES    this.firstName = ko.observable("Bert");    this.lastName = ko.observable("Bertington");    // COMPUTED VALUES    this.fullName = ko.computed(function() {        return this.firstName() + " " + this.lastName();        }, this);        // EVENT HANDLER     this.capitalizeLastName = function() {        var currentVal = this.lastName();        // Read the current value        this.lastName(currentVal.toUpperCase()); // Write back a modified value    };}
<!-- Eingabe --><p>First name: <input data-bind="value: firstName" /></p><p>Last name: <input data-bind="value: lastName" /></p><!-- Ausgabe --><p>First name: <strong data-bind="text: firstName"></strong></p><p>Last name: <strong data-bind="text: lastName"></strong></p><p>Full name: <strong data-bind="text: fullName"></strong></p><!-- Button --><button data-bind="click: capitalizeLastName">Go caps</button>

Lists and Collections

// Class to represent a row in the seat reservations gridfunction SeatReservation(name, initialMeal) {    var self = this;    self.name = name;    self.meal = ko.observable(initialMeal);    self.formattedPrice = ko.computed(function() {        var price = self.meal().price;        return price ? "$" + price.toFixed(2) : "None";            });    }// Overall viewmodel for this screen, along with initial statefunction ReservationsViewModel() {    var self = this;    // Non-editable catalog data - would come from the server    self.availableMeals = [        { mealName: "Standard (sandwich)", price: 0 },        { mealName: "Premium (lobster)", price: 34.95 },        { mealName: "Ultimate (whole zebra)", price: 290 }    ];        // Editable data    self.seats = ko.observableArray([        new SeatReservation("Steve", self.availableMeals[0]),        new SeatReservation("Bert", self.availableMeals[0])    ]);    // Computed data    self.totalSurcharge = ko.computed(function() {       var total = 0;       for (var i = 0; i < self.seats().length; i++)           total += self.seats()[i].meal().price;       return total;    });        // Operations    self.addSeat = function() {        self.seats.push(new SeatReservation("", self.availableMeals[0]));    }    self.removeSeat = function(seat) { self.seats.remove(seat) }}ko.applyBindings(new ReservationsViewModel());
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2><table>    <thead><tr>        <th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>    </tr></thead>    <tbody data-bind="foreach: seats">        <tr>            <td><input data-bind="value: name" /></td>            <td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>            <td data-bind="text: formattedPrice"></td>            <td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>        </tr>        </tbody></table><button data-bind="click: addSeat, enable: seats().length < 5">Reserve another seat</button><h3 data-bind="visible: totalSurcharge() > 0">    Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span></h3>

SPA mit Hash-Navigation

function WebmailViewModel() {    // Data    var self = this;    self.folders = ['Inbox', 'Archive', 'Sent', 'Spam'];    self.chosenFolderId = ko.observable();    self.chosenFolderData = ko.observable();    self.chosenMailData = ko.observable();    // Behaviours        self.goToFolder = function(folder) { location.hash = folder };    self.goToMail = function(mail) { location.hash = mail.folder + '/' + mail.id };    // Client-side routes        Sammy(function() {        this.get('#:folder', function() {            self.chosenFolderId(this.params.folder);            self.chosenMailData(null);            $.get("/mail", { folder: this.params.folder }, self.chosenFolderData);        });        this.get('#:folder/:mailId', function() {            self.chosenFolderId(this.params.folder);            self.chosenFolderData(null);            $.get("/mail", { mailId: this.params.mailId }, self.chosenMailData);        });            this.get('', function() { this.app.runRoute('get', '#Inbox') });    }).run();    };ko.applyBindings(new WebmailViewModel());
<script src="/scripts/lib/sammy.js" type="text/javascript"></script><!-- Folders --><ul class="folders" data-bind="foreach: folders">    <li data-bind="text: $data,                    css: { selected: $data == $root.chosenFolderId() },                   click: $root.goToFolder"></li></ul><!-- Mails grid --><table class="mails" data-bind="with: chosenFolderData">    <thead><tr><th>From</th><th>To</th><th>Subject</th><th>Date</th></tr></thead>    <tbody data-bind="foreach: mails">        <tr data-bind="click: $root.goToMail">            <td data-bind="text: from"></td>            <td data-bind="text: to"></td>            <td data-bind="text: subject"></td>            <td data-bind="text: date"></td>        </tr>         </tbody></table>

Custom bindings

// ----------------------------------------------------------------------------// Reusable bindings - ideally kept in a separate fileko.bindingHandlers.fadeVisible = {    init: function(element, valueAccessor) {        // Start visible/invisible according to initial value        var shouldDisplay = valueAccessor();        $(element).toggle(shouldDisplay);    },    update: function(element, valueAccessor) {        // On update, fade in/out        var shouldDisplay = valueAccessor();        shouldDisplay ? $(element).fadeIn() : $(element).fadeOut();    } };ko.bindingHandlers.jqButton = {    init: function(element) {       $(element).button(); // Turns the element into a jQuery UI button    },    update: function(element, valueAccessor) {        var currentValue = valueAccessor();        // Here we just update the "disabled" state, but you could update other properties too        $(element).button("option", "disabled", currentValue.enable === false);    }    };ko.bindingHandlers.starRating = {    init: function(element, valueAccessor) {        $(element).addClass("starRating");        for (var i = 0; i < 5; i++)           $("<span>").appendTo(element);               // Handle mouse events on the stars        $("span", element).each(function(index) {            $(this).hover(                function() { $(this).prevAll().add(this).addClass("hoverChosen") },                 function() { $(this).prevAll().add(this).removeClass("hoverChosen") }                            ).click(function() {                 var observable = valueAccessor();  // Get the associated observable                observable(index+1);               // Write the new rating to it            });        });                },    update: function(element, valueAccessor) {        // Give the first x stars the "chosen" class, where x <= rating        var observable = valueAccessor();        $("span", element).each(function(index) {            $(this).toggleClass("chosen", index < observable());        });    }    };// ----------------------------------------------------------------------------// Page viewmodelfunction Answer(text) { this.answerText = text; this.points = ko.observable(1); }function SurveyViewModel(question, pointsBudget, answers) {    this.question = question;    this.pointsBudget = pointsBudget;    this.answers = $.map(answers, function(text) { return new Answer(text) });    this.save = function() { alert('To do') };               this.pointsUsed = ko.computed(function() {        var total = 0;        for (var i = 0; i < this.answers.length; i++)            total += this.answers[i].points();        return total;            }, this);}ko.applyBindings(new SurveyViewModel("Which factors affect your technology choices?", 10, [   "Functionality, compatibility, pricing - all that boring stuff",   "How often it is mentioned on Hacker News",       "Number of gradients/dropshadows on project homepage",           "Totally believable testimonials on project homepage"]));
<h3 data-bind="text: question"></h3><p>Please distribute <b data-bind="text: pointsBudget"></b> points between the following options.</p><table>    <thead><tr><th>Option</th><th>Importance</th></tr></thead>    <tbody data-bind="foreach: answers">        <tr>            <td data-bind="text: answerText"></td>            <td data-bind="starRating: points"></td>        </tr>        </tbody></table><h3 data-bind="fadeVisible: pointsUsed() > pointsBudget">You've used too many points! Please remove some.</h3><p>You've got <b data-bind="text: pointsBudget - pointsUsed()"></b> points left to use.</p><button data-bind="jqButton: { enable: pointsUsed() <= pointsBudget }, click: save">Finished</button>

Communicate with server

function Task(data) {    this.title = ko.observable(data.title);    this.isDone = ko.observable(data.isDone);}function TaskListViewModel() {    // Data    var self = this;    self.tasks = ko.observableArray([]);    self.newTaskText = ko.observable();    self.incompleteTasks = ko.computed(function() {        return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.isDone() && !task._destroy });    });    // Operations    self.addTask = function() {        self.tasks.push(new Task({ title: this.newTaskText() }));        self.newTaskText("");    };    self.removeTask = function(task) { self.tasks.destroy(task) };    self.save = function() {        $.ajax("/tasks", {            data: ko.toJSON({ tasks: self.tasks }),            type: "post", contentType: "application/json",            success: function(result) { alert(result) }        });    };    // Load initial state from server, convert it to Task instances, then populate self.tasks    $.getJSON("/tasks", function(allData) {        var mappedTasks = $.map(allData, function(item) { return new Task(item) });        self.tasks(mappedTasks);    });    }ko.applyBindings(new TaskListViewModel());
<h3>Tasks</h3><form data-bind="submit: addTask">    Add task: <input data-bind="value: newTaskText" placeholder="What needs to be done?" />    <button type="submit">Add</button></form><ul data-bind="foreach: tasks, visible: tasks().length > 0">    <li>        <input type="checkbox" data-bind="checked: isDone" />        <input data-bind="value: title, disable: isDone" />        <a href="#" data-bind="click: $parent.removeTask">Delete</a>    </li> </ul>You have <b data-bind="text: incompleteTasks().length">&nbsp;</b> incomplete task(s)<span data-bind="visible: incompleteTasks().length == 0"> - it's beer time!</span><button data-bind="click: save">Save</button>