Avec Backbone.js J'ai une collection configurée avec une fonction de comparateur. C'est bien de trier les modèles, mais j'aimerais inverser l'ordre.
Comment trier les modèles par ordre décroissant plutôt que croissant?
Eh bien, vous pouvez retourner des valeurs négatives à partir du comparateur. Si nous prenons, par exemple, l'exemple du site de Backbone et que nous voulons inverser la commande, cela ressemblera à ceci:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
chapters.comparator = function(chapter) {
return -chapter.get("page"); // Note the minus!
};
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
alert(chapters.pluck('title'));
Personnellement, je ne suis pas très satisfait de l'une des solutions proposées ici:
La solution de multiplication par -1 ne fonctionne pas lorsque le type de tri n'est pas numérique. Bien qu'il existe des moyens de contourner cela (en appelant Date.getTime()
par exemple), ces cas sont spécifiques. Je voudrais une manière générale d'inverser la direction de toute sorte, sans avoir à vous soucier du type spécifique du champ en cours de tri.
Pour la solution de chaîne, la construction d'une chaîne un caractère à la fois semble être un goulot d'étranglement des performances, en particulier lors de l'utilisation de grandes collections (ou, en fait, de grandes chaînes de champs de tri)
Voici une solution qui fonctionne bien pour les champs String, Date et Numeric:
Tout d'abord, déclarez un comparateur qui inversera le résultat d'un résultat d'une fonction sortBy, comme ceci:
function reverseSortBy(sortByFunction) {
return function(left, right) {
var l = sortByFunction(left);
var r = sortByFunction(right);
if (l === void 0) return -1;
if (r === void 0) return 1;
return l < r ? 1 : l > r ? -1 : 0;
};
}
Maintenant, si vous voulez inverser le sens du tri, tout ce que nous devons faire est de:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
// Your normal sortBy function
chapters.comparator = function(chapter) {
return chapter.get("title");
};
// If you want to reverse the direction of the sort, apply
// the reverseSortBy function.
// Assuming the reverse flag is kept in a boolean var called reverseDirection
if(reverseDirection) {
chapters.comparator = reverseSortBy(chapters.comparator);
}
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
alert(chapters.pluck('title'));
La raison pour laquelle cela fonctionne est que Backbone.Collection.sort
se comporte différemment si la fonction de tri a deux arguments. Dans ce cas, il se comporte de la même manière que le comparateur passé dans Array.sort
. Cette solution fonctionne en adaptant la fonction sortBy à un seul argument dans un comparateur à deux arguments et en inversant le résultat.
Le comparateur de collection de Backbone.js s'appuie sur la méthode Underscore.js _.sortBy. La façon dont sortBy est implémentée finit par "envelopper" javascript .sort () d'une manière qui rend le tri des chaînes en sens inverse difficile. La simple négation de la chaîne finit par retourner NaN et rompt le tri.
Si vous devez effectuer un tri inverse avec des chaînes, tel qu'un tri alphabétique inversé, voici une façon vraiment hack de le faire:
comparator: function (Model) {
var str = Model.get("name");
str = str.toLowerCase();
str = str.split("");
str = _.map(str, function(letter) {
return String.fromCharCode(-(letter.charCodeAt(0)));
});
return str;
}
Ce n'est pas joli du tout, mais c'est un "négateur de chaînes". Si vous n'avez aucun scrupule à modifier les types d'objets natifs en javascript, vous pouvez vous rendre le code plus clair en extrayant le négateur de chaîne et en l'ajoutant en tant que méthode sur String.Prototype. Cependant, vous ne voulez probablement le faire que si vous savez ce que vous faites, car la modification des types d'objets natifs en javascript peut avoir des conséquences imprévues.
Ma solution était d'inverser les résultats après le tri.
Pour éviter le double rendu, triez d'abord avec silencieux, puis déclenchez "reset".
collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
Modifiez votre fonction de comparateur pour renvoyer une valeur inversement proportionnelle au lieu de renvoyer les données que vous êtes actuellement. Du code de: http://documentcloud.github.com/backbone/#Collection-comparator
Exemple:
var Chapter = Backbone.Model;
var chapters = new Backbone.Collection;
/* Method 1: This sorts by page number */
chapters.comparator = function(chapter) {
return chapter.get("page");
};
/* Method 2: This sorts by page number in reverse */
chapters.comparator = function(chapter) {
return -chapter.get("page");
};
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
Les éléments suivants ont bien fonctionné pour moi:
comparator: function(a, b) {
// Optional call if you want case insensitive
name1 = a.get('name').toLowerCase();
name2 = b.get('name').toLowerCase();
if name1 < name2
ret = -1;
else if name1 > name2
ret = 1;
else
ret = 0;
if this.sort_dir === "desc"
ret = -ret
return ret;
}
collection.sort_dir = "asc";
collection.sort(); // returns collection in ascending order
collection.sort_dir = "desc";
collection.sort(); // returns collection in descending order
J'ai lu certains endroits où la fonction de comparateur Backbone utilise ou imite la fonction JavaScript Array.prototype.sort (). Cela signifie qu'il utilise 1, -1 ou 0 pour décider de l'ordre de tri de chaque modèle de la collection. Celui-ci est constamment mis à jour et la collection reste dans le bon ordre en fonction du comparateur.
Des informations sur Array.prototype.sort () peuvent être trouvées ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en- US & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort
De nombreuses réponses à cette question inversent simplement l'ordre juste avant de le rendre sur la page. Ce n'est pas l'idéal.
En utilisant l'article ci-dessus sur Mozilla comme guide, j'ai pu garder ma collection triée dans l'ordre inverse en utilisant le code suivant, puis nous rendons simplement la collection à la page dans son ordre actuel et nous pouvons utiliser un indicateur (reverseSortDirection) pour inverser l'ordre .
//Assuming this or similar will be in your Backbone.Collection.
sortKey: 'id', //The field name of the item in your collection
reverseSortDirection: false,
comparator: function(a, b) {
var sampleDataA = a.get(this.sortKey),
sampleDataB = b.get(this.sortKey);
if (this.reverseSortDirection) {
if (sampleDataA > sampleDataB) { return -1; }
if (sampleDataB > sampleDataA) { return 1; }
return 0;
} else {
if (sampleDataA < sampleDataB) { return -1; }
if (sampleDataB < sampleDataA) { return 1; }
return 0;
}
},
Comparateur sauf les deux modèles, lors des modifications de la collection ou du modèle, la fonction de compartiment s'exécute et modifie l'ordre de la collection.
J'ai testé cette approche avec Numbers and Strings et elle semble fonctionner parfaitement pour moi.
J'espère vraiment que cette réponse pourra m'aider car j'ai lutté avec ce problème à plusieurs reprises et je finis généralement par contourner ce problème.
Cela peut être fait avec élégance en remplaçant la méthode sortBy. Voici un exemple
var SortedCollection = Backbone.Collection.extend({
initialize: function () {
// Default sort field and direction
this.sortField = "name";
this.sortDirection = "ASC";
},
setSortField: function (field, direction) {
this.sortField = field;
this.sortDirection = direction;
},
comparator: function (m) {
return m.get(this.sortField);
},
// Overriding sortBy (copied from underscore and just swapping left and right for reverse sort)
sortBy: function (iterator, context) {
var obj = this.models,
direction = this.sortDirection;
return _.pluck(_.map(obj, function (value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function (left, right) {
// swap a and b for reverse sort
var a = direction === "ASC" ? left.criteria : right.criteria,
b = direction === "ASC" ? right.criteria : left.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index < right.index ? -1 : 1;
}), 'value');
}
});
Vous pouvez donc l'utiliser comme ceci:
var collection = new SortedCollection([
{ name: "Ida", age: 26 },
{ name: "Tim", age: 5 },
{ name: "Rob", age: 55 }
]);
//sort by "age" asc
collection.setSortField("age", "ASC");
collection.sort();
//sort by "age" desc
collection.setSortField("age", "DESC");
collection.sort();
Cette solution ne dépend pas du type de champ.
// For numbers, dates, and other number-like things
var things = new Backbone.Collection;
things.comparator = function (a, b) { return b - a };
// For strings
things.comparator = function (a, b) {
if (a === b) return 0;
return a < b ? -1 : 1;
};
Voici une solution vraiment simple, pour ceux qui veulent simplement retourner la commande en cours. C'est utile si vous avez une table dont les colonnes peuvent être ordonnées dans deux directions.
collection.models = collection.models.reverse()
Vous pouvez le combiner avec quelque chose comme ça si vous êtes intéressé par le cas de table (coffeescript):
collection.comparator = (model) ->
model.get(sortByType)
collection.sort()
if reverseOrder
collection.models = collection.models.reverse()
Pour l'ordre inverse sur id:
comparator: function(object) { return (this.length - object.id) + 1; }
J'avais une exigence légèrement différente, en ce sens que j'affiche mes modèles dans un tableau, et je voulais pouvoir les trier par n'importe quelle colonne (propriété du modèle) et soit ascendante soit descendante.
Il a fallu pas mal de temps pour faire fonctionner tous les cas (où les valeurs peuvent être des chaînes, des chaînes vides, des dates, des nombres. Cependant, je pense que c'est assez proche.
inverseString: function(str)
{
return str.split("").map(function (letter) { return String.fromCharCode(-(letter.charCodeAt(0))); }).join("");
}
getComparisonValue: function (val, desc)
{
var v = 0;
// Is a string
if (typeof val === "string")
{
// Is an empty string, upgrade to a space to avoid strange ordering
if(val.length === 0)
{
val = " ";
return desc ? this.inverseString(val) : val;
}
// Is a (string) representing a number
v = Number(val);
if (!isNaN(v))
{
return desc ? -1 * v : v;
}
// Is a (string) representing a date
v = Date.parse(val);
if (!isNaN(v))
{
return desc ? -1 * v : v;
}
// Is just a string
return desc ? this.inverseString(val) : val;
}
// Not a string
else
{
return desc ? -1 * val : val;
}
},
utilisation:
comparator: function (item)
{
// Store the collumn to 'sortby' in my global model
var property = app.collections.global.get("sortby");
// Store the direction of the sorting also in the global model
var desc = app.collections.global.get("direction") === "DESC";
// perform a comparison based on property & direction
return this.getComparisonValue(item.get(property), desc);
},
Voici un moyen rapide et facile de trier à l'envers les modèles d'une collection en utilisant UnderscoreJS
var reverseCollection = _.sortBy(this.models, function(model) {
return self.indexOf(model) * -1;
});
J'ai récemment rencontré ce problème et j'ai simplement décidé d'ajouter une méthode rsort.
Collection = Backbone.Collection.extend({
comparator:function(a, b){
return a>b;
},
rsort:function(){
var comparator = this.comparator;
this.comparator = function(){
return -comparator.apply(this, arguments);
}
this.sort();
this.comparator = comparator;
}
});
J'espère que quelqu'un trouvera cela utile
Je viens de remplacer la fonction de tri pour inverser le tableau des modèles une fois terminé. J'ai également retardé le déclenchement de l'événement "tri" jusqu'à ce que l'inverse soit terminé.
sort : function(options){
options = options || {};
Backbone.Collection.prototype.sort.call(this, {silent:true});
this.models.reverse();
if (!options.silent){
this.trigger('sort', this, options);
}
return this;
},