I have a backbone collection that is pulling in a bunch of template names for people to use and I would like to sort them alphabetically so their easier to find. I am very unsure of how to do this though.
I have my backbone collection
this.templates = new Backbone.Collection();
and then I'm sorting through the templates to figure out where to add what.
var Names = this.model.collection.models.map(function(model){
return (model.attributes.Name) ? model.attributes.Name : 'Template';
});
Names.forEach(function(name) {
_this.templates.add(api.collections[(_this.templateType)].where({Name : name, ShowInToolBox : true}));
//adding a bunch of conditionals to add cretin forms to modules that are outside the scope
}
Is it possible to alphabetize these?
I've tried adding .sortBy("Name") to the backbone collection, but it just stopped my code from running.
Backbone offers the comparator property for sorting. You can pass the name of the property that the collection should be sorted on into the constructor:
this.templates = new Backbone.Collection([], { comparator: 'Name' })
Every time the collection changes, it will be re-sorted by the property name in the comparator. If you're doing something more complicated, you can define the comparator as a function. If you go this route, then I would recommend extending Backbone.Collection for clarity:
var Templates = Backbone.Collection.extend({
comparator: function(template1, template2){
return template1.get('someValue') - template2.get('someValue')
}
})
var templates = new Templates()
Backbone collections can be sorted with the comparator function.
If you define a comparator, it will be used to maintain the collection in sorted order. This means that as models are added, they are inserted at the correct index in collection.models. A comparator can be defined as a sortBy (pass a function that takes a single argument), as a sort (pass a comparator function that expects two arguments), or as a string indicating the attribute to sort by.
Related
I am trying to create a page that shows a list of products with filtering and multiple ways of sorting the list. Rather than repeating the filtering code for each sort, I wanted to start each of the sorting methods with a call to the filtering method. I can currently filter by name:
filterText: '',
filteredResults: function() {
var filterText = this.get('filterText');
var regexp = new RegExp(filterText,'gi');
var causes = this.get('model.products').filter(function(name){
return name.get('name').match(regexp);
});
return causes;
}.property('filterText'),
However I now want to use this function in the sort methods. I've tried using Ember.computed.sort :
priceAscSorting: ['price'],
sortPriceAsc: Ember.computed.sort('filteredResults', 'priceAscSorting'),
but it seems like it treats the first input as null if I stick it in a template. If I try calling the filter method inside the sorting method:
priceAscSorting: ['views'],
sortPriceAsc: function() {
var products = filteredResults();
}
Ember throws a compiler error saying 'filteredResults' is not defined. How can I access my filter methods to use in my sorting methods?
Your code is correct, it will take filteredResults computed property and return sorted result in ascending order by using price key.
priceAscSorting: ['price'],
sortPriceAsc: Ember.computed.sort('filteredResults', 'priceAscSorting'),
but it seems like it treats the first input as null if I stick it in a
template
that means you need to check filteredResults computed property
I have a backbone collection and based on an attribute of models inside collection I iterate over the collection and show it in the UI.
The logic is, if the model has property isNewCar as true, I'll first show them all in UI, followed by a separator then i'll show all models having property isNewCar as false.
this.cars.forEach(function (car, index)
{
if(car.isNewCar()){ //IF A NEW CAR
//some logic.
//Attach current view in DOM with this model's properties
}
});
//Here, Attach some separator in DOM
this.cars.forEach(function (car, index)
{
if(!car.isNewCar()){ //IF 'NOT' A NEW CAR
//some logic.
//Attach current view in DOM with this model's properties
}
});
This looks messy and I understand its not so elegant, can someone suggest a better way to replace above code with some elegant solution?
I would suggest have a common rendering logic to render the items in a separate function say renderCars() and filter the collection as below
function filterCars(isNew) {
var isNewCar = isNew
return function(car) {
return (car.isNewCar() === isNewCar);
}
}
renderCars (this.cars.filter(filterCars(true)) );
renderCars (this.cars.filter(filterCars(false)) );
All we have done above is created a helper function filterCars that takes a boolean to decide if we need new cars or not. This helper function returns a function that is used to filter the cars .
Backbone filter uses the underscore filter that returns a new array of the filtered results. I am passing this to the common render function.
I did not test this but this should help clear out some repeated code.
I have two backbone collections Categories and Items, Categories contains category models with id, name, and items (w/c contains item ids in string format separated by commas) and Items collection that contains item models(id, name, etc.).
How do I merge them into one object that can be easily be rendered in a handlebars template
Sample Structure:
var Categories = [{'id':'1', 'category_name':'Burgers', 'category_items':'1,2'},{'id':'2','category_name':'Drinks','category_items':'3'}];
var Items = [{'id':'1','item_name':'Burger 1'},{'id':'1','item_name':'Burger 2'},{'id':'1','item_name':'Chicken; 1'}];
Sample Output:
var output = [{'id':'1', 'category_name':'Burgers', 'items':[{'id':'1','item_name':'Burger1', ...},{'id':'1','item_name':'Burger2', ...} ]}, {'id':'2', 'category_name':'Chicken', 'items':[{'id':'3','item_name':'Chicken1', ...} ]
And yes, i tried enough, but I can't seem to manage it
Relying on Underscore's helpful functions:
var json = _.map(c.toJSON(), function(c) {
return _.extend(c, {
category_items: _.map(c.category_items.split(','), function(id) {
return i.get(id).toJSON();
})
});
});
http://jsfiddle.net/626x9/1/
The first _.map coupled with _.extend just serves the purpose on replacing the category_items. I find it quite abusive, but can't remember if there's any method that would do that in one shot.
I write a function called "mergeObjectWithId". It merge data from two collection follow these step:
Using collection.findWhere() to get the target model which need to merge.
Using collection.where() to get the models in item collection.
Then assign the items array to target object's property items. Now you get the output object which you wanted.
Try this function:
function mergeObjectWithId(id){
//assume categories is your Category collection instance
var obj = categories.findWhere({id:id}).clone();
//assume items is your Item collection instance
obj.items = items.find({id:id}).slice(0);
return obj;
}
var output = mergeObjectWithId(1);
Hope this is helpful for you.
What is the basic way to keep models any views in order using Backbone? I have some idea but it's not totally clear.
I want to keep them in order by a field called "created_at". I know there is the ability to provier a comparator function in the collection but I'm not sure how it works.
I also want this order in the collection to be reflected by the views at all times (in a list). I'm not exactly sure where I tie into the model though. I'm guessing i look for change in an index attribute and then update a to match?
Thanks very much for any help or explanation!
When you define your collection, you also define comparators.
I did it this way recently:
comparators: {
id: function(animal) {
return Number(animal.get("id"));
},
d_id: function(animal) {
return -Number(animal.get("id")); // descending
},
name: function(animal) {
return animal.get("name");
},
d_name: function(animal) {
return String.fromCharCode.apply(String, _.map(animal.get("name").split(""), function (c) {
return 0xffff - c.charCodeAt();
})
);
},
}
these I defined within my collection code.
Then, in rendering my collection views, I just did this
(this was within my view that renders the whole collection, in initialize():
this.collection = new MyCollection();
this.collection.comparator = Collection.comparators[// here I put 'id' or 'd_id' etc. ];
this.collection.sort();
Since this code is in your views's initialize, you can define your comparator
when you initialize your view, and pass it a name of a comparator like this:
var directory = new pageView("d_id");
and than thru initialize(comparator_id) you could pass this to your code in initialize:
this.collection = new MyCollection();
this.collection.comparator = Collection.comparators[comparator_id];
this.collection.sort();
And then I can use the collection in rendering and re-rendering the view/page
Edited:
Here is Backbone's collection.comparator documentation,
And right below it is an explanation of sort()
Basically, comparator can be a property of a model or a function that returns a property,
Or a negative property, if it's numeric, for descending order,
or a string or its reverse value for descending order
like in the example I gave you here.
So, comparator returns a property like "id" or "name", or "-id" , "-name" etc etc.
(for string you can't just make it "negative", you need to apply more complex function,
as I wrote.)
I have a backbone collection which is rendered in a table. I would like to make the table sortable based on certain attributes the collection has, like "task_status","task_group". I've being reading the backbone documentation about collection.comparator,nd collection.sort.
How can I get this done?
The comparator function is used to compare two models in the collection and it can compare them in any (consistent) way that it wants to. In particular, it can choose which model attribute to use so you could have something like this in your collection:
initialize: function() {
this.sort_key = 'id';
},
comparator: function(a, b) {
// Assuming that the sort_key values can be compared with '>' and '<',
// modifying this to account for extra processing on the sort_key model
// attributes is fairly straight forward.
a = a.get(this.sort_key);
b = b.get(this.sort_key);
return a > b ? 1
: a < b ? -1
: 0;
}
and then you just need some methods on the collection to change the sort_key and call sort:
sort_by_thing: function() {
this.sort_key = 'thing';
this.sort();
}
In older Backbones, calling sort will trigger a "reset" event whereas newer versions will trigger a "sort" event. To cover both cases you can listen to both events and re-render:
// in the view...
initialize: function() {
this.collection.on('reset sort', this.render, this);
}
Demo: http://jsfiddle.net/ambiguous/7y9CC/
You can also use listenTo instead of on to help you avoid zombies:
initialize: function() {
this.listenTo(this.collection, 'reset sort', this.render);
}
Demo: http://jsfiddle.net/ambiguous/nG6EJ/
#mu-is-too-short's answer is good, except there's an easier way to compare the field values:
The easiest way to sort the collection based on a field, is to provide a comparator function that returns the exact field's value you want to sort by. This kind of comparator causes Backbone to call sortBy function, instead of sort, which then does that complex comparison on it's own and you don't have to worry about the logic.
So in essence, you don't have to provide a complex comparator function, unless you have a more advanced need for determining the order.
var myCollection = Backbone.Collection.extend({
sort_key: 'id', // default sort key
comparator: function(item) {
return item.get(this.sort_key);
},
sortByField: function(fieldName) {
this.sort_key = fieldName;
this.sort();
}
});
After this you can just call the collection's sortByField -function with a string that represents the key that you want to sort by.
For example:
collection.sortByField('name');
Modified #my-is-too-short's demo: http://jsfiddle.net/NTez2/39/
#jylauril's answer helps out tremendously, but needed to modify the demo (perhaps slight changes in backbone since it was posted?)
Looks like you need to trigger a render after you've sorted.
$('#by-s').click(function() {
c.sortByField('s');
v.render();
});
Updated #my-is-too-short's demo: http://jsfiddle.net/NTez2/13/