如何将数据绑定到带有敲除的对象列表?
How to data bind to a list of objects with knockout?
这是我的视图模型。
服务器端:
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>
}
上面的代码:
- 使用
JSON.NET
将服务器端Model
序列化为驼峰json。
- 构建客户端视图模型。
- ko.applyBinding()
现在我想利用双向绑定。
首先我在 Name
:
上测试
@Html.HiddenFor(model => model.Name, new { @data_bind="value:name"})
<input type="text" data-bind="value:name"/>
一切顺利,我能够编辑文本 input
上的 Name
值并将该值保存到隐藏的 input
中。提交表单时,更新的值可能会达到 POST 操作。
现在的问题是:如何实现对列表的绑定?
我的测试是从 'Items' 列表中删除一项:
@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
始终为 null。
我想这是合理的,因为在 Html:
<input type="hidden" value="xxx" />
我们希望输入的值是一个简单的值。
我正在考虑循环遍历列表并从那里进行数据绑定。但这也很困难。敲除 foreach
在 tbody
标签上,而 C# foreach
放在 tr
周围(在 tbody
内)。
那么绑定列表的正确方法是什么?
根据法比奥的建议,我的解决方案如下:
- 在客户端ko模型中,
添加计算值以将列表绑定到 json 字符串。
vm.itemsJson = ko.computed(function() {
return ko.toJSON(vm.items);
},this);
- 在 html 视图中,
添加一个隐藏的输入来保存 json 字符串和 post 我们的表单。
<input name="itemsjson" type="hidden" data-bind="value:itemsJson"/>
- 在服务器端视图模型中,
此外
public List<ItemModel> Items{ get; set; }
添加另一个字符串 属性 来保存 posted json.
public string ItemsJson { get; set; }
此时,我们可以看到 ItemsJson
值已成功发送到控制器操作。
- 将json字符串解析为服务器端的List。
因为是类型化模型,我们要用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
而不是 JObject.Parse()
。
有效。
让我们看看有没有比手动解析 json 字符串更好的方法。否则,我会将 Fabio 的回答标记为本周末后的解决方案。
您可以使用计算的可观测值来绑定隐藏字段,如下所示:
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>
然后你就可以发送post了,不用担心集合,当你改变observableArray的项目时它会自动刷新。
这是我的视图模型。
服务器端:
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>
}
上面的代码:
- 使用
JSON.NET
将服务器端Model
序列化为驼峰json。 - 构建客户端视图模型。
- ko.applyBinding()
现在我想利用双向绑定。
首先我在 Name
:
@Html.HiddenFor(model => model.Name, new { @data_bind="value:name"})
<input type="text" data-bind="value:name"/>
一切顺利,我能够编辑文本 input
上的 Name
值并将该值保存到隐藏的 input
中。提交表单时,更新的值可能会达到 POST 操作。
现在的问题是:如何实现对列表的绑定?
我的测试是从 'Items' 列表中删除一项:
@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
始终为 null。
我想这是合理的,因为在 Html:
<input type="hidden" value="xxx" />
我们希望输入的值是一个简单的值。
我正在考虑循环遍历列表并从那里进行数据绑定。但这也很困难。敲除 foreach
在 tbody
标签上,而 C# foreach
放在 tr
周围(在 tbody
内)。
那么绑定列表的正确方法是什么?
根据法比奥的建议,我的解决方案如下:
- 在客户端ko模型中,
添加计算值以将列表绑定到 json 字符串。
vm.itemsJson = ko.computed(function() {
return ko.toJSON(vm.items);
},this);
- 在 html 视图中,
添加一个隐藏的输入来保存 json 字符串和 post 我们的表单。
<input name="itemsjson" type="hidden" data-bind="value:itemsJson"/>
- 在服务器端视图模型中,
此外
public List<ItemModel> Items{ get; set; }
添加另一个字符串 属性 来保存 posted json.
public string ItemsJson { get; set; }
此时,我们可以看到 ItemsJson
值已成功发送到控制器操作。
- 将json字符串解析为服务器端的List。
因为是类型化模型,我们要用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
而不是 JObject.Parse()
。
有效。
让我们看看有没有比手动解析 json 字符串更好的方法。否则,我会将 Fabio 的回答标记为本周末后的解决方案。
您可以使用计算的可观测值来绑定隐藏字段,如下所示:
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>
然后你就可以发送post了,不用担心集合,当你改变observableArray的项目时它会自动刷新。