How to place a collection within a model in Backbone.js? - backbone.js

I would like some insight on how one would add structure a collection within a model. My simple app has teams (so a team model and collection of teams) and each team has a bunch of players(player model and player collections). So a visual structure of it is like so:
Team A
- Player 1
- Player 2
- Player 3
Team B
- Player 1
- Player 2
and so on...
How would one structure such a backbone app? Here is how I am planning it so far:
1) I would have a Team Collection, that would hold multiple teams whose model property corresponds to the TeamModel.
2) A Player Collection, that would hold all multiple players and model property corresponds to the PlayerModel.
Now I am confused as to how I would have the Team Collection and Model, correspond with the Player Collection and Model. I.e. according to my design, a third relationship would be that each team would have a collection of players. However I am unsure of how to implement that.

"Now I am confused as to how I would have the Team Collection and Model, correspond with the Player Collection and Model. I.e. according to my design, a third relationship would be that each team would have a collection of players."
Simply add an attribute to your Team Model that'd be a collection of players.
var Team = Backbone.Model.extend({
initialize: function() {
// assuming Players a collection of players
this.set('players', new Players());
}
});
Now, populating the data is another problem which has a lot of solutions. But doing things that way gives you a strong structure.

You could do something like:
App.Models.Player = Backbone.Model.extend({});
App.Collections.Players = Backbone.Collection.extend({
model: App.Models.Player,
url: 'players',
getTeam: function(idTeam){
var gf = _.filter( this.models, function(model){
return (
model.get('idTeam') == idTeam
);
});
return gf;
}
});
App.Models.Team = Backbone.Model.extend({
players: players( this.get('id') ) // asuming that players is an App.Collections.Players instance.
});
App.Collections.Team = Backbone.Collection.extend({
model: App.Models.Team,
url: 'teams'
});
And then, when you create the instances of each and collect data from the server, start the router once all collections have been populated.
It should work that way.

Related

Eager loading same model multiple times in a model

I have a Model named Player with 'id' and 'shortname'.
I have another Model named Team with 'id', 'teamname', 'player_1_id', 'player_2_id' and 'player_3_id'.
I am trying to use relations:
// in Model Team
public function players()
{
return $this->hasOne('App\Player', 'id','player_1_id')
->hasOne('App\Player', 'id','player_2_id')
->hasOne('App\Player', 'id','player_3_id');
}
// In controller
$resource = Team::with('players')->get(); doesnt work.
In this case, Which is the best(fastest) way to use eagerloading?
Thanks in advance...
You should read about database normalization. It does not make sense to store player_1_id, player_2_id and player_3_id in teams database. What if team will contain 20 players? You will create another fields?
You should either add team_id into Player model (in case players always belongs to only one team) or create extra table where you will store connections between players and teams.

Backbone.js Dynamic model creation based on JSON received from the server

I am trying to create a backbone client side application. I am receiving a list of json objects from the server on startup that will be a list of the possible tables exposed from the server, with their structure. E.g. Customers, Orders, Invoices, Employees
I want to create the models, collections and views dynamically based on the data I receive from the server.
Only when I receive the json on load will I know what the models should be and what the relationships between the models should be.
E.g. Customers structure might be Id, CustomerName, Address, Contact Numbers.
Order Structure might be Id, CustomerId, OrderDate, Amount
etc
By building Models, collections, views, controllers dynamically, I could in theory on startup point at another server who might give me a totally different set of tables e.g. : Movies, Actors etc.. with their structures.
Also, if additional fields are added I don't have to change the client side code again. E.g. Customer table might include a new field called ContactPerson
Please assist me as all the examples I saw on backbone is all based on statically defining the models on the client side up front. So create a model and collections and views for Customers, Orders, Invoices, Employees etc. etc.
Best wishes,
Andy
As already mentioned in the comments, Backbone models are dynamic by nature. So this is perfectly valid for example:
// A example dataset, this could be returned as JSON from the server
var jsonDataA = [
{
name: "Foo",
title: "Bar"
},
{
name: "a",
title: "b"
}
],
// A different example dataset
jsonDataB = [
{
make: "X",
model: "Y"
},
{
make: "Z",
model: "ZZ"
}
],
MyModel = Backbone.Model.extend({
/* Empty Model definition */
}),
MyCollection = Backbone.Collection.extend({
model: MyModel
}),
collection = new MyCollection();
collection.reset(jsonDataA);
console.log(collection.models);
collection.reset(jsonDataB);
console.log(collections.models);
Here I have reused the same Collection and Model definition to store completely different datasets.
One part is the raw data, the other part is its relations. You need to transport the metadata also, which contains the types and their relations. Model attributes will be populated automatically.
From your metadata a simple object can be constructed, where the keys describe one entity, for example:
var entites = {};
entities["Customer"] = Backbone.Model.extend({
/* Model definition based on metadata */
});
var parametersFromServer = {name: "John Doe"};
var customer = new entities["Customer"](parametersFromServer);
For building relations I would recommend using BackboneRelational plugin.

How to group backbone collection by model category, then sort each group by model rank

