2013-02-12 14 views
39

Voglio usare qualcosa di simile al costrutto foreach di Knockout per iterare sulle proprietà di un oggetto. Ecco quello che sto cercando di creare ...Come usare il knockout per iterare su un oggetto (non su un array)

risultato desiderato

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

Tuttavia, il mio modello simile a questa ...

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

Ogni riga ha un entityId e oggetti di scena che è un oggetto stesso. Questo modello non funziona, ma come dovrei cambiarlo per generare la tabella desiderata sopra?

EDIT: Il props in questo esempio sono name e lastLogin, ma ho bisogno di una soluzione che è agnostica a ciò che è contenuto all'interno props.

Ho questo FIDDLE andando pure.

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

è la quantità di oggetti di scena sempre 2 o possono differire da ogni altri? –

risposta

45

Si può sempre creare un gestore di binding per gestire la trasformazione.

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

poi applicarlo:

<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

Il valore ritorna come oggetto, perché non può essere semplicemente una stringa come la chiave? –

+0

Basta chiedersi se è possibile con questo bind di foreachprop utilizzare il contesto di vincita '$ parent'? – Thewads

+0

@Thewads: ovviamente. Puoi usarlo con qualsiasi oggetto semplice che abbia proprietà. Anche se non sono sicuro di capire a cosa ti riferisci. –

0

(non strettamente iterazione sulle proprietà, ma fa creare la tabella sopra)

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

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

+2

Vero, ma ho bisogno di iterare sulle proprietà in quanto i nomi in "oggetti di scena" saranno diversi per le diverse tabelle. Ho modificato la domanda per chiarire questo. –

53

In un browser moderno (o con un polyfill appropriato) è possibile iterare Object.keys(obj) (il metodo restituisce solo proprie proprietà enumerabili, il che significa che non c'è la necessità di un ulteriore controllo 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: Ero semplicemente curioso di vedere se questo dovrebbe funzionare, il corpo modello sopra è più inquinato di quello che mi piacerebbe utilizzare nella produzione (o tornare a un paio di mesi più tardi ed essere come "WTF ").

personalizzato vincolante sarebbe una scelta migliore, anche se la mia preferenza personale sarebbe quella di utilizzare un calcolato osservabile o un writeable computed observable (quest'ultimo sarebbe utile quando si lavora con json risposte a-la RESTful API).

+0

Questa risposta è sbalorditiva. Mi sono imbattuto in questo e ho pensato di controllare cosa avesse da dire SO. Le associazioni personalizzate per uno scopo specifico non sono valide per la produzione di siti su larga scala come semplici soluzioni in linea come questa. – MattSizzle

+0

Mi piacerebbe poter passare questo più volte. Mi piace questo perché non devo creare un bindinghandler. – valdetero

+0

Questa è la soluzione migliore! Non capisco perché non è il primo. – jbartolome

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

Speranza che è utile (pardon il brevità)

appendice:

Ecco un esempio di lavoro che è stato testato ...

<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

Questa è una modifica del La risposta di Jeff, con il contesto vincolante conservato

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

Ora applicare con i genitori e la radice:

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

Questa risposta è migliore di IMO accettato in quanto include la funzione di aggiornamento ..il che significa che si comporterà come un osservabile non solo per una singola iterazione. –

+0

@SteveCadwallader: non rende migliore l'esistenza di una funzione di "aggiornamento" esplicita, chiama direttamente la funzione di aggiornamento sul gestore 'foreach'. In altre parole, sta propagando manualmente le chiamate di aggiornamento. L'altra risposta ha applicato il gestore foreach all'elemento in modo che tutto ciò che 'foreach' succederà (inclusi gli aggiornamenti). La funzione di aggiornamento esplicito non è necessaria. –

2

Presumibilmente, c'è un problema più profondo (see this thread at Google groups) cioè che foreach considera l'oggetto come un dizionario di parametri, non come la collezione per scorrere.

La mia soluzione migliore finora è combinata foreach in Object.keys(myobject) e contesto di associazione "con".

0

risposta a lavorare con qualsiasi oggetto di base semplificato, ha lavorato per me:

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