我在 Knockout 中遇到 ViewModel 结构问题。
更新
结束更新
概述
- 我想从 MVC 操作返回模型并使用映射 用于创建从 JSON 到 ViewModel 的可观察对象的 knockout 扩展。
- 表中的显示项目只有一个字段可编辑,而此 字段是下拉列表,但它导致项目的所有属性更新 值(value)观。
- 将模型发布回 MVC Action。
描述
我正在使用映射扩展将从 MVC 操作返回的 JSON 模型映射到 knockout 模型,我的模型如下所示:
public class SomeClassInput
{
public string Help { get; set; }
public IEnumerable<SomeClassItem> Items { get; set; }
public class SomeClassItem
{
public string Code { get; set; }
public string Location { get; set; }
public string Easting { get; set; }
public string Northing { get; set; }
public string WaterName { get; set; }
public string WfdCode { get; set; }
}
public SomeClassInput()
{
Items = new List<SomeClassItem>();
}
}
从操作返回的模型,可以包含默认数据 - 少数项目。
我正在使用 JSON 的自定义序列化设置,因此所有 PascalCase 属性名称都会转换为驼峰命名法。
绑定(bind)非常简单(包装某种 Controller 来管理少数屏幕,以及动态加载 View ):
this.model = ko.mapping.fromJS(modelJson);
ko.applyBindings(this.model, this.view.content[0]);
现在在 View 中我有一张 table :
<table class="table table-striped table-bordered" id="some-table"
data-swo-codes="@Url.Action("Codes", "Controller")"
>
<thead>
<tr>
<th>
[Actions]
</th>
<th>
Code
</th>
<th>
Location
</th>
<th>
Name of Water
</th>
<th>
WFD Code
</th>
</tr>
</thead>
<tbody data-bind="template: {name: 'rowTemplate', foreach: items}">
</tbody>
<tfoot>
<tr>
<td colspan="6">
<button class="btn" data-bind="click: table.addRow">Add new Storm Water Overflow</button>
</td>
</tr>
</tfoot>
</table>
<script type="text/html" id="rowTemplate">
<tr>
<td>
<button class="btn" data-bind="click: $parent.table.removeRow "><i class="icon-remove"></i></button>
<button class="btn" data-bind="click: $parent.table.moveUp, disable: ko.computed(function() { return $parent.table.moveUpEnabled.call($parent, $data); }, $parent) "><i class="icon-arrow-up"></i></button>
<button class="btn" data-bind="click: $parent.table.moveDown, disable: ko.computed(function() { return $parent.table.moveDownEnabled.call($parent, $data); }, $parent) "><i class="icon-arrow-down"></i></button>
</td>
<td>
<select data-bind="options: $parent.codes, optionsText: 'name', value: code, optionsCaption: 'Select Code...'"></select>
</td>
<td>
<label data-bind="visible: code, text: location" />
</td>
<td>
<label>E</label><label data-bind="visible: code, text: easting" />
<label>N</label><label data-bind="visible: code, text: northing" />
</td>
<td>
<label data-bind="visible: code, text: waterName" />
</td>
<td>
<label data-bind="visible: code, text: wfdCode" />
</td>
</tr>
</script>
这不是工作示例 - 所以标签可以是跨度或任何需要的东西。
$parent.table
| table
- 围绕表格包装所有操作,例如添加行、删除、上下移动。这有效。
只有一个单元格是可编辑的——代码。代码将是一个下拉列表,由后台的一些 ajax 代码初始化,返回所有可能的代码和连接到项目的元数据 - 此调用在模型 ko.applyBindings
之前执行。 ajax 调用的结果看起来几乎像模型:
[ {
name: 'some_name_of_code',
code: 'GUID',
location: 'some_place',
easting: '435',
northing: '345',
waterName: 'some_name',
wfdCode: 'some_code'
}, {
//..
}]
每当用户从代码下拉列表中选择一个项目时,所有属性都应该显示并映射到模型。 this.model.items[0].easting
将并且应该返回一个值。
因此,当用户单击“保存”时,我可以解开模型并将其作为 JSON 发布到 MVC 操作。
更新
以防万一,因为我所有的 knockout View 模型都是由 Controller 管理的,所以有一个std。将模型发布回服务器的方式,负责它的片段:
save: function () {
var that = this,
model = ko.mapping.toJS(this.model);
delete model.help;
logger.log(this.id + ' SAVE event executing');
return $.ajax({
type: 'POST',
url: this.saveUri,
data: JSON.stringify(model),
contentType: 'application/json; charset=utf-8',
success: function() {
logger.log(that.id + ' SAVE event executed and finished with success, cleaning up dirty flag');
that.model.tracker().markCurrentStateAsClean();
}
});
}
结束更新
问题
我不确定如何在不编写大量自定义函数来设置数据的情况下实现我想要的目标 - 从操作返回的模型到 View 模型,以及从 View 上的内容到将要发回的模型到服务器。
任何帮助将不胜感激。
更新
结束更新
最佳答案
这是工作 fiddle 。
http://jsfiddle.net/madcapnmckay/wgRdj/
发现了一些东西。首先,您最初的 View 模型创建没有使用任何映射 配置所以这一行。
var viewModel = ko.mapping.fromJS(jsonModel);
创建了匿名对象而不是 Item 类型的对象。我将其更改为。
var viewModel = ko.mapping.fromJS(jsonModel, {
items: {
create: function (options) {
return new Item(options.data);
}
}
});
其次,您传入的 json 包含代码对象中每条数据的副本。为了让选项绑定(bind)正确选择值,您必须引用相同的确切对象实例,因为即使值相同,js 也无法区分两个对象。有多种方法可以完成此操作,但为了保留现有的 json 结构,我只是将绑定(bind)更改为仅引用 codeid 而不是整个对象,并使其他项目值从中派生。
function Item(config) {
var self = this;
this.code = ko.observable(config.code);
var value = ko.computed(function() {
if (this.code()) {
// find the code in the list
return ko.utils.arrayFirst(viewModel.codes(),
function (c) {
return c.code == self.code();
});
}
return null;
}, this);
var getValue = function(name) {
if (value()) {
return value()[name];
}
return "";
}
this.location = ko.computed(function() {
return getValue("location");
}, this);
this.easting = ko.computed(function() {
return getValue("easting");
}, this);
this.northing = ko.computed(function() {
return getValue("northing");
}, this);
this.waterName = ko.computed(function() {
return getValue("waterName");
}, this);
this.wfdCode = ko.computed(function() {
return getValue("wfdCode");
}, this);
}
这样做的优点是最初只需要在 json 中传递 id。
希望这有帮助。
关于knockout.js - Knockout - 下拉列表中所选项目的只读模型绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9769955/