从Knockout observable数组中删除表中的选定行

时间:2014-11-18 21:07:15

标签: javascript jquery asp.net-mvc asp.net-mvc-4 knockout.js

好吧,我有工作代码,通过选中的复选框删除选定的行。但是我遇到了强制执行的问题,在任何给定时刻只能检查其中一个单选按钮。我的第一种方法是将click事件与每个单选按钮绑定,如果单击它,它将遍历可观察数组并标记所有" false。"然后,它只是将标志翻转为true,以触发事件。我知道这不是最好的方式,但是我对淘汰赛缺乏光彩的知识迫使我走上这条道路......尽管这种方法并不适合我。任何人都可以阐明我做错了什么或如何正确地解决这个问题?

表的html

                <table class="accountGroups information" id="tblAccountGroups">
                <tr>
                    <td width="125px;" style="font-weight: bold;">StandardAccountNo</td>
                    <td width="125px;" style="font-weight: bold; text-align: center;">Primary</td>
                    <td style="font-weight: bold;">Effective Date</td>
                    <td style="font-weight: bold;">End Date</td>
                    <td style="font-weight: bold;">Remove</td>
                </tr>
                <!-- ko foreach: NewAccountGroupDetails-->
                <tr id="Model.NewAccountGroupDetails[0].AccountGroupName" class="acctgrp-row">
                    <td>
                        <div>
                            <input style="width: 100%;" data-bind="value: StandardAccountNo, attr: {name: 'NewAccountGroupDetails[' + $index() + '].StandardAccountNo'}" />
                        </div>
                    </td>
                    <td>
                        <div style="text-align:center;">
                            <input style="width:100%;" type="radio" data-bind="value: IsPrimary, attr: {name: 'NewAccountGroupDetails[' + $index() + '].IsPrimary'}, click: $parent.markIsPrimary" />
                        </div>
                    </td>
                    <td>
                        <div>
                            <input style="width:125px;" class="datepicker" data-bind="value: EffectiveDate, attr: {name: 'NewAccountGroupDetails[' + $index() + '].EffectiveDate'}" readonly="readonly" />
                        </div>
                    </td>
                    <td>
                        <div>
                            <input style="width:125px;" class="datepicker" data-bind="value: EndDate, attr: {name: 'NewAccountGroupDetails[' + $index() + '].EndDate'}" readonly="readonly" />
                        </div>
                    </td>
                    <td>
                        <div style="text-align:center;">
                            <input type="checkbox" data-bind="checked: markedForDeletion, attr: {name: 'NewAccountGroupDetails[' + $index() + '].MarkedForDeletion'}" />
                        </div>
                    </td>
                </tr>
                <!-- /ko -->
            </table>

以下JS为页面提供支持

////VIEW MODEL FOR KNOCKOUT////
var Detail = function () {
    this.StandardAccountNo = ko.observable('');
    this.IsPrimary = ko.observable(false);
    this.EffectiveDate = ko.observable(formattedDate(new Date()));
    this.EndDate = ko.observable(formattedDate(new Date()));
    this.markedForDeletion = ko.observable(false);
};

var ViewModel = function () {
    var rawList = '@Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model.NewAccountGroupDetails))';
    this.NewAccountGroupDetails = ko.observableArray(convertJSONToKoObservableObject($.parseJSON(rawList)));
    this.NewAccountGroupDetails.push(new Detail());

    this.deleteMarkedItems = function () {
        this.NewAccountGroupDetails.remove(function (item) {
            return item.markedForDeletion();
        });
    };

    this.markIsPrimary = function () {
        for (i = 0; this.NewAccountGroupDetails().length > 0; i++) {
            this.NewAccountGroupDetails[i].IsPrimary(false);
        }
        return item.IsPrimary(true);
    };

    this.addNew = function () {
        this.NewAccountGroupDetails.push(new Detail());

        $('.datepicker').each(function (i, obj) {
            $(obj).datepicker({ changeYear: true, changeMonth: true });
        });
    }
};

ko.applyBindings(new ViewModel());

function convertJSONToKoObservableObject(json) {
    var ret = [];
    $.each(json, function (i, obj) {
        var newOBJ = {};
        for (prop in obj) {
            newOBJ[prop] = ko.observable(obj[prop]);
        }
        ret.push(newOBJ);
    });

    return ret;
}

一旦我按照我想要的方式运行页面,我将研究语法改进,例如数组的ko映射库。

3 个答案:

答案 0 :(得分:2)

在视图模型中,构造如下的删除按钮:

viewModel.remove = function (row) {
    console.log(row);
    viewModel.NewAccountGroupDetails.remove(row);
};

现在,当前上下文作为第一个参数传递给knockout中的任何回调。因此,如果您添加一个带有data-bind="click: $parent.remove"的按钮,它将使用行上下文调用viewModel.remove函数。

<tr ...>
    ...
    <td>
        <button data-bind="click: $parent.remove">Remove</button>
    </td>
</tr>

答案 1 :(得分:1)

我需要一些额外的信息,但是让我给你举个例子,并给你一些建议:

