创新互联Knockout教程:第九章:高级应用举例

1   Contacts editor

这个例子和微软为演示jQuery Data Linking Proposal例子提供的例子一样的提供的,我们可以看看Knockout实现是难了还是容易了。

代码量的多少不重要(尽快Knockout 的实现很简洁),重要的看起来是否容易理解且可读。查看HTML源代码,看看如何实现的view model以及绑定的。

代码: View

Contacts

代码: View model

var viewModel = {
    contacts: new ko.observableArray([
        { firstName: "Danny", lastName: "LaRusso", phones: [
            { type: "Mobile", number: "(555) 121-2121" },
            { type: "Home", number: "(555) 123-4567"}]
        },

        { firstName: "Sensei", lastName: "Miyagi", phones: [
            { type: "Mobile", number: "(555) 444-2222" },
            { type: "Home", number: "(555) 999-1212"}]
        }
    ]),

    addContact: function () {
        viewModel.contacts.push({ firstName: "", lastName: "", phones: [] });
    },

    removeContact: function (contact) {
        viewModel.contacts.remove(contact);
    },

    addPhone: function (contact) {
        contact.phones.push({ type: "", number: "" });
        viewModel.contacts.valueHasMutated();
    },

    removePhone: function (contact, phone) {
        ko.utils.arrayRemoveItem(contact.phones, phone);
        viewModel.contacts.valueHasMutated();
    },

    save: function () {
        viewModel.lastSavedJson(JSON.stringify(viewModel.contacts(), null, 2));
    },

    lastSavedJson: new ko.observable("")
};

ko.applyBindings(viewModel);

2   Editable grid

该例是使用“foreach”绑定为数组里的每一项来render到 template上。好处(相对于模板内部使用for循环)是当你添加或者删除item项的时候,Knockout不需要重新render – 只需要render新的item项。就是说UI上其它控件的状态(比如验证状态)不会丢失。

如何一步一步构建这个例子并集成ASP.NET MVC,请参阅此贴。

代码: View

You have asked for   gift(s)

Gift name Price

代码: View model

var viewModel = {
    gifts: ko.observableArray([
        { name: "Tall Hat", price: "39.95" },
        { name: "Long Cloak", price: "120.00" }
    ]),

    addGift: function () {
        this.gifts.push({ name: "", price: "" });
    },

    removeGift: function (gift) {
        this.gifts.remove(gift);
    },

    save: function (form) {
        alert("Could now transmit to server: " + ko.utils.stringifyJson(this.gifts));
        // To transmit to server, write this: ko.utils.postJson($("form")[0], this.gifts);
    }
};

ko.applyBindings(viewModel);

$("form").validate({ submitHandler: function () { viewModel.save() } });

3   Shopping cart screen

这个例子展示的是依赖监控属性(dependent observable)怎么样链在一起。每个cart对象都有一个dependentObservable对象去计算自己的subtotal,这些又被一个进一步的dependentObservable对象依赖计算总的价格。当改变数据的时候,整个链上的依赖监控属性都会改变,所有相关的UI元素也会被更新。

这个例子也展示了如何创建联动的下拉菜单。

代码: View

Category Product Price Quantity Subtotal

Total value:

代码: View model

function formatCurrency(value) { return "$" + value.toFixed(2); }

var cartLine = function () {
    this.category = ko.observable();
    this.product = ko.observable();
    this.quantity = ko.observable(1);
    this.subtotal = ko.dependentObservable(function () {
        return this.product() ? this.product().price * parseInt("0" + this.quantity(), 10) : 0;
    } .bind(this));

    // Whenever the category changes, reset the product selection
    this.category.subscribe(function () { this.product(undefined); } .bind(this));
};

var cart = function () {
    // Stores an array of lines, and from these, can work out the grandTotal
    this.lines = ko.observableArray([new cartLine()]);   // Put one line in by default    
    this.grandTotal = ko.dependentObservable(function () {
        var total = 0;
        for (var i = 0; i < this.lines().length; i++)
            total += this.lines()[i].subtotal();
        return total;
    } .bind(this));

    // Operations
    this.addLine = function () { this.lines.push(new cartLine()) };
    this.removeLine = function (line) { this.lines.remove(line) };

    this.save = function () {
        var dataToSave = $.map(this.lines(), function (line) {
            return line.product() ? { productName: line.product().name, quantity: line.quantity()} : undefined
        });

        alert("Could now send this to server: " + JSON.stringify(dataToSave));
    };
};

var cartViewModel = new cart();

ko.applyBindings(cartViewModel, document.getElementById("cartEditor"));

4   Twitter client

