必要な機能
Backbone.Collectionを使用して、並べ替え可能なリストのデータを表示しています。特定のdom要素をクリックすると、コレクションのプロパティが設定され、並べ替えるフィールドと、並べ替えを実行する方向を決定する部分があります。次に、コレクションで並べ替えがトリガーされ、その後ビューが更新されます。
最終的には、文字列だけでなく数値フィールドも並べ替えられるようにしたいと思います。一部の文字列はアルファベット順に並べ替える必要があり、その他の文字列はカスタムの事前定義された順序で並べ替える必要があります。
私の現在のアプローチ
文字列の逆ソートに関する次の投稿を見つけました: backbone.jsを使用して文字列を逆順でソートする
これをコンパレータ関数と次のカスタムプロパティと組み合わせてみました。
- 'status'フィールドで可能な値のカスタムソート順
- 並べ替えに使用するフィールド
- 並べ替える方向
私がこれまでに持っているコレクションとモデルのクラス:
コレクション
var ApplicationCollection = Backbone.Collection.extend({
model: ApplicationModel,
url: 'rest/applications',
// sorting
statusOrder: ['new', 'to_pay', 'payed', 'ready'],
sortField: 'submission_timestamp',
sortOrder: 'asc',
sortBy: function() {
console.log('ApplicationCollection.sortBy', this, arguments);
console.log('this.comparator', this.comparator);
var models = _.sortBy(this.models, this.comparator);
if (this.sortOrder != 'asc') {
models.reverse();
}
return models;
},
comparator: function(application) {
console.log('ApplicationCollection.comparator', this, arguments);
switch(this.sortField) {
case 'status':
return _.indexOf(this.statusOrder, application.get(this.sortField));
break;
case 'submission_timestamp':
default:
return application.get(this.sortField)
break;
}
}
});
モデル
var ApplicationModel = Backbone.Model.extend({
defaults: {
'drupal_id': 0,
'language': '',
'last_name': '',
'first_name': '',
'address': '',
'zip_code': '',
'city': '',
'country': '',
'telephone': '',
'cell_phone': '',
'email': '',
'date_of_birth': '',
'email': '',
'date_of_birth': '',
'pilot_training_institute': '',
'date_of_exam': '',
'flight_hours_total': '',
'date_of_last_flight': '',
'date_of_mcc_certificate': '',
'date_of_ir_renewel': '',
'package': '',
'submission_timestamp': '',
'status': 'new'
},
urlRoot: 'rest/applications'
});
現在の(望ましくない)結果
次のように、「pendingApplications」に保存されているコレクションのインスタンスがあります。
var pendingApplications = new ApplicationCollection();
pendingApplications.fetch();
これにより、サーバーからアプリケーションが読み込まれ、すべてが計画どおりに機能します。アプリケーションのリストを使用してビューをレンダリングできます。すべてのプロパティがモデルにあります。
コレクションを並べ替えるには、次のようにします。
pendingApplications.sortOrder = 'asc';
pendingApplications.sortField = 'current_timestamp'; // format: YYYY-mm-dd HH:mm:ss
pendingApplications.sort();
これにより、コレクションの並べ替え機能がトリガーされます。コンソールは、「ApplicationCollection.sortBy」メソッドが「pendingApplications」インスタンスのスコープ内で実行されることを通知します。これは期待どおりです。
ただし、「ApplicationCollection.comparator」メソッドはグローバルスコープ内で実行されるため、理由はわかりません。また、コンパレータメソッドの引数には「pendingApplications」インスタンスが含まれていません。
代わりに、コンパレータメソッドを「pendingApplications」インスタンスのスコープ内で実行するか、少なくとも「pendingApplications」インスタンスのプロパティにアクセスできるようにしたいのです。
スコープの問題?間違ったアプローチ?任意の提案を歓迎します...
この問題を解決する方法を知っている人はいますか?または、これを間違った方法で行っていますか?Backbone.Collectionでカスタムソートを任意に定義する別の解決策はありますか?
ソリューション
結局、Backbone.Collectionのデコレータとしてソート機能を実装することになりました。このようにする理由は、コレクション内のアイテムをフィルタリングするためのデコレータも持っているためです。ソートデコレータを使用することで、フィルタリングされたアイテムのサブセットにソートを適用できます。これは、潜在的に高速です。
/**
* returns a new Backbone.Collection which represents a sorted version of the
* data contained within the original Backbone.Collection.
*
* @param {Backbone.Collection} original
*/
SortedCollection: function(original, criteria) {
var sorted = new original.constructor(),
// sensible defaults
defaultSortCriteria = {
custom: {},
field: 'id',
direction: 'asc'
};
// configuration
sorted.sortCriteria = _.extend(defaultSortCriteria, criteria);
// do the stuff
sorted.comparator = function(a, b) {
// @formatter:off
var criteria = this.sortCriteria,
custom,
field = criteria.field,
direction = criteria.direction,
valA,
valB;
// @formatter:on
// custom sort
if (_.has(criteria.custom, field)) {
custom = criteria.custom[field];
// custom param is a comparator itself.
if (_.isFunction(custom)) {
return custom(a, b);
}
// custom param is an example of a sorted array.
else if (_.isArray(custom)) {
valA = _.indexOf(custom, a.get(field));
valB = _.indexOf(custom, b.get(field));
}
else {
throw new Error('Invalid custom sorting criterium.');
}
}
// nothing custom here, use the field value directly.
else {
valA = a.get(field);
valB = b.get(field);
}
// compare that shizzle!
if (valA > valB) {
return (direction == 'desc') ? -1 : 1;
}
if (valA < valB) {
return (direction == 'desc') ? 1 : -1;
}
else {
if (a.get('id') > b.get('id')) {
return (direction == 'desc') ? -1 : 1;
}
else if (a.get('id') < b.get('id')) {
return (direction == 'desc') ? 1 : -1;
}
else {
return 0;
}
}
};
// update collection if original changes
original.on("add", function(model) {
sorted.add(model);
});
original.on("reset", function() {
sorted.reset(original.models);
});
original.on("remove", function(model) {
sorted.remove(model);
});
return sorted;
}
デコレータの使用法:
original = new ApplicationsCollection();
sortable = SortedCollection(original);
sortable.sortCriteria = {
sortField: 'submission_timestamp',
sortDirection: 'desc'
}
sortable.sort();
上記のスニペットは次のことを行います。
- 新しいApplicationsCollectionをインスタンス化します。
- オリジナルを拡張し、オリジナルコレクションの関連イベントをリッスンするソート可能なコレクションをインスタンス化します。
- 'submission_timestamp'プロパティで降順で並べ替えるようにSortableCollectionに指示します。
- 並べ替え可能なコレクションを並べ替えます。
新しいソート可能コレクションは、新しいモデルが元のコレクションに追加または削除されたとき、または元のコレクションがリセットされたときにも自動的にソートされたままになります。