0

我正在学习将 MVC 4/MVVM/Knockout 用于 Web 管理的数据项目。在可观察数组上使用删除函数时,我在更新视图时遇到了问题。使用 push 或 unshift 时会发生更新,但不会删除。使用 chrome 中的调试器,我可以看到数据正在从数组中删除,更新事件不起作用。

来自 html 的片段是下表,有一个我没有包含用于添加或编辑数据的表单。

<div id="MessageDiv" data-bind="message: Message"></div>
<div class="tableContainer hiddenHead">
    <div class="headerBackground"></div>
    <div class="tableContainerInner">
        <table id="adapter-table" class="grid" data-bind="sortTable: true">
            <thead>
                <tr>
                    <th class="first">
                        <span class="th-inner">Name</span>
                    </th>
                    <th>
                        <span class="th-inner">DeviceID</span>
                    </th>
                    <th>
                        <span class="th-inner"></span>
                    </th>
                    <th>
                        <span class="th-inner"></span>
                    </th>
                </tr>
            </thead>
            <tbody data-bind="template: { name: 'AdaptersTemplate', foreach: Adapters }">
            </tbody>
        </table>
        <script id="AdaptersTemplate" type="text/html">
            <tr>
                <td data-bind="text: Name"></td>
                <td data-bind="text: DeviceID"></td>
                <td><a href="#" data-bind="click: $parent.selectItem">Edit</a>
                <td><a href="#" data-bind="click: $parent.deleteItem">Delete</a>
            </tr>
        </script>
    </div>
    <input type="button" data-bind='click: addAdapter' value="Add New Adapter" />
    <input type="button" data-bind='click: saveAll' value="Save Changes" id="SaveChangesButton" />
</div>

我的 javascript 已设置为将 VM 管理为 restful 并缓存更改。添加、编辑和保存/删除数据似乎都可以正常工作,而不会引发我在 Chrome 的调试器中看到的错误。确认更改似乎工作正常,并按预期对数据库进行更改。

$(function () {
    var viewModel = new AdaptersModel();
    getData(viewModel);
});

function getData(viewModel) {
    $.getJSON("/api/AdapterList",
    function (data) {
        if (data && data.length > 0) {
            viewModel.SetAdaptersFromJSON(data);
        }
        ko.applyBindings(viewModel);
    });
}

//#region AdapterVM
function Adapter(name, siFamily, deviceIDs) {
    var self = this;
    self.Name = ko.observable(name);
    self.DeviceID = ko.observable(deviceIDs);
    self.ID = 0;
}

function AdaptersModel() {
    var self = this;

    self.Adapters = ko.observableArray([]);
    self.DeleteAdapters = ko.observableArray([]);
    self.NewAdapter = ko.observable(new Adapter("", "", "", ""));

    self.Message = ko.observable("");

    self.SetAdaptersFromJSON = function (jsData) {
        self.Adapters = ko.mapping.fromJS(jsData);
    };

    //#region Edit List Options: confirmChanges
    self.confirmChanges = function () {
        if (self.NewAdapter().ID == 0) {
            self.Adapters.push(self.NewAdapter());
        }
    };
    //#endregion

    //#region Adapter List Options: addAdapter, selectItem, deleteItem, saveAll
    self.addAdapter = function () {
        self.NewAdapter(new Adapter("", "", "", ""));
    };

    self.selectItem = function (item) {
        self.NewAdapter(item);
    };

    self.deleteItem = function(item) {
        self.DeleteAdapters.push(item.ID());
        self.Adapters.remove(item);
    };

    self.saveAll = function () {
        if (self.Adapters && self.Adapters().length > 0) {
            var filtered = ko.utils.arrayFilter(self.Adapters(),
                function(adapter) {
                    return ((!isEmpty(adapter.Manufacturer())) &&
                        (!isEmpty(adapter.Name())) &&
                        (!isEmpty(adapter.DeviceIDs()))
                    );
                }
            );

            var updateSuccess = true;
            if (self.DeleteAdapters().length > 0) {
                jsonData = ko.toJSON(self.DeleteAdapters());
                $.ajax({
                    url: "/api/AdapterList",
                    cache: false,
                    type: "DELETE",
                    data: jsonData,
                    dataType: "json",
                    contentType: "application/json; charset=utf-8",
                    success: function () { updateSuccess = true; },
                    error: function () { updateSuccess = false; }
                });
            }

            var jsonData = ko.toJSON(filtered);
            $.ajax({
                url: "/api/AdapterList",
                type: "POST",
                data: jsonData,
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                success: function(data) {
                    self.SetAdaptersFromJSON(data);
                    updateSuccess = true && updateSuccess;
                },
                error: function () { updateSuccess = false; }
            });

            if (updateSuccess == true) { self.Message("Update Successfull"); } 
            else { self.Message("Update Failed"); }
        }
    };
    //#endregion
}
//#endregion

