如何使用knockout迭代一个对象(不是数组)

Joh*_*ore 43 javascript knockout.js

我想使用类似于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>
Run Code Online (Sandbox Code Playgroud)

但是,我的模型看起来像这样......

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);
Run Code Online (Sandbox Code Playgroud)

每行都有一个entityId和props,它们本身就是一个对象.此模板不起作用,但我如何更改它以生成上面所需的表?

编辑:props在这个例子中是namelastLogin,但我需要一个与内部包含的内容无关的解决方案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>
Run Code Online (Sandbox Code Playgroud)

o.v*_*.v. 62

在现代浏览器中(或使用适当的polyfill),您可以迭代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>
Run Code Online (Sandbox Code Playgroud)

摆弄.

注意:我只是很好奇,看看这是否有效,上面的模板体比我想要在生产中使用的更污染(或者回到几个月之后,就像"wtf").

自定义绑定是一个更好的选择,我个人的偏好虽然是使用计算的observable或可写的计算observable(后者在处理json响应a-la restful api 时会很方便).


Jef*_*ado 46

您始终可以创建一个绑定处理程序来处理转换.

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 };
  }
};
Run Code Online (Sandbox Code Playgroud)

然后申请:

<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>
Run Code Online (Sandbox Code Playgroud)


小智 21

这是对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 };
    }
};
Run Code Online (Sandbox Code Playgroud)

现在申请父和根:

<table>
    <tbody data-bind="foreach: data">
        <tr data-bind="eachProp: props">
            <td data-bind="text: value, click: $root.doSomething"></td>
        </tr>
    </tbody>
</table> 
Run Code Online (Sandbox Code Playgroud)

  • 这个答案比接受的IMO更好,因为它包含更新函数.这意味着它的行为就像一个可观察的而不仅仅是一次迭代. (2认同)

And*_*rew 5

使用任何基本对象的简化答案,对我有用:

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