使用模板和函数从JSON中敲除Master / Detail对象

时间:2013-04-07 00:29:04

标签: jquery json knockout.js

我最近开始与Knockout合作,大多数事情都很容易。然而,这个让我有点难过,并希望有人可以提供一些帮助。我通过序列化具有Master / Detail结构的JSON对象来创建我的Knockout模型,我怀疑这会导致我的一些问题。我可以绑定/渲染记录的主要部分,我可以使用foreach模板绑定和呈现细节。但是,在我的场景中,我希望用户从子列表中选择要编辑的详细记录。问题是我无法让选择工作。我将点击事件触发,看来我的详细记录正在传递给我的事件 - 除了我无法读取属性的值。看着提琴手,看来正在通过的对象是事件的函数调用。在这一点上不太确定。所以,这是我的设置

我的JSON

{
"Height":"250",
"Width":"200",
"Speed":"700",
"Pause":"4",
"ShowItems":"1",
"Animation":"fade",
"MousePause":"true",
"IsPaused":"false",
"Direction":"up",
"DataItems":[{
     "OrderId":1,
     "ItemHtml":"This is item 1"
  },{
     "OrderId":2,
     "ItemHtml":"This is item 2"
  },{"
     OrderId":3,
    "ItemHtml":"This is item 3"
  }]
}

Javascript来序列化JSON对象

var viewModel = {
  vmRotator: ko.mapping.fromJSON(jsonObj)

  //-- THIS IS WHERE I THINK THE PROBLEM IS
  , selectRotatorItem: function (item) {
    //alert(item.OrderId); // THIS DOES NOT WORK
    console.log(item);
  }
}
ko.applyBindings(viewModel);

//-- hookup the click event for templated items
$("#rotatorItems").on("click", ".sel", function () {
  var data = ko.dataFor(this);
  viewModel.selectRotatorItem(data); //-- call to object function
});     

HTML显示项目,提供点击按钮

<table id="rotatorList" width="100%" border="0" cellspacing="0" cellpadding="2">
  <tbody id="rotatorItems" data-bind="template: { foreach: vmRotator.DataItems }">
  <tr>
    <td style="width: 20px;">
      <input type="button" class="sel" value="Select" />
      <span data-bind="text: OrderId"></span>
    </td>
    <td style="width: 90%;" data-bind="text: ItemHtml"></td>
  </tr>
  </tbody>
</table>

最好我能说这一切都正常。我正在调用我的事件但是当我在控制台中查看结果时,它看起来像是函数的文本而不是值。

在功能中:

, selectRotatorItem: function (item) {
  //alert(item.OrderId); // THIS DOES NOT WORK
  console.log(item);
}

alert(item.OrderId)显示:

"function d(){if(0<arguments.length){if(!d.equalityComparer||!d.equalityComparer(c,arguments[0]))d.H(),c=arguments[0],d.G();return this}b.r.Wa(d);return c}"

而不是像1或2这样的数字,它是订单ID的值。不知道为什么会这样,但我做错了。

我已经将这些代码与一些与我正在做的类似的示例混合在一起。我可以完全以错误的方式解决这个问题,但我认为我只是错过了一些简单的事情。我确信那里的一些Javascrtipt神很容易看到我不知道的东西,所以我很感激你的帮助。


在回答Rune对原始问题的回答时,这是最终版本

构建绑定的Javascript代码

//-- define models: master and detail modesl
var viewRotatorItemModel = {
  'Height': ko.observable(),
  'Width': ko.observable()
};

var viewRotatorModel = {
  'Height': ko.observable(),
  'Width': ko.observable(),
  'Speed': ko.observable(),
  'ShowItems': ko.observable(),
  'Animation': ko.observable(),
  'IsPaused': ko.observable(),
  'Direction': ko.observable(),
  'UseEditor': ko.observable(),
  'HtmlData': ko.observable(),
  'DataItems': {
        create: function (options) {
        return new viewRotatorDataItem(options.data);
      }
    }
};

//-- define objects around models
var viewRotatorDataItem = function (data) {
  var self = this;
  //--> IMPORTANT TO USE fromJS as JSON is already an object
  ko.mapping.fromJS(data, viewRotatorItemModel, self); 
  self.selectItem = function () {
    alert("item with id " + self.OrderId() + " clicked");
  };
};

var ViewModel = function (data) {
    var self = this;
    //--> IMPORTANT TO USE fromJSON as incoming object is JSON text
    ko.mapping.fromJSON(data, viewRotatorModel, self);
};

...
...

//-- create objects when ready
    vmRotator = new ViewModel(jsonObj);
    ko.applyBindings(vmRotator);    

用于显示主/明细项目的HTML

<h4>Main Items</h4>
<input type="text" id="wc_Height" data-bind="value: vmRotator.Height" />

<input type="text" id="wc_Width" data-bind="value: vmRotator.Width" />

<h3>Detail Items</h3>
<table id="rotatorList" width="100%" border="0" cellspacing="0" cellpadding="2">
  <tbody id="rotatorItems" data-bind="template: { foreach: vmRotator.DataItems }">
  <tr>
    <td style="width: 20px;"> 
      <input type="button" value="Select" data-bind="click: selectItem" />
      <span data-bind="text: OrderId"></span>
    </td>
    <td style="width: 90%;" data-bind="text: ItemHtml"></td>
  </tr>
</tbody>
</table>

2 个答案:

答案 0 :(得分:0)

我建议您使用knockout映射为您可以从标记调用的数据项添加自定义功能。 (我为你创建了一个有效的JSBin

您的初始数据保持不变,您的javascript变为:

var DataItem = function(data) {
    var self = this;
    ko.mapping.fromJS(data, {}, self);
    // add custom functionality to each data item here
    // here an alert is added that can be called via 'selectItem' in markup
    self.selectItem = function(){
      alert("item with id " + self.OrderId() + " clicked");
    }; 
};

var viewModelMapping = {
    'DataItems': {
     create: function(options) {
        return new DataItem(options.data);
        }
    }
};

var ViewModel = function(data) {
    var self = this;
    ko.mapping.fromJS(data, viewModelMapping, self);
};

var vm = new ViewModel(initialData);
$(document).ready(function () {
    ko.applyBindings(vm);   
}); 

并且您的HTML表格变为(为了清晰起见而删除了样式):

<table>
  <tbody data-bind="foreach: DataItems">
  <tr>
      <td">
        <input type="button" value="Select" data-bind="click: selectItem" />
        <span data-bind="text: OrderId"></span>
      </td>
      <td data-bind="text: ItemHtml"></td>
    </tr>
  </tbody>
</table>

您可以在使用“创建”自定义对象构建和使用“更新”部分here自定义对象更新中阅读有关自定义映射对象的更多信息

答案 1 :(得分:0)

我认为问题是你应该删除#rotatorItems的jQuery绑定,并在.sel按钮上使用'click'绑定:

请看这个小提琴:

http://jsfiddle.net/EKXKF/2/

主要变化:

<tbody id="rotatorItems" data-bind="foreach: vmRotator.DataItems">
  <tr>
    <td style="width: 20px;">
      <input type="button" class="sel" value="Select" data-bind="click: $root.selectRotatorItem" />
      <span data-bind="text: OrderId"></span>
    </td>
    <td style="width: 90%;" data-bind="text: ItemHtml"></td>
  </tr>
</tbody>