I am following David Sulc's tutorial on Marionette.
http://davidsulc.com/blog/2012/04/15/a-simple-backbone-marionette-tutorial/ and in order to learn extending over it.
Now lets say each cat has a name, rank as well as Category
AngryCat = Backbone.Model.extend({
urlRoot: '/api/cats',
defaults: {
name: "New Cat Name",
category: "Red Cat"
}
});
Now I want to make a composite view like this:
Category 1: Red Cat (03 Cats)
Cat 1
Cat 2
Cat 3
Category 2: Blue Cat (02 Cats)
Cat X
Cat Y
How can I achieve this. Please help!
You can use the groupBy function to group your AngryCat models by their by category property, then use sortBy to sort each group individually by its rank (I am assuming your model has a property named rank that is a number). I also assume you have an intial collection variable named angryCats that contains your models.
var groups = angryCats.groupBy(function(ac) {
return ac.get("category");
});
groups.each(function(group, key) {
// This will output the category name & item count
console.log(key + " " + group.length + " items");
group.sortBy(function (model) {
return model.get("rank");
});
group.each(function(model) {
// This will output the model name
console.log(model.get("name"));
});
});
Additionally, if your wanted to sort each group by a string property of the model, such as the name property, you'll need to create a new locally scoped angryCats collection out of each group (the groups are arrays), then sort each collection by using a comparator as the linked question outlines.
Hope this helps
You can probably achieve what you want to do by combining dcarson's code with my blog post on nested views: http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/
You need to provide a collection of groups to the composite view. The composite view's item views will be a collection view displaying the cats in that group (i.e. what is done in the linked blog post).

Backbone.js firing Collection change event multiple times

In one of by Backbone.js views I am updating the attribute "read" of the current model (instance of Message) by using this.model.set( { read: true } );. I verified that this command is only executed once (I know about "ghost events"). As you can see below I configured the Collection to fire an update event in which the whole Collection gets saved into a variable.
Unfortunately the saveToVar function gets called 3 times instead of one! Also, the first time saveToVar is called, this correctly consists of all the collection's models, whilst the 2nd and 3rd time this only has one model, namely the one I did the update on.
I tracked everything down piece by piece but I have no clue why this happens.
window.Message = Backbone.Model.extend({
});
window.MessageCollection = Backbone.Collection.extend({
model: Message,
initialize: function()
{
this.on("change", this.saveToVar);
},
saveToVar: function(e)
{
App.Data.Messages = this.toJSON();
return;
}
});
In your jsfiddle, you're doing this:
App.Collections.message = new MessageCollection([ ... ]);
var elements = App.Collections.message.where({ id: 4 });
var item = new MessageCollection(elements);
Your where call will return models that are in the message collection, not copies of those models but exactly the same model objects that are in message. Now you have two references to your id: 4 model:
The original one buried inside App.Collections.message.
The one in elements[0].
Both of those references are pointing at the same object. Then you add elements to another MessageCollection. Now you have something like this:
App.Collections.message.models[3] item.models[0]
| |
+--> [{id: 4}] <--+
Both of those collections will be notified about change events on the id: 4 model since collections listen to all events on their members:
Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience.
And your collection listens for "change" events in itself:
initialize: function()
{
this.on("change", this.saveToVar);
}
So when you do this:
this.model.set({ read: true });
in your view, both collections will be notified since that model happens to be in both collections.
If we alter your event handler to look like this:
saveToVar: function() {
console.log(_(this.models).pluck('cid'));
}
then you'll see that the same cid (a unique identifier that Backbone generates) appears in both collections. You can also attach a random number to each collection and see what you get in saveToVar: http://jsfiddle.net/ambiguous/mJvJJ/1/
You probably shouldn't have one model in two collections. You probably shouldn't have two copies of the same model kicking around either so cloning elements[0] before creating item might not be a good idea either. You might need to reconsider your architecture.

Backbone Model Structure

I'm making a Grocery list app, which is very similar with the todo list. I have several years of Rails dev experience, but am having trouble figuring out from all the examples what to put into a collection, and what to make a model.
I mocked up the app with Sinatra and Redis as the backend. My goal is to make Sinatra just the simple API and have backbone manage all the view.
Right now, a Grocery list is just a complex ID, which has a Set of string items. So something like:
/lists/asdfasdf34asdf => ["eggs", "bacon", "milk"]
Moving to backbone, would I make the model an "Item" and then the collection would be the "List", or would it be something else?
I guess my routes aren't classic Rest so maybe that's why i'm having trouble sorting out what to do where.
If there's only one grocery list, a Collection of item Models is probably appropriate. Backbone isn't too prescriptive about how things are organized, but you will definitely want to set the url property of each model/collection in a logical fashion. You might do something like this:
var app = {
item: Backbone.Model.extend({
// define an item model to go in the itemCollection
}),
itemCollection: Backbone.Collection.extend({
initialize: function (key) {
this.key = key;
},
model: app.item,
url: function () {
return 'lists/' + this.key + '/items/'
}
})
}
and then instantiate each version of the application along these lines:
var userListKey = 'foobar',
userCollection = new app.itemCollection(foobar);
// proceed with app. Requests for userCollection will now be
// directed to /lists/foobar/items
There are many other ways to do this, but hopefully this is a start.

Resources