这是一个复杂的例子,展示了几乎所有Knockout特性来构建一个富客户端。

用户数据存在一个JavaScript模型里,通过模板来展示。就是说我们可以通过清理用户列表里的数据来达到隐藏用户信息的目的,而不需要手动去隐藏DOM元素。

按钮将根据他们是否可操作来自动变成enabled或disabled状态。例如,有一个叫hasUnsavedChanges的依赖监控属性(dependentObservable)控制这“Save”按钮的enabled状态。

可以非常方便地从外部JSON服务获取数据,并集成到view model里,然后显示在页面上。

代码: View

Loading...

Currently viewing   user(s):

代码: View model

// The view model holds all the state we're working with. It also has methods that can edit it, and it uses
// dependentObservables to compute more state in terms of the underlying data
// --
// The view (i.e., the HTML UI) binds to this using data-bind attributes, so it always stays up-to-date with
// the view model, even though the view model does not know or care about any view that binds to it

var viewModel = {
    savedLists: ko.observableArray([
        { name: "Celebrities", userNames: ['JohnCleese', 'MCHammer', 'StephenFry', 'algore', 'StevenSanderson'] },
        { name: "Microsoft people", userNames: ['BillGates', 'shanselman', 'haacked', 'ScottGu'] },
        { name: "Tech pundits", userNames: ['Scobleizer', 'LeoLaporte', 'techcrunch', 'BoingBoing', 'timoreilly', 'codinghorror'] }
    ]),

    editingList: {
        name: ko.observable("Tech pundits"),
        userNames: ko.observableArray()
    },

    userNameToAdd: ko.observable(""),
    currentTweets: ko.observableArray([])
};

viewModel.findSavedList = function (name) {
    var lists = this.savedLists();

    for (var i = 0; i < lists.length; i++)
        if (lists[i].name === name)
            return lists[i];
};

// Methods
viewModel.addUser = function () {
    if (this.userNameToAdd() && this.userNameToAddIsValid()) {
        this.editingList.userNames.push(this.userNameToAdd());
        this.userNameToAdd("");
    }
}

viewModel.saveChanges = function () {
    var saveAs = prompt("Save as", this.editingList.name());

    if (saveAs) {
        var dataToSave = this.editingList.userNames().slice(0);
        var existingSavedList = this.findSavedList(saveAs);
        if (existingSavedList)
            existingSavedList.userNames = dataToSave; // Overwrite existing list
        else
            this.savedLists.push({ name: saveAs, userNames: dataToSave }); // Add new list

        this.editingList.name(saveAs);
    }
}

viewModel.deleteList = function () {
    var nameToDelete = this.editingList.name();
    var savedListsExceptOneToDelete = $.grep(this.savedLists(), function (list) { return list.name != nameToDelete });
    this.editingList.name(savedListsExceptOneToDelete.length == 0 ? null : savedListsExceptOneToDelete[0].name);
    this.savedLists(savedListsExceptOneToDelete);
};

ko.dependentObservable(function () {
    // Observe viewModel.editingList.name(), so when it changes (i.e., user selects a different list) we know to copy the saved list into the editing list
    var savedList = viewModel.findSavedList(viewModel.editingList.name());

    if (savedList) {
        var userNamesCopy = savedList.userNames.slice(0);
        viewModel.editingList.userNames(userNamesCopy);
    } else
        viewModel.editingList.userNames([]);
});

viewModel.hasUnsavedChanges = ko.dependentObservable(function () {
    if (!this.editingList.name())
        return this.editingList.userNames().length > 0;

    var savedData = this.findSavedList(this.editingList.name()).userNames;
    var editingData = this.editingList.userNames();
    return savedData.join("|") != editingData.join("|");
}, viewModel);

viewModel.userNameToAddIsValid = ko.dependentObservable(function () {
    return (this.userNameToAdd() == "") || (this.userNameToAdd().match(/^\s*[a-zA-Z0-9_]{1,15}\s*$/) != null);
}, viewModel);

// The active user tweets are (asynchronously) computed from editingList.userNames
ko.dependentObservable(function () {
    twitterApi.getTweetsForUsers(this.editingList.userNames(), function (data) { viewModel.currentTweets(data) })
}, viewModel);

ko.applyBindings(viewModel);

// Using jQuery for Ajax loading indicator - nothing to do with Knockout
$(".loadingIndicator").ajaxStart(function () { $(this).fadeIn(); })
                      .ajaxComplete(function () { $(this).fadeOut(); });

本文题目:创新互联Knockout教程:第九章:高级应用举例
文章路径:http://www.shufengxianlan.com/qtweb/news23/431673.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联