Asp.net mvc 4 在ASP.NET MVC 4和KnockoutJS中使用主详细信息表单时视图不更新

Asp.net mvc 4 在ASP.NET MVC 4和KnockoutJS中使用主详细信息表单时视图不更新,asp.net-mvc-4,knockout.js,Asp.net Mvc 4,Knockout.js,我正在尝试使用ASP.NETMVC4和KnockoutJS为数据记录创建一个单页编辑器。它非常简单,有一个显示记录的表和一个编辑单个记录的表单 单击“编辑”编辑记录时,表单将更新,数据将毫无问题地保留到数据库中。这之后有两个问题: 保存后,正在编辑的记录不会在表中更新(即,观察值不会更新) 保存后,包含正在编辑的记录的控件不清除 我不知道如何解决(1)。对于(2),是否有某种方法可以编写一个泛型扩展方法或函数,以便在Knockout完成后清除任何表单。我可以很容易地用JQuery完成它,但我可能

我正在尝试使用ASP.NETMVC4和KnockoutJS为数据记录创建一个单页编辑器。它非常简单,有一个显示记录的表和一个编辑单个记录的表单

单击“编辑”编辑记录时,表单将更新,数据将毫无问题地保留到数据库中。这之后有两个问题:

  • 保存后,正在编辑的记录不会在表中更新(即,观察值不会更新)
  • 保存后,包含正在编辑的记录的控件不清除
  • 我不知道如何解决(1)。对于(2),是否有某种方法可以编写一个泛型扩展方法或函数,以便在Knockout完成后清除任何表单。我可以很容易地用JQuery完成它,但我可能错过了Knockout已经可以做的事情

    该页面的代码如下所示:

    @model IEnumerable<SiteDto>
    
    @{
        ViewBag.Title = "Index";
    }
    
    <h2>Sites</h2>
    
    
    <table>
        <caption>Sites</caption>
        <thead>
        <tr>
            <th>Name</th>
            <th>Link</th>
            <th>Url</th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: sites">
        <tr>
            <td><span data-bind="text: id"></span></td>
            <td><span data-bind="text: name"></span></td>
            <td><span data-bind="text: url"></span></td>
            <td><button data-bind="click: $parent.selectItem">Edit</button></td>
    
        </tr>
    </tbody>
    </table>
    
    <div data-bind="with: selectedItem">
    <table>
        <caption data-bind="text: name"></caption>
        <tbody>
            <tr>
                <td><input data-bind="value: id" /></td>
            </tr>
            <tr>
                <td><input data-bind="value: url" /></td>
            </tr>
            <tr>
                <td><input data-bind="value: name" /></td>
            </tr>
        </tbody>
    </table>
    
    
    <button data-bind="click: save">Save</button>
    
    </div>
    
    <script type="text/javascript">
    
    function viewModel() {
    
        var sites = ko.observableArray(@Html.Raw(Model.ToJson()));
        var selectedItem = ko.observable();
    
        selectItem = function (s) {
            selectedItem(s);
        };
    
        save = function () {
            alert(ko.toJSON(selectedItem));
            $.ajax({
                url: "/Home/Save",
                type: "POST",
                data: ko.toJSON(selectedItem),
                contentType: "application/json",
                dataType: "json",
                success: function(result) {
                    alert(result);
                },
                error: function() {
                    alert("fail");
                }
            });
    
        };
    
        return {
            sites: sites,
            selectedItem: selectedItem,
            selectItem: selectItem,
            save: save
        }
    }
    
    ko.applyBindings(viewModel);
    
    
    </script>
    
    @model IEnumerable
    @{
    ViewBag.Title=“Index”;
    }
    地点
    地点
    名称
    链接
    网址
    编辑
    拯救
    函数viewModel(){
    var sites=ko.observearray(@Html.Raw(Model.ToJson());
    var selectedItem=ko.observable();
    选择项=功能{
    选择编辑项;
    };
    save=函数(){
    警报(ko.toJSON(selectedItem));
    $.ajax({
    url:“/Home/Save”,
    类型:“POST”,
    数据:ko.toJSON(selectedItem),
    contentType:“应用程序/json”,
    数据类型:“json”,
    成功:功能(结果){
    警报(结果);
    },
    错误:函数(){
    警报(“失败”);
    }
    });
    };
    返回{
    地点:地点,,
    selectedItem:selectedItem,
    selectItem:selectItem,
    保存:保存
    }
    }
    应用绑定(视图模型);
    
    我会一次回答你的一个问题,因为它们并不真正相关

    1) 这里的问题是,您将ASP.NETMVC模型放在一个可观察的环境中。问题是,如果项目被添加、删除或交换,observableArray将更新UI,但它不会通知UI对单个项目的更改。因此,即使您确实正确地编辑了行,UI也永远不会知道。理想的解决方案不是简单地将MVC模型注入到observableArray中,而是将模型映射到一个数据结构,在该数据结构中,项的可编辑属性(id、url、名称)是可观察的。未经测试的演示代码:

    var rawSites = @Html.Raw(Model.ToJson()),
        sites = ko.observableArray(rawSites.map(function (rawSite) {
            return {
                id: ko.observable(rawSite.id),
                url: ko.observable(rawSite.url),
                name: ko.observable(rawSite.name)
            };
        }));
    
    编辑:我最初的回答建议了第二种方法,即通过从ObservalArray中删除编辑的项并重新添加来“黑客”UI更新@Tomalak在评论中提出了一个更好的建议:对项目使用
    valueHasMutated()
    。结果是一样的,但它的黑客性要小得多。请注意,在我看来,上述解决方案仍然更可取,因为它将执行得更好(需要的UI回流更少),并且当您稍后向该代码添加更多功能时,它会更加健壮

    2) 这取决于你想要什么。您希望编辑表单保持可见还是消失?您已经在使用带有:selectedItem绑定的
    ,这使得消失行为非常简单:只需从
    保存
    成功回调调用
    selectItem(null)
    。如果您希望表单始终可见,只需清除字段,我想以下方法可以奏效:

    function viewModel() {
    
        var sites = ko.observableArray(@Html.Raw(Model.ToJson()));
        var originalItem = null;
        var selectedItem = {
            id: ko.observable(),
            url: ko.observable(),
            name: ko.observable()
        };
        var selectItem = function (s) {
            // This function now copies the properties instead of using the item itself
            selectedItem.id(ko.unwrap(s.id));
            selectedItem.url(ko.unwrap(s.url));
            selectedItem.name(ko.unwrap(s.name));
            // Get a reference to s so we can update it when we are done editing
            originalItem = s;
        };
        var resetSelectedItem = function () {
            // Clear the form and reset the reference we held earlier
            selectItem({
                id: null,
                url: null,
                name: null
            });        
            originalItem = null;
        };
    
        save = function () {
            alert(ko.toJSON(selectedItem));
            $.ajax({
                url: "/Home/Save",
                type: "POST",
                data: ko.toJSON(selectedItem),
                contentType: "application/json",
                dataType: "json",
                success: function(result) {
                    alert(result);
                    // Done editing: update the item we were editing
                    originalItem.id(selectedItem.id());
                    originalItem.url(selectedItem.url());
                    originalItem.name(selectedItem.name());
                    // Clear the form
                    resetSelectedItem();
                },
                error: function() {
                    alert("fail");
                    // Clear the form
                    resetSelectedItem();
                }
            });
    
        };
    
        return {
            sites: sites,
            selectedItem: selectedItem,
            selectItem: selectItem,
            save: save
        }
    }
    

    我假设您在第一个代码示例中遗漏了一个
    ko.observable(
    。说到丑陋的黑客行为,您可以使用
    valueHasMutated()
    通知订阅者更改,但“不必要的回流”问题仍然存在。制作可观察的数组元素要优雅得多。可以使用单独的子视图模型和映射插件使处理复杂结构更加愉快。对!我编辑了
    ko.observable(
    返回。
    值发生了变化
    确实是一个选项,而且比我建议的更好。我会根据你的建议编辑我的答案。这仍然是一个黑客行为,但比我建议的要少。我个人不喜欢映射插件,所以我不能在这里真正演示它的用法,但它确实是一个受欢迎的插件可以在这里使用。如果你不喜欢“官方”的,还有其他的映射插件。但是,使用一个插件有它的优点,特别是因为你可以轻松地进行局部映射,只更新视图模型中实际更改的部分,从而减少屏幕更新。(更不用说通过数据映射的声明性方法编写更清晰的代码了。)