如何将数据绑定到具有淘汰赛的对象列表?

时间:2015-04-24 01:28:24

标签: javascript asp.net-mvc knockout.js knockout-mapping-plugin

这是我的观点模型。

服务器端:

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

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

在客户端,我使用knockout.mapping

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

将服务器端模型绑定到客户端模型:

@{
    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>
}

上面的代码:

  1. 使用JSON.NET将服务器端Model序列化为驼峰式json。
  2. 构建客户端视图模型。
  3. ko.applyBinding()
  4. 现在我想利用双向绑定。

    首先,我在Name

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

    一切顺利,我可以修改文字Name上的input值,并将值保存到隐藏的input中。提交表单时,更新后的值可能会达到POST操作。

    现在的问题是:如何在列表上实现绑定?

    我的测试是从“项目”列表中删除一项:

    @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>
    

    console.log()告诉我客户端模型已更新,但这一次,HiddenFor上的绑定从未奏效!提交表单后,Items始终为空。

    我想这是合理的,因为在Html:

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

    我们期望输入的值是一个简单的值。

    我正在考虑循环遍历列表并从那里进行数据绑定。但这也很困难。淘汰赛foreach位于tbody代码上,而C#foreach位于tr附近(tbody内)。

    那么绑定列表的正确方法是什么?

    根据Fabio的建议,这是我的解决方案:

    1. 在客户端ko模型中,
    2. 添加计算值以将列表绑定到json字符串。

      vm.itemsJson = ko.computed(function() {
          return ko.toJSON(vm.items);
      },this);
      
      1. 在html视图中,
      2. 添加隐藏的输入以保存json字符串并使用我们的表单发布。

        <input name="itemsjson" type="hidden" data-bind="value:itemsJson"/>
        
        1. 在服务器端视图模型中,
        2. 除了

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

          添加另一个字符串属性来保存已发布的json。

          public string ItemsJson { get; set; }
          

          此时,我们可以看到ItemsJson值已成功发送到控制器操作。

          1. 将json字符串解析为服务器端的List。
          2. 由于它是类型模型,我们将使用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);
            

            请务必使用JArray.Parse()代替List

            有效。

            让我们看看是否有比手动解析json字符串更好的方法。否则,我会在本周末之后将Fabio的答案标记为我们的解决方案。

1 个答案:

答案 0 :(得分:1)

您可以使用计算的observable来绑定隐藏字段,如下所示:

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
}

了解更多信息http://knockoutjs.com/documentation/json-data.html

编辑1

您不需要在控制器内转换json。而是将json发送到您的服务器,使用隐藏字段列表发送集合。像这样:

<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>

然后您可以发送帖子,而不必担心收集,当您更改项目observableArray时它会自动刷新。