首先,建议:

  1. 为了将具有可观察属性的对象中的常规对象转换为数组,您可以使用Knockout Mapping plugin
  2. 您可以省略解析JSON的步骤。您可以简单地将JSON分配给var,如下所示:var JSON=*your serialized JSON*;(不要忘记末尾的分号。
  3. 而不是在data-bind中包含这么多代码,如下所示:NewAccountGroupDetails['+ $index() + '].EndDate,对viewmodel本身进行此计算,使用计算名称,例如EndDateName
  4. 您的viewmodel应包含selectedRow可观察对象。当用户选择行时,将行放在那里,您可以使用计算的observable来确定行是否是所选行。
  5. 考虑到您可以绑定在代码中调用函数的事件,并且此事件包含与发起事件的DOM对象关联的数据。即如果用户点击与帐户组详细信息相关联的行,您将在活动中收到该行。
  6. 2的例子:

    // Instead of:
    var viewModelJson = '[{"name": "Pepe"},{"name":"Juan"}]';
    var viewModel = $.parseJSON(viewModelJson);
    
    // Do this directly:
    
    var people = [{"name": "Pepe"},{"name":"Juan"}];
    

    由于4和5不能立即显示,这是您想要实现的简单示例。

    <ul data-bind="foreach: people">
        <li data-bind="text: name, click: $root.select,
           css: {red: $data == $root.selectedPerson()}" >
        </li>
    </ul>    
    

    请注意,当条件为true时,将应用css类red。条件是绑定到当前行的值与selectedPerson observable中的值相同。

    这是相应的JavaScript(记得引用淘汰映射!!)

    var people = [{"name": "Pepe"},{"name":"Juan"}];
    
    var PeopleModel = function(people) {
        var self = this;
        self.selectedPerson = ko.observable();    // This will hold the selected person
        self.people = ko.mapping.fromJS(people);  // Note ko.mapping!!
        self.select = function(person) {  // event receives the current data as 1st param
            self.selectedPerson(person);
        }
        self.delete = function(person) {
            // find de index of person and remove 1 item from that index
            self.people.splice(self.people.indexOf(person),1);
        }
        return self;
    };
    
    var peopleModel = new PeopleModel(people);
    
    ko.applyBindings(peopleModel);
    

    你可以run the jsfiddle here

    如果您更改点击装订以调用$root.delete而不是$root.select,则点击该列表时,您会看到该列表中的人员消失。当然,您可以添加额外的元素来执行此操作。

    注意:您可以阅读click binding on knockout js site上的文档。

    最后一条建议:使用Web API或返回JsonResult直接从服务器恢复数据的方法要好得多,并将js保存在单独的文件中。

    <强>更新 一点点模式代码。

    您可以添加此HTML:

    <input type="button" data-bind="click: removeSelected" value="removeSelected"/>
    

    在视图模型中使用此方法:

    self.removeSelected = function() {
        if (self.selectedPerson()) {
            self.delete(self.selectedPerson());
        }
    };
    

    如果这样做,当点击按钮时,如果有所选项目,它将从列表中删除。

    更新:另一个更完整的例子

    这里有一个更完整的示例in this fiddle,其中包含以下代码:

    CSS:

    body {
        font-family: Arial;
    }
    .container {
        margin: 10px 0;
        border: solid 1px #ABF;
    }
    .container > div {
        padding: 4px;
        border: solid 1px #ABF;
        position: relative;
    }
    .selected {
        border: solid 1px #00A;
        color: #00A;
        background-color: #BCF;
    }
    

    HTML:

    <div data-bind="foreach: people" class="container">
        <div data-bind="click: $root.select,
               css: {selected: $data == $root.selectedPerson()}" >
                   <!-- ko text: name --><!-- /ko -->
                   <input type="button" value="Remove" 
                       style="right:3px;top:2px; position:absolute;"
                       data-bind="click:$root.delete"/>
        </div>
    </div>    
    
    <div data-bind="visible: selectedPerson()" >
        <input type="button" data-bind="click: removeSelected" value="Remove Selected"/>
        <input type="button" data-bind="click: unSelect" value="Deselect"/>
    </div>
    
    <div data-bind="visible: selectedPerson()" class="container">
        <div>
            Selected: <!-- ko text: selectedPerson().name --><!-- /ko -->
        </div>
    </div>
    

    JavaScript的:

    var people = [{"name": "Pepe"},{"name":"Juan"},{"name":"Luis"},{"name":"Adolfo"}];
    
    var PeopleModel = function(people) {
        var self = this;
        self.selectedPerson = ko.observable();    // This will hold the selected person
        self.people = ko.mapping.fromJS(people);  // Note ko.mapping!!
        self.select = function(person) { // The event receives the current data as parameter
            self.selectedPerson(person);
        };
        self.delete = function(person) {
            // find de index of person and remove (splice) it from the observable array
            self.people.splice(self.people.indexOf(person),1);
            self.selectedPerson(null);
        }
        self.removeSelected = function() {
            if (self.selectedPerson()) {
                self.delete(self.selectedPerson());
            }
        };
        self.unSelect = function() {
            self.selectedPerson(null);
        }
        return self;
    };
    
    var peopleModel = new PeopleModel(people);
    
    ko.applyBindings(peopleModel);
    

答案 2 :(得分:0)

尝试在选择时暂时保存所选行

    function AccountGroupViewModel() {
        var viewModel = this;
        viewModel.selectedRow = null;
        // ...

        viewModel.selectRow = function (data) {
            // ...
            viewModel.selectedRow = data;
        }

        viewModel.remove = function () {
            // ...
            if (viewModel.selectedRow != null) {
                this.NewAccountGroupDetails.remove(viewModel.selectedRow);
            }
        }
    }