2013-02-12 70 views
39

我想使用类似Knockout的foreach构造来迭代对象的属性。这是我想创建...如何使用敲除来遍历对象(而不是数组)

期望的结果

<table> 
    <tr> 
     <td>Name 1</td> 
     <td>8/5/2012</td> 
    </tr> 
    <tr> 
     <td>Name 2</td> 
     <td>2/8/2013</td> 
    </tr> 
</table> 

然而,我的模型看起来像这样...

JS

function DataModel(){ 
    this.data = ko.observableArray([{ 
         entityId: 1, 
         props: { 
          name: 'Name 1', 
          lastLogin: '8/5/2012' 
         } 
        }, 
        { 
         entityId: 2, 
         props: { 
          name: 'Name 2', 
          lastLogin: '2/8/2013' 
         } 
        }]); 
} 

var dataModel = new DataModel(); 
ko.applyBindings(dataModel); 

每行都有一个entityId和道具,它们本身就是一个对象。这个模板不起作用,但我如何改变它来生成上面所需的表格?

编辑:在这个例子中propsnamelastLogin,但我需要一个解决方案,是不可知的什么是包含内部props

我也有这个FIDDLE去。

HTML

<div data-bind="template: { name: 'template', data: $data }"></div> 

<script type="text/html" id="template"> 
    <table> 
     <tr data-bind="foreach: data()"> 
      <td data-bind="text: entityId"></td> 
     </tr> 
    </table> 
</script> 
+0

是道具的数量总是2或者他们可以彼此之间有什么不同? – 2013-02-14 11:20:09

回答

45

你总是可以创建一个绑定处理程序来处理转换。

ko.bindingHandlers.foreachprop = { 
    transformObject: function (obj) { 
    var properties = []; 
    ko.utils.objectForEach(obj, function (key, value) { 
     properties.push({ key: key, value: value }); 
    }); 
    return properties; 
    }, 
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
    var properties = ko.pureComputed(function() { 
     var obj = ko.utils.unwrapObservable(valueAccessor()); 
     return ko.bindingHandlers.foreachprop.transformObject(obj); 
    }); 
    ko.applyBindingsToNode(element, { foreach: properties }, bindingContext); 
    return { controlsDescendantBindings: true }; 
    } 
}; 

然后应用它:

<div data-bind="template: { name: 'template', data: $data }"></div> 

<script type="text/html" id="template"> 
    <table> 
     <tbody data-bind="foreach: data"> 
      <tr data-bind="foreachprop: props"> 
       <td data-bind="text: value"></td> 
      </tr> 
     </tbody> 
    </table> 
</script> 
+0

作为一个对象返回值,为什么它不能只是一个像键一样的字符串? – 2013-05-21 15:25:15

+0

只是想知道这个foreachprop绑定是否可以使用'$ parent.'绑定上下文? – Thewads 2014-01-09 08:56:20

+0

@Thewads:当然。您可以将它用于具有属性的任何普通对象。虽然我不确定我明白你指的是什么。 – 2014-01-23 02:10:55

0

(在性能没有严格的迭代,但创建上表)

<div data-bind="template: { name: 'template', data: $data }"></div> 

<script type="text/html" id="template"> 
    <table data-bind="foreach: data()"> 
     <tr> 
      <td data-bind="text: props.name"></td> 
      <td data-bind="text: props.lastLogin"></td> 
     </tr> 
    </table> 
</script> 

更新:http://jsfiddle.net/cwnEE/7/

+2

确实如此,但我需要它迭代属性,因为'道具'中的名称对于不同的表格会有所不同。我编辑了这个问题来澄清这一点。 – 2013-02-12 17:31:59

53

在现代的浏览器(或用适当的填充工具),你可以在Object.keys(obj)迭代(该方法只返回自己的枚举的属性,这意味着不存在需要额外hasOwnProperty检查):

<table> 
    <tbody data-bind="foreach: {data: data, as: '_data'}"> 
    <tr data-bind="foreach: {data: Object.keys(props), as: '_propkey'}"> 
     <th data-bind="text: _propkey"></th> 
     <td data-bind="text: _data.props[_propkey]"></td> 
    </tr> 
    </tbody> 
</table> 

Fiddled

注:我只是好奇,想看看这是否会工作,模板身体上面比想什么,我在生产中使用(或回来几个月后,成为像“跆拳道污染更严重“)。

