简体   繁体   English

如何使用剔除将数据绑定到对象列表?

[英]How to data bind to a list of objects with knockout?

Here is my view model. 这是我的视图模型。

Server side: 服务器端:

public class ShoppingListModel
{
    public string Name { get; set; }
    public List<ItemModel> Items{ get; set; }

    public ShoppingListModel()
    {
        Items=new List<ItemModel>();
    }
}

On the client side, I use knockout.mapping . 在客户端,我使用knockout.mapping

ShoppingListModel = function(data) {
    var vm = ko.mapping.fromJSON(data);
    return vm;
};

To bind the server-side model to the client-side model: 要将服务器端模型绑定到客户端模型:

@{
    var jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
    var data = new JavaScriptSerializer().Serialize(JsonConvert.SerializeObject(Model, jsonSerializerSettings));
}

@section scripts {
<script src="~/App/ShoppingListModel.js"></script>

    <script>    
        var vm = ShoppingListModel(@Html.Raw(data));
        ko.applyBindings(vm);
    </script>
}

The code above: 上面的代码:

  1. use JSON.NET to serialize the server-side Model into a camel cased json. 使用JSON.NET将服务器端Model序列化为驼峰式json。
  2. build the client-side view model. 建立客户端视图模型。
  3. ko.applyBinding() ko.applyBinding()

Now I want to take advantage of the two-way binding. 现在,我想利用双向绑定。

First I tested on the Name : 首先,我测试了Name

@Html.HiddenFor(model => model.Name, new { @data_bind="value:name"})
<input type="text" data-bind="value:name"/>

It went well, I was able to edit the Name value on a text input and persist the value into the hidden input . 一切顺利,我能够在文本input编辑Name值,并将该值保留到隐藏的input The updated value could reach the POST action when the form is submitted. 提交表单后,更新后的值可能会达到POST操作。

Now the question: how to implement the binding on the list? 现在的问题是:如何在列表上实现绑定?

My test is to remove one item from the 'Items' list: 我的测试是从“项目”列表中删除一项:

@Html.HiddenFor(model => model.Items, new { data_bind = "value: items" })

<tbody data-bind="foreach:items">
    <tr>
        <td>

            <span data-bind="text:name"></span>
        </td>
        <td><span data-bind="text:count"></span></td>
        <td>
            <button class="btn btn-xs" data-bind="click:$parent.remove">
                <i class="fa fa-trash"></i>
            </button>
        </td>
    </tr>
</tbody>

A console.log() tells me that the client-side model has been updated, but this time, the binding on the HiddenFor has never worked! console.log()告诉我客户端模型已经更新,但是这次, HiddenFor上的HiddenFor从未起作用! When the form is submitted, Items is always null. 提交表单后, Items始终为null。

I guess it is reasonable because in Html: 我认为这是合理的,因为在HTML中:

<input type="hidden" value="xxx" />

we are expecting the value of an input to be a simple value. 我们期望输入的值是一个简单的值。

I was thinking about loop through the list and data-bind from there. 我正在考虑遍历列表并从那里进行数据绑定。 But it is also difficult. 但这也是困难的。 The knockout foreach is on tbody tag while a C# foreach is placed around tr (inside tbody ). 敲除foreachtbody标签上,而C# foreach放在tr周围( tbody内部)。

Then what is the correct way to bind the list? 那么绑定列表的正确方法是什么?


Here is my solution based on Fabio's suggestion: 这是根据Fabio的建议我的解决方案:

  1. In the client-side ko model, 在客户端ko模型中,

add a computed value to bind the list into a json string. 添加计算值以将列表绑定到json字符串中。

vm.itemsJson = ko.computed(function() {
    return ko.toJSON(vm.items);
},this);
  1. In the html view, 在html视图中,

add a hidden input to hold the json string and post with our form. 添加一个隐藏的输入来保存json字符串并以我们的表单发布。

<input name="itemsjson" type="hidden" data-bind="value:itemsJson"/>
  1. In the server side view model, 在服务器侧视图模型中,

besides 除了

public List<ItemModel> Items{ get; set; }

Add another string property to hold the posted json. 添加另一个字符串属性以保存发布的json。

public string ItemsJson { get; set; }

At this point, we are able to see the ItemsJson value successfully sent to the controller action. 至此,我们可以看到ItemsJson值已成功发送到控制器操作。

  1. Parse the json string to the List on the server side. 将json字符串解析到服务器端的列表。

Since it is typed model, we are to use JSON.Net to deserialize. 由于它是类型化模型,因此我们将使用JSON.Net进行反序列化。

var items=JArray.Parse(model.ItemsJson);

model.Items = items.
    Select(i => new ItemModel {Name = (string) i["name"], Count = (int) i["count"]})
    .ToList();
return View(model);

Be sure to use JArray.Parse() for a List instead of JObject.Parse() . 确保将JArray.Parse()用于List而不是JObject.Parse()

It works. 有用。

Let's see if there is any better way than manually parsing json string. 让我们看看是否有比手动解析json字符串更好的方法。 Otherwise, I would mark Fabio's answer as our solution after this weekend. 否则,我将在本周末之后将法比奥的答案标记为我们的解决方案。

You could use computed observables to bind the hidden field, like this: 您可以使用计算的可观察值绑定隐藏字段,如下所示:

function YourViewModel() {
     var self = this;
     self.items = ko.observableArray(); //fill it with your stuff;
     self.valueForHiddenField = ko.computed(function() {
         return ko.toJSON(self.items);
     }, this); //use this observable as value of your hidden field
}

For more information http://knockoutjs.com/documentation/json-data.html 有关更多信息,请访问http://knockoutjs.com/documentation/json-data.html

EDIT 1 编辑1

You don't need to convert the json inside your controller. 您无需在控制器内部转换json。 Instead sending a json to your server, send the collection using list of hidden fields. 而不是将json发送到您的服务器,而是使用隐藏字段列表发送集合。 Like this: 像这样:

<form>
   <!-- ko foreach: items -->
       <input type="hidden" data-bind="value: property1, attr: { name: 'Items[' + $index() + '].Property1' }">
        <input type="hidden" data-bind="value: property2, attr: { name: 'Items[' + $index() + '].Property2' }">
   <!-- /ko -->

</form>

Then you can send the post, and don't need to worry about the collection, it will refresh automatically when you changed the items observableArray. 然后,您可以发送帖子,而不必担心集合,当您更改项目observableArray时,它将自动刷新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM