从挖空可观察数组中删除表中的选定行

Remove selected row in table from Knockout observable array

本文关键字:删除 观察 数组      更新时间:2023-09-26

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

表的 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 映射库。

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

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. 为了在具有可观察属性的对象中转换您的常规对象,您可以使用挖空映射插件。
  2. 您可以省略解析 JSON 的步骤。您可以简单地将 JSON 分配给 var,如下所示:var JSON=*your serialized JSON*;(不要忘记末尾的分号。
  3. 而不是在数据绑定中包含如此多的代码,如下所示: NewAccountGroupDetails['+ $index() + '].EndDate ,在视图模型本身上执行此计算,使用名为的计算,例如EndDateName
  4. 视图模型应包含可观察selectedRow。当用户选择该行时,将该行放在那里,您可以使用计算的可观察量来确定某一行是否为所选行。
  5. 考虑到您可以绑定调用代码中函数的事件,并且此事件携带与发起事件的 DOM 对象关联的数据。 也就是说,如果用户点击与帐户组详细信息关联的行,您将在事件中收到该行。

示例 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中的值相同。

这是相应的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);

你可以在这里运行jsfiddle。

如果将单击绑定更改为调用$root.delete而不是$root.select ,则在单击该绑定时,您将看到该人员从列表中消失。当然,您可以添加一个额外的元素来执行此操作。

注意:您可以在KNOCKOUT JS网站上阅读有关单击绑定的文档。

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

更新一点点模式代码。

您可以添加此 HTML:

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

而视图模型中的这种方法:

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

如果这样做,则在单击按钮时,如果有选定的项目,它将从列表中删除。

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

在这里,您有一个更完整的示例,在此小提琴中,其中包括以下代码:

.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);

选择所选行时尝试临时保存该行

    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);
            }
        }
    }