Chain Backbone.js collection method - backbone.js

How can I chain collection methods in backbone.js?
var Collection = this.collection;
Collection = Collection.where({county: selected});
Collection = Collection.groupBy(function(city) {
return city.get('city')
});
Collection.each(function(city) {
// each items
});
I tried something like this, but it's wrong:
Object[object Object],[object Object],[object Object] has no method 'groupBy'

You cannot access Backbone.Collection methods that way (hope I'm not wrong) but as you probably know most of the Backbone methods are Underscore.js based methods so that means if you look at the the source code for where method you will see it uses Underscore.js filter method, so this means you can achieve what you want doing so:
var filteredResults = this.collection.chain()
.filter(function(model) { return model.get('county') == yourCounty; })
.groupBy(function(model) { return model.get('city') })
.each(function(model) { console.log(model); })
.value();
.value() isn't of any use to you here, you are making "stuff" inside the .each method for each of the model but if you would like to let's say return an array of filtered cities you can do with .map and in filteredResults will be your results
var filteredResults = this.collection.chain()
.filter(function(model) { return model.get('county') == yourCounty; })
.map(function(model) { return model.get('city'); })
.value();
console.log(filteredResults);

Related

angularjs: invoking custom filter from within controller

I'm trying to manipulate some JSON data using a custom angular filter and I'm having difficulty when trying to invoke it from within my controller.
As you can see in this jsfiddle, the idea is to only return items from my json with a rating higher than 6, which should return two items: items 2 & 3.
This is my filter:
.filter('greaterThan', function() {
return function(a, b) {
return parseInt(a) > parseInt(b);
}
})
and I'm invoking it within my controller like so:
var greaterThanFilter = $filter('greaterThan'),
filteredItems = greaterThanFilter(data, {'rating': 6});
$scope.items = filteredItems;
I've tried to model my filter and the invocation on the built-in angular filters, so I'm not sure what I'm doing wrong.
You're not implementing the filter the way you think you are, it looks like you're sorting, but what?
This is probably more like what you are looking for, note the options parameter.
.filter('greaterThan', function() {
return function(data, options) {
var result = [];
data = data || [];
if (options.rating != undefined) {
for (let i = 0; i < data.length; i++)
if (data[i].rating > options.rating)
result.push(data[i]);
}
return result;
}
})
And in your controller, invoke like this
$scope.items = $filter('greaterThan')(data, {rating: 6});
I forked your fiddle, see this

Backbone, getting parameters others than data in collection

Given the following json:
{
"admin": false,
"data": [
{
value: key,
value :key
},
{
value: key,
value :key
}
]
}
I defined my collection like this:
var myCollection = Backbone.Collections.extend({
url: myurl.com,
parse : function (response) {
return response.data;
}
});
It works like charm, it fill my collection with the data array, however, into the tamplate, I need to render some content when admin is equal true. But I cannot find a way to pass that value to the template.
Any chance any of u kind guys can point it into the right direction to solve this?
You could save the admin flag as a property of the collection in the parse method:
var myCollection = Backbone.Collection.extend({
model: myModel,
isAdmin: false,
...
parse : function (response) {
this.isAdmin = response.admin; //save admin flag from response
return response.data;
}
});
Then you could retrieve it and pass it to your template or use it in any other way in the view render method:
var myView = Backbone.View.extend({
collection: new myCollection(),
...
render: function(){
//retrieve admin flag from collection:
var isAdmin = this.collection.isAdmin;
//you could add it into the json you pass to the template
//or do anything else with the flag
}
});
You can try this fiddle with a very basic render function.

Angular JS - Manipulating Data in a Service / Factory and Making Sanitized Object Available to Controller

I currently have a simple factory like the following:
myFactory.factory('myFactory', function($resource){
return $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
});
I would like to move some of the data manipulation / sanitation that I am currently doing in my controller to my factory. I changed my factory to the following:
myFactory.factory('myFactory', function($resource){
var connection = $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
var filters = connection.query();
for(var i = 0; i < filters.data.length; i++){
// Get the name value in the data array
var name = filters.data[i].Name;
// Create new properties in $scope.filters and assign arrays to them. Each array is a separate filter
filters[name] = filters.data[i].Values;
}
return filters;
});
Essentially what I am doing in the above is reordering the structure of the object array returned from the AJAX call.
When I try to console log my 'filters' variable from the factory in my controller, I get an 'undefined' message. What is wrong with my above factory syntax?
Because you're not publicly exposing anything to return filters, try:
myFactory.factory('myFactory', function($resource){
return {
getFilters:function(){
var connection = $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
var filters = connection.query();
for(var i = 0; i < filters.data.length; i++){
// Get the name value in the data array
var name = filters.data[i].Name;
// Create new properties in $scope.filters and assign arrays to them. Each array is a separate filter
filters[name] = filters.data[i].Values;
}
return filters;
}
}
});
and call it like this in your controller:
$scope.filters = myFactory.getFilters();
Excuse the horrible formatting, here's a plunk.

Backbone/Underscore _.each(...) doesn't seem to work according to docs

In this code...
_.each(this.photos, function(element,index,list) {
console.log('element...');
console.log(element);
var photoView = new PhotoView({photo:element});
self.$el.append(photoView.render());
});
element is the entire this.photos collection. Why is not just one photo element of the 10 in the collection?
EDIT: Here is my method that populates the photos collection....
loadPhotos: function(memberId) {
var self = this;
this.photos = new PhotosCollection([]);
this.photos.on('error', this.eventSyncError, this);
this.photos.fetch({
url: this.photos.urlByMember + memberId,
success: function(collection,response,options) {
console.log('Fetch photos success!');
self.render();
}
});
},
The collection loads with models just fine. In the Chrome console, I can see the collection of models. I'm not sure what's wrong. I cannot iterate the collection with any of the methods recommended by posters below.
You are using the _.each method incorrectly. The underscore methods needs to called directly on the collection:
this.photos.each(function(element,index,list) {
console.log('element...');
console.log(element);
var photoView = new PhotoView({photo:element});
self.$el.append(photoView.render());
});
Or you if want to use the _.each from you need to pass in the models property and not the collection object itself as the list:
_.each(this.photos.models, function(element,index,list) {
console.log('element...');
console.log(element);
var photoView = new PhotoView({photo:element});
self.$el.append(photoView.render());
});
One should use this.photos.each(function(elt, index, list){...}) instead of _.each(this.photos,...) because this.photos is not an underscorejs _.chain object.
Thank you for your suggestions! I would never have figured this out without all your advice above. So here was the problem...
In the parent view, this loads up photo records for a particular member...
loadPhotos: function(memberId) {
var self = this;
this.photos = new PhotosCollection([]);
this.photos.on('error',this.eventSyncError,this);
this.photos.fetch({
url: this.photos.urlByMember + memberId,
success: function(collection,response,options) {
self.render();
}
});
},
Still in the parent view, Backbone.Subviews uses this to call each child view when it renders. Note how I'm passing this.photos to the subvw-photos...
subviewCreators: {
"subvw-profile": function() {
var options = {member: this.member};
// do any logic required to create initialization options, etc.,
// then instantiate and return new subview object
return new ProfileView( options );
},
"subvw-photos": function() {
var options = {photos: this.photos};
return new PhotosView( options );
},
"subvw-comments": function() {
var options = {};
return new CommentsView( options );
}
},
This is in the subvw-photos child view. Note how the intialize is accepting the collection as a parameter. See this problem?...
initialize: function(photos) {
Backbone.Courier.add(this);
this.photos = photos;
},
render: function() {
console.log('rendering photosview now...');
var self = this;
this.photos.each(function(element,index,list) {
var photoView = new PhotoView({photo:element});
$(self.el).append(photoView.render());
});
return this;
},
I was passing an object wrapping the photos collection in to initalize but then treating it like it was just a ref to the photos collection. I had to change the subvw-photos initialize to the following...
initialize: function(args) {
Backbone.Courier.add(this);
this.photos = args.photos;
},
Then of course all the other code magically began working :-/
Thank you again for your tips! You definitely kept me on track :-)

