Knockout - 下拉列表中所选项目的只读模型绑定

时间:2012-03-19 12:12:26

标签: knockout.js

我在Knockout中遇到了ViewModel结构的问题。

更新

JS Fiddle

END UPDATE

概述

  • 我想从MVC操作返回一个模型并使用映射 knockout的扩展,用于从JSON创建observable到ViewModel。
  • 表格中的显示项目只有一个字段是可编辑的 字段是下拉列表,但它会导致项目的所有属性更新 值。
  • 将模型发布回MVC Action。

描述

我正在使用映射扩展来将从MVC操作返回的JSON模型映射到淘汰模型,我的模型看起来像这样:

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>();
    }
}

从Action返回的模型,可以包含默认数据 - 几个项目。

我正在使用JSON的自定义序列化设置,因此所有PascalCase属性名称都转换为camelCase。

绑定非常简单(包装某些控制器以管理几个屏幕,动态加载视图):

this.model = ko.mapping.fromJS(modelJson);
ko.applyBindings(this.model, this.view.content[0]);

现在在View中我有一张表:

<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操作。

更新

以防万一,因为我的所有淘汰视图模型都是由控制器管理的,所以有一个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();
        }
    });
}

结束更新

问题

我不确定如何实现我想要的而不需要编写大量自定义函数来设置数据 - 从动作返回的模型到视图模型,以及我们在视图上看到的模型将被回发到服务器。

任何帮助将不胜感激。

更新

JS Fiddle

END UPDATE

1 个答案:

答案 0 :(得分:2)

这是工作小提琴。

http://jsfiddle.net/madcapnmckay/wgRdj/

发现了一些事情。首先,您的初始viewmodel创建没有使用任何映射 配置所以这一行。

var viewModel = ko.mapping.fromJS(jsonModel);

创建匿名对象而不是Item类型的对象。我把它改成了。

var viewModel = ko.mapping.fromJS(jsonModel, { 
    items: {
       create: function (options) {
           return new Item(options.data);
       }
    } 
});

其次,您传入的json具有代码对象中每个数据的副本。为了获得选项绑定以正确选择值,您必须引用同一个确切的对象实例,因为即使具有相同的值,js也无法区分两个对象。有许多方法可以做到这一点,但为了保持现有的json结构,我只是简单地将绑定更改为仅使用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。

希望这有帮助。