I am currently trying to render out this json object in a ul. I'd like to be able to cycle through the GamesList and get the games and their attributes in a list. I've kinda hit a wall where I am not entirely sure how to accomplish this. Still very new to backbone so any help would be greatly appreciated.
JSON Object:
{
"GamesList":[
{
"Date":"2013/07/02",
"Games":[
{
"Id":"3252",
"Time":"12:10 AM"
}
]
},
{
"Date":"2013/07/02",
"Games":[
{
"Id":"3252",
"Time":"12:10 AM"
}
]
},
{
"Date":"2013/07/02",
"Games":[
{
"Id":"3252",
"Time":"12:10 AM"
}
]
}
]
}
App Structure:
App.Models.Game = Backbone.Model.extend({
defaults: {
GamesList: ''
}
});
App.Collections.Game = Backbone.Collection.extend({
model: App.Models.Game,
url: 'path/to/json',
parse: function (response) {
return response;
}
});
App.Views.Games = Backbone.View.extend({
tagName: 'ul',
initialize: function () {
this.collection = new App.Collections.Game();
this.listenTo(this.collection, 'reset', this.render, this);
this.collection.fetch();
},
render: function () {
//filter through all items in a collection
this.collection.each(function (game) {
var gameView = new App.Views.Game({
model: game
});
this.$el.append(gameView.render().el);
}, this)
return this;
}
});
App.Views.Game = Backbone.View.extend({
tagName: 'li',
template: _.template($('#gameTemplate').html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var gameCollection = new App.Collections.Game();
gameCollection.fetch({
data: {
collection_id: 25
},
success: function (data, textStatus, jqXHR) {
console.log(data);
console.log(textStatus);
console.log(jqXHR);
console.log('success');
},
error: function () {
alert('Oh noes! Something went wrong!')
}
});
var gamesView = new App.Views.Games({
collection: gameCollection
});
$(document.body).append(gamesView.render().el);
It looks like your JSON object is not inlined with Backbone.Collection...
as you declared App.Collections.Game has url /path/to/json which means the json that needs to return is a list... without the GamesList that is seen in your JSON
EDIT:
You can use the parse function in your Games Collection to fix the json retrieved from your server
parse:function(response){
return response.GamesList;
}
Important:
Please note that your json objects that are fetched from the server should have ID. Backbone will 'think' these models are new and will create them upon save...
I'm seeing a little confusion in it. Let's proceed step by step:
--------- AFTER COMMENT ---------
You can set your model as:
defaults: {
Date:'',
Games:''
}
then modifying your parse function as
parse: function (response)
{
var _this = this;
_.map(response, function(obj) {
_this.add(obj)
});
}
This way you add each single item in the collection as your model expect.
Another problem I'm seeing is that you're creating and fetching the collection twice:
...
this.collection = new App.Collections.Game();
this.listenTo(this.collection, 'reset', this.render, this);
this.collection.fetch();
...
and then
var gameCollection = new App.Collections.Game();
...
gameCollection.fetch({
data: {
....
...
var gamesView = new App.Views.Games({
collection: gameCollection
});
Related
var Order = Backbone.Model.extend({
url: function() {
return 'http://localhost:51782/api/orders/'+this.id;
}
});
var DataSetOrders = Backbone.Collection.extend({
url: "http://localhost:51782/api/orders",
model: Order,
initialize: function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
// This is where my data is being extracted I am returning the
// response since its an array of all the objects within my database
// perhaps i should not use return? Is there a way to save this information so it can always
// be available when i call DataSetOrders();?
fetchSuccess: function (collection, response) {
// console.log('Collection fetch success', response);
// console.log('Collection models: ', collection.models);
return response;
},
fetchError: function (collection, response) {
throw new Error("Orders fetch error");
}
});
var DataSetOrdersView = Backbone.View.extend({
el: $("#orders"),
// collection: new DataSetOrders(),
initialize: function () {
this.collection = new DataSetOrders();
this._modelBinder = new Backbone.ModelBinder();
this.render();
this.$el.find("#filter").append(this.createSelect());
this.on("change:filterType", this.filterByStatus, this);
this.collection.on("reset", this.render, this);
this.collection.on("add", this.renderOrder, this);
this.collection.on("remove", this.removeOrder, this);
},
render: function () {
this.$el.find("article").remove();
// This is the line where we are getting all of our models and rendering it to the #orders view
// as you can see it is referencing the this.collection = new DataSetOrders()
// This is actually working the view is being rendered and I can see the orders being pulled from
// the database
_.each(this.collection.models, function (item) {
this.renderOrder(item);
}, this);
},
renderOrder: function (item) {
// The item which is an element of the previous array returned is now being placed in the model
// and its being rendered prefectly.
var orderView = new OrderView({
model: item
});
this.$el.append(orderView.render().el);
// this.$el.append(this._modelBinder.bind(orderView.render().el));
},
getTypes: function () {
// HERE!!! IS where this.collection is now = [] wtf?!? I dont know why its doing this pleace help me!!
return _.uniq(this.collection.pluck("OrderStatus"), false, function (type) {
return type.toLowerCase();
});
},
Guys sorry for the long code, but I have a simple question. Why does the first time my code calls when using the method render this.collection has the elements within it and because of my code structure it renders my database objects and works perfectly. But when I call this.collection again on my geTypes function I get an empty array I dont understand whats going on??
Heres my code:
var RowsSubView = Backbone.View.extend({
initialize: function() {
log.debug(this.collection);
},
render: function() {
var html = RowView();
this.setElement(html);
return this;
}
});
var View = BaseView.extend({
id: 'wrapper',
className: 'container-fluid',
events: {
},
initialize: function() {
_.bindAll(this, 'render');
log.debug('Initialized Queue View');
this.opportunities = new Opportunities();
this.opportunities.on('add', function(model){
});
this.opportunities.fetch({
success: function(response, options) {
},
error: function(response) {
}
});
},
render: function() {
var template = QueueView();
this.$el.html(template);
this.renderRowsSubView();
return this;
},
renderRowsSubView: function() {
// render rows
this.row = new RowsSubView({collection: this.opportunities});
this.row.render();
this.$el.find('tbody').append(this.row.el);
}
});
Heres my question:
Sorry for the noob question! I am learning Backbone and having a bit of an issue. I've looked at a bunch of tutorials/guides, but I think I've confused myself.
I am trying to create a list of items and render them in a table. I want to pass each item into my template and spit it out in the view.
I am stuck after passing my collection to my RowsSubView. I'm not sure how to render each object in the template. Then insert those.
PS: I am able to log this.collection in my RowsSubView and see an object with the array of items.
Thanks.
Ok well start with this. Looks like there's quite a bit of cleanup that needs to be done =)
var RowsSubView = Backbone.View.extend({
initialize: function() {
log.debug(this.collection);
},
render: function() {
//var html = RowView(); // Looks like you're already placing a tbody as the container
//this.setElement(html);
this.collection.forEach(function( model ){
this.$el.append( RowView( model.toJSON() ) ); // Assuming RowView knows what to do with the model data
});
return this;
}
});
Then change the renderRowsSubView to
renderRowsSubView: function() {
// render rows
this.row = new RowsSubView({collection: this.opportunities});
this.row.render();
this.$el.find('tbody').append(this.row.$el.html());
}
For those that this might help, heres what I ended up with:
var RowsSubView = Backbone.View.extend({
initialize: function() {
},
render: function() {
var html = RowView({
opp: this.model.toJSON()
});
this.setElement(html);
return this;
}
});
var View = BaseView.extend({
id: 'wrapper',
className: 'container-fluid',
events: {
},
initialize: function() {
_.bindAll(this, 'render', 'add');
log.debug('Initialized Queue View');
this.opportunities = new Opportunities();
this.opportunities.on('add', this.add);
this.fetch();
},
add: function(row) {
this.row = new RowsSubView({model: row});
this.row.render();
$('tbody').append(this.row.el);
},
fetch: function() {
this.opportunities.fetch({
data: $.param({
$expand: "Company"
}),
success: function(response, options) {
// hide spinner
},
error: function(response) {
// hide spinner
// show error
}
});
},
render: function() {
var template = QueueView();
this.$el.html(template);
return this;
}
});
return View;
});
So my application below is actually firing "FIRE!" in the console twice on page load. Not sure why backbone is firing the url function twice when I am only seeing the one fetch being made. Any ideas as to why this might be causing it to fire twice?
window.ScheduleApp = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template($('#' + id).html());
};
//Define the Game Model.
ScheduleApp.Game = Backbone.Model.extend({
initialize: function() {
this.gameId = this.get('Id');
this.gameTime = this.get('Time');
}
});
//Define the Games Collection that contains Game Models.
ScheduleApp.Games = Backbone.Collection.extend({
model: ScheduleApp.Game
});
//Define the Day Model.
ScheduleApp.Day = Backbone.Model.extend({
initialize: function() {
this.games = new ScheduleApp.Games(this.get('Games'));
this.games.parent = this;
this.gameDayGDT = this.get('GeneratedDateTime');
this.gameDayDate = this.get('Date');
}
});
//Define the Days Collection that contains the Day Models.
ScheduleApp.Days = Backbone.Collection.extend({
model: ScheduleApp.Day,
url: function() {
console.log('FIRE!');
return '/js/test.json'
},
parse: function(data) {
var parsedSchedule = JSON.parse('[' + data.STUFF + ']');
return parsedSchedule;
}
});
ScheduleApp.DayCollectionView = Backbone.View.extend({
el: '.container', //Container where the views get rendered to.
initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
},
render: function(event) {
if (this.collection.length === 0) {
$('.container-hidden').show();
}
//Cycle through collection of each day.
this.collection.each(function(day) {
var dayView = new ScheduleApp.DayView({
model: day
});
this.$el.append(dayView.render().el);
}, this);
return this;
}
});
ScheduleApp.DayView = Backbone.View.extend({
tagName: 'div',
className: 'game-date',
template: _.template($("#gameSchedule").html(), this.model),
initialize: function() {
this.listenTo(this.model, "reset", this.render);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var daysList = new ScheduleApp.Days();
daysList.fetch({
reset: true,
update: true,
cache: false,
success: function(collection, response) {
//console.log(collection);
},
error: function(model, resp) {
// console.log('error arguments: ', arguments);
// console.log("error retrieving model");
}
});
//create new collection view.
var daysCollectionView = new ScheduleApp.DayCollectionView({
collection: daysList
});
All models belonging to a collection build their URLs based on the collection URL, as stated here. My guess would be that your collection is calling the method once, then your model / models place the second call, in order to build the model URL.
Then again, this method seems pretty harmless to me: it's just a getter. I'd rather place the console.log call in the Collection#parse or Model#initializer methods, and count how many times it gets invoked there.
I'm relatively new to Backbone and though I know the general idea of how to use it, my learning has been rapid and I'm probably missing some key elements.
So I have a collection that contains an attribute called "type" which can be article, book, video, class. I have the view rendering and everything but I need to be able to filter the collection when links are clicked.
My question is - how can I get it to filter down the collection and still be able to refilter the original collection when I click on another type?
Here's the gist of my code, I simplified it for easy reading:
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filter: function() {
//filtered does work correctly the first time I click on it but not the second.
var filtered = this.collection.where({'type':filter});
this.collection.reset(filtered);
}
});
update: I managed to get this working. I ended up triggering a filter event.
var TagsCollection = Backbone.Collection.extend({
initialize: function(model, options) {
this.query = options.query;
this.fetch();
},
url: function() {
return '/api/assets?tag=' + this.query;
},
filterBy: function(filter) {
filtered = this.filter(function(asset) {
return asset.get('type') == filter;
});
this.trigger('filter');
return new TagsCollection(filtered, {query: this.query});
},
model: AssetModel
});
And then in my view, I added some stuff to render my new collection.
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('filter sync', this.filterTemplate, this);
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filterCollection: function(target) {
var filter = $(target).text().toLowerCase().slice(0,-1);
if (filter != 'al') {
var filtered = this.collection.filterBy(filter);
} else {
this.render();
}
},
filterTemplate: function() {
filterResults = new TagsCollection(filtered, {query: self.apiQuery});
console.log(filterResults);
$('.asset').remove();
filterResults.each(function(asset,index) {
dust.render('dust/academy-card', asset.toJSON(), function(error,output) {
self.$el.append(output);
});
});
},
});
The reason it's not working a second time is because you're deleting the models that don't match your filter when you call reset. That's normal behaviour for the reset function.
Instead of rendering with the view's main collection, try using a second collection just for rendering which represents the filtered data of the original base collection. So your view MIGHT look something like:
var TagsView = Backbone.View.extend({
filter: null,
events: {
'click .filter-button': 'filter'
},
initialize: function (query) {
this.baseCollection = new TagsCollection([], {query: self.apiQuery} );
this.baseCollection.on('reset sync', this.filterCollection, this);
this.collection = new Backbone.Collection;
this.collection.on('reset', this.render, this);
},
render: function () {
var self = this,
data = this.collection.toJSON();
// This renders all models in the one template
dust.render('some-template', data, function (error, output) {
self.$el.append(output);
});
},
filter: function (e) {
// Grab filter from data attribute or however else you prefer
this.filter = $(e.currentTarget).attr('data-filter');
this.filterCollection();
},
filterCollection: function () {
var filtered;
if (this.filter) {
filtered = this.baseCollection.where({'type': this.filter});
} else {
filtered = this.baseCollection.models;
}
this.collection.reset(filtered);
}
});
To remove any filters, set a button with class filter-button to have an empty data-filter attribute. collection will then be reset with all of baseCollection's models
Here's a better answer to this. Instead of making it so complicated, you can just use the where method. Here's my replacement solution for the question above.
filterby: function(type) {
return type === 'all' ? this : new BaseCollection(this.where({type: type});
});
You can try using comparator function of your Collection.
http://backbonejs.org/#Collection-comparator
Basically its is like sorting your collection.
I am getting an
Object function (a){return new n(a)} has no method 'has'
error on calling the fetch() method on my model. Heres the code:
var Exercise = Backbone.Model.extend({
defaults: {
idAttribute: 'e_id',
e_id: "-1",
exerciseName: "Exercise",
exerciseDescription: "Address",
exerciseURL: "vimeo.com",
reps: "0",
sequence: "0"
},
initialize: function() {
alert("Exercise!");
}
});
var ExerciseList = Backbone.Collection.extend({
url: "/getWorkoutList.php",
model: Exercise,
initialize: function() { }
});
var Workout = Backbone.Model.extend({
urlRoot: "/getWorkoutList.php",
url: function() {
return this.urlRoot + "?workoutID=" + this.get('workoutId');
},
defaults: {
idAttribute: 'workoutId',
workoutId: "-1",
workoutName: "WorkoutName",
workoutDescription: "WorkoutDescription",
exercises: new ExerciseList()
},
initialize: function() {
_.bindAll(this);
directory.renderWorkout(this);
},
parse: function(response) {
return response;
}
});
var WorkoutList = Backbone.Collection.extend({
url: "/getWorkoutList.php",
model: Workout,
initialize: function() {
_.bindAll(this);
},
parse: function(response) {
return response;
}
});
var WorkoutView = Backbone.View.extend({
tagName: "div",
className: "workout-container",
template: $("#tmp-workout").html(),
initialize: function() {
_.bindAll(this);
this.model.bind('change', this.render, this);
},
render: function(){
console.log("WorkoutView");
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
},
//add ui events
events: {
"click #workout-details": "getWorkoutDetails"
},
getWorkoutDetails: function (e) {
e.preventDefault();
this.model.fetch();
}
});
var ExerciseView = Backbone.View.extend({
tagName: "exercise",
className: "exercise-container",
template: $("#tmp-exercise").html(),
initialize: function() {
_.bindAll(this);
alert("ExerciseView");
},
render: function(){
console.log("render exercise view");
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
var WorkoutListingView = Backbone.View.extend({
el: $("#workouts"),
initialize: function() {
var collection = new WorkoutList();
collection.fetch();
},
render: function() {
var that = this;
_.each(this.collection.models, function(item){
that.renderWorkout(item);
});
},
renderWorkout: function(item) {
var workoutView = new WorkoutView({
model:item
});
this.$el.append(workoutView.render().el);
var that = this;
_.each(workoutView.model.get('exercises').models, function(exercise) {
that.renderExercise(exercise);
});
},
renderExercise: function(item) {
var exerciseView = new ExerciseView({
model:item
});
this.$el.append(exerciseView.render().el);
}
});
Everything works fine when I am retrieving the Workout Collection the fist time. However, when I call getWorkoutDetails, I get the error. By inserting alerts and console.logs in parse() of Workout Model, I've found out that it does get the correct response from server, but for some reason, its giving this error.
Any ideas? Thanks.
OK, after spending a lot of time in the beautiful world of minified javascript spaghetti, I found out that the underscore.js version I was using didnt had the function 'has' in it. Updating underscore.js from 1.2.2 to 1.4.4 solved the problem. Also, my backbone.js version is 0.9.1