Laravel 4 and Backbone not playing well together -- JSON improperly formatted

I'm trying to return JSON output from a Laravel route. Here's my route:
Route::get('main-contact-count', function() {
$mc = MainContact::where('flag', '=', '1')->count();
return Response::json(['count' => $mc]);
});
If I look at the response tab in Firebug, I'm getting back:
{"count":9}
The JSON encoding is missing the square brackets. This JSON is different than the JSON Laravel returns from a resourceful controller. It has the square brackets. Backbone parses it fine. Backbone doesn't parse the above JSON correctly. If you look at the length and models when you console.log a collection, they're both zero. You can, however, drill down into the object and you can find a count property set to 9.
How do I get the correct JSON out of Laravel?
Just for kicks and giggles, I'm posting my Backbone, in case I'm doing something hinky there:
App.Collections.Count = Backbone.Collection.extend({
model: App.Models.Count,
initialize: function(models, options) {
this.fetch({
success: function(data, options) {
// console.log(data.models);
}
});
if (options) {
this.url = this.url || options.url;
}
}
});
App.Views.Count = Backbone.View.extend({
tagName: 'span',
className: 'leadCount',
template: _.template($('#contactCount').html()),
initialize: function() {
},
render: function() {
this.$el.html(this.template(this.collection.toJSON()));
return this;
}
});
And in my route:
var mc = new (App.Collections.Count.extend({ url: 'main-contact-count' }))();
var mcv = new (App.Views.Count.extend({ collection: mc }))();
The JSON response sent by your service is a single JSON object ({...}) Your collection is not able to parse it because it expects an array of JSON objects ([{...}, {...}]).
If I've understood your code correctly, /main-contact-count is a specialized service whose function is to return and single datum: a count of contacts. If this is the case, Backbone.Collection may not be the correct solution. Collections are meant for... well, collections of things.
You should use just a model instead:
App.Models.Count = Backbone.Model.extend({
url:'main-contact-count'
});
var countModel = new App.Models.Count();
countModel.fetch({success: function(model, resp) {
var count = model.get('count');
});
Or even better, forego Backbone altogether and just fetch the data using jQuery:
$.getJSON('main-contact-count', function(response) {
var count = response.count;
});
try:
return Response::json(array(array('count' => $mc));

Resources