Javascript 如何使用淘汰赛迭代对象(不是数组)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14838135/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 18:05:34  来源:igfitidea点击:

How to use knockout to iterate over an object (not array)

javascriptknockout.js

提问by John Livermore

I want to use something similar to the Knockout foreach construct to iterate over the properties of an object. Here is what I am trying to create...

我想使用类似于 Knockout foreach 构造的东西来迭代对象的属性。这是我正在尝试创建的内容...

DESIRED RESULT

想要的结果

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

However, my model looks like this...

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

JS

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

Each row has an entityId and props which is an object itself. This template doesn't work, but how would I change it to generate the desired table above?

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

EDIT: The propsin this example are nameand lastLogin, but I need a solution that is agnostic to what is contained inside props.

编辑:props在这个例子中是nameand lastLogin,但我需要一个与里面包含的内容无关的解决方案props

I have this FIDDLEgoing as well.

我也有这个FIDDLE

HTML

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>

回答by o.v.

In a modern browser (or with an appropriate polyfill) you can iterate over Object.keys(obj)(the method returns only own enumerable properties, meaning that there is no need for an additional hasOwnPropertycheck):

在现代浏览器(或使用适当的 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>

Fiddled.

摆弄

NB:I was simply curious to see if this would work, the template body above is more polluted than what I'd like to use in production (or come back to a few months later and be like "wtf").

注意:我只是想知道这是否可行,上面的模板主体比我想在生产中使用的污染更严重(或者几个月后回到像“wtf”一样)。

Custom binding would be a better option, my personal preference though would be to use a computed observable or a writeable computed observable(the latter would be handy when working with jsonresponses a-la restful api).

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

回答by Jeff Mercado

You could always create a binding handler to handle the transformation.

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

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

Then apply it:

然后应用它:

<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>

回答by Jitters

This is a modification of Jeff's answer, with the binding context preserved

这是对 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 };
    }
};

Now apply with parent and root:

现在与父和根一起申请:

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

回答by Andrew

Simplified answer to work with any basic object, worked for me:

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

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

回答by Sufiyan Ansari

I am a bit late, But I think this should work, a simple solution without using any template.

我有点晚了,但我认为这应该可行,这是一个不使用任何模板的简单解决方案。

var json = [
 {
  "PortfolioCompanyId":240,
  "dt":"2018-12-31 00:00:00.0",
  "ValuationDate":"2017-09-30 00:00:00.0",
  "capitalexpenditure":-5555660.0,
  "workingcapitalchange":-812350.0
 },
 {
  "PortfolioCompanyId":240,
  "dt":"2019-12-31 00:00:00.0",
  "ValuationDate":"2017-09-30 00:00:00.0",
  "capitalexpenditure":-5613520.0,
  "workingcapitalchange":-893530.0
 },
 {
  "PortfolioCompanyId":240,
  "dt":"2020-12-31 00:00:00.0",
  "ValuationDate":"2017-09-30 00:00:00.0",
  "capitalexpenditure":-5674130.0,
  "workingcapitalchange":-982850.0
 },
 {
  "PortfolioCompanyId":240,
  "dt":"2021-12-31 00:00:00.0",
  "ValuationDate":"2017-09-30 00:00:00.0",
  "capitalexpenditure":-6241543.0,
  "workingcapitalchange":-1081135.0
 },
 {
  "PortfolioCompanyId":240,
  "dt":"2022-12-31 00:00:00.0",
  "ValuationDate":"2017-09-30 00:00:00.0",
  "capitalexpenditure":-6865697.3,
  "workingcapitalchange":-1189248.5
 }
];

var DataModel = function () {
            this.jsonArray = ko.observable(json);
        };
ko.applyBindings(new DataModel());
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table class="table" data-bind="foreach:jsonArray">
       <tr data-bind="foreach:Object.keys($data)"> <!-- JSON Object -->
         <td data-bind="text : $parent[$data]"></td>
       </tr>
    </table>
    
    

回答by Cody

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

Hope that's helpful (pardon the brevity)

希望这会有所帮助(请原谅篇幅太短)

appendix:

附录:

Here's a working example which has been testing...

这是一个正在测试的工作示例......

<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>

回答by Jakub Macek

Supposedly, there is a deeper problem (see this thread at Google groups) that is that foreach treats the object as a dictionary of parameters, not as the collection to iterate.

据说,还有一个更深层次的问题(请参阅 Google 群组中的此线程),即 foreach 将对象视为参数字典,而不是要迭代的集合。

My best solution so far is to combined foreachin Object.keys(myobject)and 'with' binding context.

我最好的解决办法,到目前为止是要结合foreachObject.keys(myobject)和“用”结合上下文。

回答by paul

(not strictly iterating over the properties, but doescreate the table above)

(不是严格迭代属性,但确实创建了上面的表格)

<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>

updated: http://jsfiddle.net/cwnEE/7/

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