web-dev-qa-db-fra.com

Comment utiliser knockout pour parcourir un objet (pas un tableau)

Je souhaite utiliser quelque chose de similaire à la construction Knockout foreach pour parcourir les propriétés d'un objet. Voici ce que j'essaie de créer ...

RÉSULTAT DÉSIRÉ

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

Cependant, mon modèle ressemble à ceci ...

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

Chaque ligne a un entityId et des accessoires qui est un objet lui-même. Ce modèle ne fonctionne pas, mais comment puis-je le modifier pour générer le tableau souhaité ci-dessus?

EDIT: La props dans cet exemple est name et lastLogin, mais j'ai besoin d'une solution qui ne dépend pas de ce qui est contenu dans props.

J'ai ce VIOLON va aussi.

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>
41
John Livermore

Vous pouvez toujours créer un gestionnaire de liaisons pour gérer la 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 };
  }
};

Puis appliquez-le:

<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>
46
Jeff Mercado

Dans un navigateur moderne (ou avec un polyfill approprié), vous pouvez effectuer une itération sur Object.keys(obj) (la méthode ne renvoie que propres propriétés énumérables, ce qui signifie qu'aucun contrôle hasOwnProperty supplémentaire n'est nécessaire):

<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: J'étais simplement curieux de voir si cela fonctionnerait, le corps du gabarit ci-dessus est plus pollué que ce que j'aimerais utiliser en production (ou revenir quelques mois plus tard et ressembler à "wtf") .

La liaison personnalisée serait une meilleure option; ma préférence personnelle consisterait toutefois à utiliser un observable calculé ou un { observable calculé en écriture (ce dernier serait pratique lorsque vous travaillerez avec des réponses json avec une api reposante).

61
o.v.

Ceci est une modification de la réponse de Jeff, avec le contexte de liaison préservé

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

Maintenant, appliquez avec parent et root:

<table>
    <tbody data-bind="foreach: data">
        <tr data-bind="eachProp: props">
            <td data-bind="text: value, click: $root.doSomething"></td>
        </tr>
    </tbody>
</table> 
21
Jitters
<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);

J'espère que c'est utile (excusez la brièveté)

appendice:

Voici un exemple de travail qui a été testé ...

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

Soi-disant, il existe un problème plus grave ( voir ce fil sur les groupes Google ): Foreach traite l’objet comme un dictionnaire de paramètres et non comme une collection à itérer. 

Ma meilleure solution jusqu'ici consiste à combiner un contexte foreach dans Object.keys(myobject) et 'avec'.

2
Jakub Macek

Réponse simplifiée pour travailler avec n'importe quel objet de base, a travaillé pour moi:

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

Je suis un peu en retard, mais je pense que cela devrait fonctionner, une solution simple sans utiliser de modèle.

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>
    
    

0
Sufiyan Ansari

(pas strictement itérant sur les propriétés, mais ne crée le tableau ci-dessus)

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

mise à jour: http://jsfiddle.net/cwnEE/7/

0
paul