ko.bindingHandlers.message = {
    update: function(element, valueAccessor) {
        $(element).hide();
        ko.bindingHandlers.text.update(element, valueAccessor);
        $(element).fadeIn();
        $(element).fadeOut(4000);
    }
};

ko.bindingHandlers.sortTable = {
    init: function (element, valueAccessor) {
        setTimeout(function () {
            $(element).addClass('tablesorter');
            $(element).tablesorter({ widgets: ['zebra'] });
        }, 0);
    }
};

function isEmpty(obj) {
    if (typeof obj == 'undefined' || obj === null || obj === '') return true;
    if (typeof obj == 'number' && isNaN(obj)) return true;
    if (obj instanceof Date && isNaN(Number(obj))) return true;
    return false;
}

无法更新我的 html 表的特定脚本部分是:

self.deleteItem = function(item) {
    self.DeleteAdapters.push(item.ID());
    self.Adapters.remove(item);
};

除了删除之外,一切似乎都正常工作,所以我似乎不知道接下来要看什么,而且我对 javascript 或淘汰赛太陌生了,不知道这是否是一个线索:如果我运行 ko.applyBindings() 命令在 self.deleteItem 函数中,我得到了更新,但它确实给了我一个未处理的错误:

未捕获的错误:无法解析绑定。消息:ReferenceError:消息未定义;绑定值:消息:消息

消息是在绑定之前在 VM 中定义的……我在这一切中遗漏了什么吗?

4

2 回答 2

1

解决周围问题有很多帮助,但实际上没有解决问题的“原因”。更新有时效果很好,但有时效果不佳。当我对其进行故障排除并开始将其简化并在 JSFiddle 中工作时,我没有data-bind="sortTable: true"在所有工作版本中包含它。显然,如果您像我一样对表格进行排序或使用代码,它将无法正常工作。我看到的示例代码在这里http://jsfiddle.net/gregmason/UChLF/16/,相关代码:

ko.bindingHandlers.tableSorter = {
    init: function (element) {
        setTimeout(function () { $(element).tablesorter(); }, 0);
    },
    update: function (element, valueAccessor) {
        ko.utils.unwrapObservable(valueAccessor()); //just to get a dependency
        $(element).trigger("update");
    }
};

通过单击该行上的删除链接,错误的行为可以很明显。

  • 如果您单击行而不进行排序,您将看到该行正确消失。
  • 如果您首先单击一列以以不同的顺序重新排序,然后删除该行,它仍保留在表中并且似乎已缓存。

这可以通过绑定每个表头而不是表本身来处理,并用此线程中讨论的自定义行为 替换tableSorter代码: knockout js - Tablesorting using column headers。排序替换在这里:sort

ko.bindingHandlers.sort = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var asc = false;
        element.style.cursor = 'pointer';

        element.onclick = function(){
            var value = valueAccessor();
            var prop = value.prop;
            var data = value.arr;

            asc = !asc;
            if(asc){
                data.sort(function(left, right){
                    return left[prop]() == right[prop]() ? 0 : left[prop]() < right[prop]() ? -1 : 1;
                });
            } else {
                data.sort(function(left, right){
                    return left[prop]() == right[prop]() ? 0 : left[prop]() > right[prop]() ? -1 : 1;
                });
            }
        }
    }
};

这已经解决了我的排序/编辑/删除问题,并且工作的 jsFiddle 在这里:http: //jsfiddle.net/gregmason/UChLF/18/

于 2013-06-17T20:41:10.977 回答
1

在您的 Js 文件的开头,您正在定义 var viewModel = new AdaptersModel(); 但更低的是,您说函数 Adapter() 是您的区域声明中的视图模型。它使您的代码难以阅读。我将再次尝试解决您可以做些什么来进行故障排除,但我建议您的视图模型包含适配器,并且您的模型包含每个适配器应具有的类类实例。

您遇到的具体错误是因为您将 Message() 绑定到某些东西,然后删除 Message()。要解决这个问题,您可以做的一件事是将您的 div 更改为:

<div id="MessageDiv" data-bind="with: Message">
    <h5 data-bind="message: $data"><h5>  
</div>

如果您可以创建一个小提琴,我可以给出一个更明确的示例来说明原因,但基本上如果 Message() 为空白,则 with 绑定不应显示删除后未定义的标头。

不过,您可能需要做的是查看作为“项目”发送的内容,并确保它不是您的视图模型。

    self.deleteItem = function(item) {
        console.log(item);   // << Check console and see what is being returned
        self.DeleteAdapters.push(item.ID());
        self.Adapters.remove(item);
    };

您可能要删除的不仅仅是一个适配器。

这将为您指引正确的方向,但我会认真考虑重命名您的代码。

于 2013-06-05T03:18:41.650 回答