KnockoutJS和多个嵌套模型

时间:2014-05-07 10:34:37

标签: javascript knockout.js viewmodel

我试图找到一些有关如何创建具有两个以上级别的嵌套视图模型的教程,例如:

  • 存储
    • 顺序
      • 订单行
    • 顺序
      • 订单行
      • 订单行
  • 存储
    • 顺序
      • 订单行

为商店列出所有订单,当我点击订单时,我应该看到能够编辑和删除订单行的订单行。我通过以下几个教程得到了这个工作,但它搞砸了,我想重新开始(最后我开始使用jQuery来得到我想要的但是感觉就像作弊和做某事做了一半)。有没有关于这个或任何指针的任何教程我应该从哪里开始(KnockoutJS或其他框架?是的我已经按照knockoutjs.com上的教程但是仍然坚持第三级的功能。

提前致谢。

编辑:按照此http://jsfiddle.net/peterf/8FMPc/light/

进行操作

JS(简化)

// required by sharepoint
ExecuteOrDelayUntilScriptLoaded(loadTeams, "sp.js");                    

ko.observable.fn.beginEdit = function (transaction) {
    var self = this;
    var commitSubscription, rollbackSubscription;

    if (self.slice) {
        self.editValue = ko.observableArray(self.slice());
    }
    else {
        self.editValue = ko.observable(self());
    }

    self.dispose = function () {        
        commitSubscription.dispose();
        rollbackSubscription.dispose(); 
    };

    self.commit = function () {
        self(self.editValue());
        self.dispose();
    };

    self.rollback = function () {
        self.editValue(self());        
        self.dispose();
    };

    commitSubscription = transaction.subscribe(self.commit, self, "commit");    
    rollbackSubscription = transaction.subscribe(self.rollback, self, "rollback");

    return self;
}

 function TeamModel (){
    var self = this;
    self.Team = function(title, members) {
        this.title = title;
        this.members = members;
    }
    self.editingItem = ko.observable();   
    self.editTransaction = new ko.subscribable();        
    self.isItemEditing = function(task) {
        return task == self.editingItem();
    };

    self.editTask = function (task) {
        if (self.editingItem() == null) {
            task.beginEdit(self.editTransaction);
            self.editingItem(task);
        }
    };

    self.removeTask = function (task) {
        if (self.editingItem() == null) {
            var answer = confirm('Are you sure you want to delete this task? ' + task.title());
            if (answer) {
                // SharePoint client object model to delete task
            }
        }
    };        

    self.applyTask = function (task) {
        self.editTransaction.notifySubscribers(null, "commit");        
        // SharePoint client object model to update task                    
        //  hides the edit fields
        self.editingItem(null);                                         
    };

    self.cancelEdit = function (task) {
        self.editTransaction.notifySubscribers(null, "rollback");        
        self.editingItem(null);
    };

    self.Member = function(name, id) {
        this.name = name;
        this.Tasks = ko.observableArray([]);
        this.Task = function(id, title, priority, userComment, managerComment) {
            this.id = ko.observable(id);
            this.title = ko.observable(title);
            this.priority = ko.observable(priority);
            this.userComment = ko.observable(userComment);
            this.managerComment = ko.observable(managerComment);
            this.beginEdit = function(transaction) {
                //this.title.beginEdit(transaction);
                //this.userComment.beginEdit(transaction);
            }                                                                                                                               
        }
        this.id = id;
        this.retrieveTasks = function() {
            if(this.Tasks().length === 0) {
                // First click, expand                                                    
                // SharePoint client object model to get tasks
            } else {
                // Collapse
                //this.Tasks.removeAll();                   
            }       
        }
    }
    self.Teams = ko.observableArray([]);                                 
    self.retrieveTeams = function() {  
            // SharePoint client object model to get a list of teams and their members
            self.Teams.push(new self.Team(oListItem.get_item('Title'), members));  
    }               
}

function loadTeams() {
    var VM = new TeamModel();
    VM.retrieveTeams();     
    VM.availableRankings = ["1","2","3","4","5","6","7","8","9","10"]
    ko.applyBindings(VM);           
}

HTML

<div id="Workload" data-bind="visible: Teams().length>0">
    <div data-bind="foreach: Teams" class="teams">                         
        <div >
            <h3 data-bind="text: title"></h3>
                <div data-bind="foreach: members">
                    <div class="member">
                        <div data-bind="click: retrieveTasks">
                            <span data-bind="text: name" class="name"></span>
                        </div>
                        <table class="tasks" data-bind="visible: Tasks().length>0">
                            <tr>
                                <td class="title">Title</td>
                                <td class="priority">Priority</td>
                                <td class="user-comment">User Comment</td>
                                <td class="manager-comment">Manager Comment</td>                                                                                                                                                                                                                                                                                                                          
                            </tr>
                            <tbody data-bind="foreach: Tasks">
                                <tr class="row">                                                                                                                                    
                                    <td class="rowItem">
                                        <input type="text" class="edit" data-bind="value: title, visible: $root.isItemEditing($data)"/>
                                        <label class="read" data-bind="text: title, visible: !$root.isItemEditing($data)"/>
                                    </td>
                                    <td class="rowItem">
                                        <select class="edit priority" data-bind="options: $root.availableRankings, value: priority, visible: $root.isItemEditing($data)"></select>
                                        <label class="read" data-bind="text: priority, visible: !$root.isItemEditing($data)" />                                                                                                                                   
                                    </td>
                                    <td class="rowItem">
                                        <textarea rows="3" cols="25" class="edit userComment" data-bind="value: userComment, visible: $root.isItemEditing($data)"></textarea>
                                        <label class="read" data-bind="text: userComment, visible: !$root.isItemEditing($data)"/>                                                                                                                         
                                    </td>
                                    <td class="rowItem">
                                        <textarea rows="3" cols="25" class="edit managerComment" data-bind="value: managerComment, visible: $root.isItemEditing($data)"></textarea>
                                        <label class="read" data-bind="text: managerComment, visible: !$root.isItemEditing($data)"/>                                                                                                                               
                                    </td>
                                    <td class="tools">
                                        <a class="button toolButton" href="#" data-bind="click: $root.editTask.bind($root), visible: !$root.isItemEditing($data)">
                                        Edit</a>
                                        <Sharepoint:SPSecurityTrimmedControl runat="server" Permissions="DeleteListItems">
                                        <a class="button toolButton" href="#" data-bind="click: $root.removeTask.bind($root), visible: !$root.isItemEditing($data)">
                                        Remove</a>
                                        </SharePoint:SPSecurityTrimmedControl>                                                                                                                                  
                                        <a class="button toolButton" href="#" data-bind="click: $root.applyTask.bind($root), visible: $root.isItemEditing($data)">
                                        Apply</a>
                                        <a class="button toolButton" href="#" data-bind="click: $root.cancelEdit.bind($root), visible: $root.isItemEditing($data)">
                                        Cancel</a>
                                    </td>                                                                                                                                    
                                </tr>                                         
                            </tbody>
                        </table>
                </div>
            </div>
        </div>   
    </div>
</div>

1 个答案:

答案 0 :(得分:1)

具有多个级别的数据绑定嵌套视图模型与具有单个级别的数据绑定嵌套视图模型相同。

在下面的示例中,我将使用您的示例Store -> Order -> OrderRow,假设每个Store都有storeName属性,每个Order都有orderNumber每个OrderRow都有一个runningNumber。我还在每个级别的ul内渲染了这些项目。

不使用模板

要对嵌套视图模型进行数据绑定,单个级别(本示例中的stores列表)可以类似于:

<ul data-bind="foreach: stores">
    <li>
        Store Name: <span data-bind="text: storeName"></span>
    </li>
</ul>

要对嵌套视图模型进行数据绑定,可以使用Store -> Order来完成与Store Name: <span data-bind="text: storeName"></span> <ul data-bind="foreach: orders"> <li data-bind="text: orderNumber"></li> </ul> 类似的单个级别:

Order -> OrderRow

要对数据绑定视图模型进行数据绑定,可以完成Order number: <span data-bind="text: orderNumber"></span> <ul data-bind="foreach: rows"> <li> A row with running number: <span data-bind="text: runningNumber"></span> </li> </ul> 的单个级别,如:

li

要执行此操作,嵌套在多个级别,它就像组合上面一样简单,移动第三个代码以替换第二个中的li的内容,然后替换新的第二个代码以替换{{1第一个内容。

<ul data-bind="foreach: stores">
    <li>
        Store Name: <span data-bind="text: storeName"></span>
        <ul data-bind="foreach: orders">
            <li>
                Order number: <span data-bind="text: orderNumber"></span>
                <ul data-bind="foreach: rows">
                    A row with running number: <span data-bind="text: runningNumber"></span>
                </ul>
            </li>
        </ul>
    </li>
</ul>

我基本上已经运行了上述代码(虽然添加了按钮,用于在http://jsfiddle.net/8yF6c/添加新的StoreOrderOrderRow个对象。

使用模板

为了使代码更易于维护,您可以使用模板来完成。当然,与往常一样,这样一个小例子的好处可能并不那么明显。

在使用模板的情况下,代码基本上看起来与上面示例中的前三种情况非常相似;在合并html之前。首先,商店的模板:

<script type="text/html" id="storeTemplate">
    Store Name: <span data-bind="text: storeName"></span>
    <ul data-bind="foreach: orders">
        <li data-bind="template: 'orderTemplate'"></li>
    </ul>
</script>

然后是订单的模板:

<script type="text/html" id="orderTemplate">
    Order number: <span data-bind="text: orderNumber"></span>
    <ul data-bind="foreach: rows">
        <li data-bind="template: 'orderRowTemplate'"></li>
    </ul>
</script>

最后是订单行的模板。

<script type="text/html" id="orderRowTemplate">
    A row with running number: <span data-bind="text: runningNumber"></span>
</script>

观察上述三个代码部分与第一个示例单级绑定相同,仅包含在script类型为text/html的元素中(以确保浏览器不会尝试将其作为脚本执行)。然后我们只需要在根级别开始使用storeTemplate

<ul data-bind="foreach: stores">
    <li data-bind="template: 'storeTemplate'"></li>
</ul>

这就是它。和以前一样,我在http://jsfiddle.net/Ag8U3/处运行了上面的代码(虽然添加了按钮以添加新的StoreOrderOrderRow个对象。

添加编辑和删除功能

为上述模板(或没有模板的绑定)添加编辑功能就像将span元素更改为input框一样简单(如果您希望其他绑定能够了解您将对当然需要改变一些属性是可观察的)。如果您需要不同的“模式”,编辑模式和视图模式,您可以考虑动态选择模板,您可以在淘汰文档中找到示例。

要添加删除功能,只需添加一个功能,在单击删除按钮时从列表中删除项目(例如,在deleteOrder对象上添加Store功能可以是self.removeOrder = function(order){ self.orders.remove(order); };并且然后在订单中添加一个按钮,例如<button data-bind="click: $parent.removeOrder">Remove Order</button>。我已在http://jsfiddle.net/Ag8U3/1/为模板示例添加了删除功能。