2012-03-26 32 views
14

Does KnockoutJS tienen una característica mientras que pude tomar algo como:KnockoutJS ObservableArray datos de agrupación

var myArray = ko.observableArray([ 
     { name: "Jimmy", type: "Friend" }, 
     { name: "George", type: "Friend" }, 
     { name: "Zippy", type: "Enemy" } 
    ]); 

A continuación, seleccione distinto en el campo "Tipo", produciendo un resultado que se ve así:

(pseudo code) 
    var distinct = myArray.distinct('type') 
     // Returns array of two arrays 
     // distinct[0] is an array of type=Friend 
     // distinct[1] is an array of type=Enemy 

Conozco ko.utils.arrayGetDistinctValues, pero eso no hace exactamente lo que quiero. También sé que podría escribir algunos bucles usando ko.utils.arrayGetDistinctValues ​​para obtener lo que quiero, solo me pregunto si hay algo más en KnockoutJS que esté pasando por alto.

Respuesta

33

No hay nada más incorporado en KO para hacerlo más fácil.

Hay muchas maneras en que puede hacer que esto funcione. Por ejemplo, podría extender las flechas observables para tener una función distinct. Entonces, sólo puede crear su observableArray como:

this.people = ko.observableArray([ 
     new Person("Jimmy", "Friend"), 
     new Person("George", "Friend"), 
     new Person("Zippy", "Enemy") 
]).distinct('type'); 

La función distinct podría ser:

ko.observableArray.fn.distinct = function(prop) { 
    var target = this; 
    target.index = {}; 
    target.index[prop] = ko.observable({});  

    ko.computed(function() { 
     //rebuild index 
     var propIndex = {}; 

     ko.utils.arrayForEach(target(), function(item) { 
      var key = ko.utils.unwrapObservable(item[prop]); 
      if (key) { 
       propIndex[key] = propIndex[key] || []; 
       propIndex[key].push(item);    
      } 
     }); 

     target.index[prop](propIndex); 
    }); 

    return target; 
};  

Es compatible con el encadenamiento por lo que se podría llamar distinct varias veces con diferentes propiedades.

Muestra aquí: http://jsfiddle.net/rniemeyer/mXVtN/

Esto reconstruir el índice una vez en cada cambio, por lo que si usted tiene una gran lista de elementos, entonces usted quiere potencialmente explorar otras maneras (suscripciones manual) para añadir/eliminar elementos de las matrices de "índice".

+0

Excelente respuesta. ¿Puedes dar un ejemplo de cómo encadenarlo (agrupar un grupo de una matriz observable)? – tmg

+0

Este es un gran ejemplo. Gracias Ryan Niemeyer. Respecto al encadenamiento. No quería trabajar fuera de la caja. Tuve que modificarlo http://jsfiddle.net/xuzhke3m/ – forgotpw1

3

Solo quiero agregar a esto que si llama este método .distinct() dos veces (como quizás a partir de un observable calculado), obtendrá los índices y la función calculada asociada llamada dos veces: enjuague y repita y obtendrá un rendimiento problema en tus manos

Para resolver esto, añada esta línea cerca de la parte superior de la función:

if (target.index && target.index[prop]) return target; //Group by already set up, bail out. 
7

He simplificado versión de RP Niemeyer en jsFiddle a hacer lo mismo sin utilizar la función distinta. Por favor, consulte aquí: jsfiddle

<ul data-bind="foreach: choices"> 
<li> 
    <h2 data-bind="text: $data"></h2> 
    <ul data-bind="foreach: $root.people"> 
     <!-- ko if: $parent === type() --> 
     <li data-bind="text: name"></li> 
     <!-- /ko --> 
    </ul> 
    <hr/> 
</li> 

var Person = function(name, type) { 
    this.name = ko.observable(name); 
    this.type = ko.observable(type);  
} 

var ViewModel = function() { 
    var self = this; 
    this.choices = ["Friend", "Enemy", "Other" ]; 
    this.people = ko.observableArray([ 
      new Person("Jimmy", "Friend"), 
      new Person("George", "Friend"), 
      new Person("Zippy", "Enemy") 
    ]); 

    this.addPerson = function() { 
     self.people.push(new Person("new", "Other")); 
    }; 

    this.removePerson = function(person) { 
     self.people.remove(person); 
    }; 
}; 


ko.applyBindings(new ViewModel()); 

Gracias Niemeyer.

Cuestiones relacionadas