自定义绑定将是一个更好的选择,我个人的偏好虽然会使用计算的observable或writeable computed observable(当使用json响应a-la restful api时,后者会很方便)。

+0

这个答案是撞。刚才碰到了这个,我想我会检查一下,因此不得不说。用于特定用途的自定义绑定不像生产大型网站那样有效,因为这是一种简单的内联解决方案。 – MattSizzle 2014-11-23 04:57:35

+0

我希望我可以多次调用这个函数。我喜欢这个,因为我不必创建绑定处理程序。 – valdetero 2015-01-02 19:41:49

+0

这是最好的解决方案!我不明白为什么它不是第一个。 – jbartolome 2015-01-29 21:49:18

2
<table> 
    <tr data-bind="foreach: {data: data, as: 'item'}"> 
     <td data-bind="foreach: { data: Object.keys(item), as: 'key' }"> 
      <b data-bind="text: item[key]"></b> 
     </td> 
    </tr> 
</table> 

function DataModel(){ 
this.data = ko.observableArray([{ 
        entityId: 1, 
        props: { 
         name: 'Name 1', 
         lastLogin: '8/5/2012' 
        } 
       }, 
       { 
        entityId: 2, 
        props: { 
         name: 'Name 2', 
         lastLogin: '2/8/2013' 
        } 
       }]); 
} 

var dataModel = new DataModel(); 
ko.applyBindings(dataModel); 

希望这是有帮助的(原谅简洁)

附录:

这里的工作的例子已经测试...

<table class="table table-hover"> 
    <thead> 
     <tr> 
      <!-- ko foreach: gridOptions.columnDefs --> 
      <th data-bind="text: displayName"></th> 
      <!-- /ko --> 
     </tr> 
    </thead> 
    <tbody> 
     <!-- ko foreach: {data: gridOptions.data, as: 'item'} --> 
     <tr> 
      <!-- ko foreach: {data: Object.keys(item), as: 'key'} --> 
      <td> 
       <span data-bind="text: item[key]"></span> 
      </td> 
      <!-- /ko --> 
     </tr> 
     <!-- /ko --> 
    </tbody> 
</table> 
20

这是一个修改Jeff的答案,保留了约束关系

ko.bindingHandlers.eachProp = { 
    transformObject: function (obj) { 
     var properties = []; 
     for (var key in obj) { 
      if (obj.hasOwnProperty(key)) { 
       properties.push({ key: key, value: obj[key] }); 
      } 
     } 
     return ko.observableArray(properties); 
    }, 
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
     var value = ko.utils.unwrapObservable(valueAccessor()), 
      properties = ko.bindingHandlers.eachProp.transformObject(value); 

     ko.bindingHandlers['foreach'].init(element, properties, allBindingsAccessor, viewModel, bindingContext) 
     return { controlsDescendantBindings: true }; 
    }, 
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
     var value = ko.utils.unwrapObservable(valueAccessor()), 
      properties = ko.bindingHandlers.eachProp.transformObject(value); 

     ko.bindingHandlers['foreach'].update(element, properties, allBindingsAccessor, viewModel, bindingContext) 
     return { controlsDescendantBindings: true }; 
    } 
}; 

现在与父母和根适用:

<table> 
    <tbody data-bind="foreach: data"> 
     <tr data-bind="eachProp: props"> 
      <td data-bind="text: value, click: $root.doSomething"></td> 
     </tr> 
    </tbody> 
</table> 
+2

这个答案比接受的IMO更好,因为它包含更新函数..这意味着它不仅仅是一次迭代就会表现得像一个可观察的事物。 – 2014-09-04 17:31:48

+0

@SteveCadwallader:它不会更好,有一个明确的'update'函数,它直接调用'foreach'处理程序的更新函数。换句话说,它是手动传播更新调用。另一个答案将foreach处理程序应用于元素,以使foreach所做的一切都会发生(包括更新)。显式更新函数是不需要的。 – 2017-04-21 00:59:21

2

据说,还有一个更深层次的问题(see this thread at Google groups)即是的foreach把对象作为参数的字典,而不是作为集合进行迭代。

我到目前为止的最佳解决方案是将foreach合并到Object.keys(myobject)和'with'绑定上下文中。

0

简化的答案与任何基本对象的工作,为我工作:

<!-- ko foreach: {data: Object.keys(myObj)} --> 
    <span data-bind="text: $data"></span> 
    <span data-bind="text: $parent.myObj[$data]"></span> 
<!-- /ko -->