KnockoutJS和多个嵌套模型

KnockoutJS and multiple nested models

本文关键字:嵌套 模型 KnockoutJS      更新时间:2023-09-26

我想找到一些教程如何创建一个嵌套的视图模型与两个以上的水平,例如:

      • <
      • 订单行/gh>
      • <
      • 订单行/gh>
      • <
      • 订单行/gh>
      • <
      • 订单行/gh>

列出了商店的所有订单,当我单击一个订单时,我应该看到能够编辑和删除订单行的订单行。通过一些教程,我已经得到了这个工作,但它搞砸了,我希望重新开始(最后我开始使用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>

对多层嵌套视图模型进行数据绑定与对单层嵌套视图模型进行数据绑定是一样的。

在下面的例子中,我将以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中的内容。

<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/上运行了上述代码(尽管添加了用于添加新Store, OrderOrderRow对象的按钮)。

<标题>与模板

为了使代码更容易维护,您可以使用模板来代替。当然,像往常一样,对于这样一个小示例,好处可能不那么明显。

在使用模板的情况下,代码基本上与上面示例中的前三种情况非常相似;在合并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框一样简单(如果你想让其他绑定知道这个更改,你当然需要将一些属性更改为可观察的)。如果你想要不同的"模式",一个编辑模式和一个视图模式,你可以动态地选择你的模板,你可以在knockout文档中找到例子。

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