Use same Backbone collection in nested object during parse [duplicate] - javascript

I'm setting up a nested categories structure in Backbone with RequireJS.
In this structure, a categories collection contains category models, and a single category model can contain a categories collection.
Unfortunately this seems to cause the dreaded circular dependencies problem in RequireJS. I have read the docs on RequireJS (http://requirejs.org/docs/api.html#circular) however I am finding the explanation with 'a' and 'b' confusing.
Here is my code, which is causing the problem:
define([
"jquery",
"underscore",
"backbone",
"collections/categories"
], function( $, _, Backbone, CategoriesCollection ) {
var Category = Backbone.Model.extend({
defaults: {
title: "Untitled"
},
parse: function(data) {
this.children = new CategoriesCollection( data.children, {parse: true} );
return _.omit( data, "children" );
}
});
return Category;
});
...
define([
"jquery",
"underscore",
"backbone",
"models/category"
], function( $, _, Backbone, CategoryModel ) {
var Categories = Backbone.Collection.extend({
model: CategoryModel
});
return Categories;
});
I am wondering if anyone who has experienced this before can help steer me in the right direction.
Thanks (in advance) for your help,

You just need to use require the collection again when you need it in the model, as the Collection passing initially can be undefined:
define([
"jquery",
"underscore",
"backbone",
"collections/categories"
], function( $, _, Backbone, CategoriesCollection ) {
var Category = Backbone.Model.extend({
defaults: {
title: "Untitled"
},
parse: function(data) {
if(!CategoriesCollection){
CategoriesCollection = require("collections/categories");
}
this.children = new CategoriesCollection( data.children, {parse: true} );
return _.omit( data, "children" );
}
});
return Category;
});
In the example they import also require but it should also work without the import.

For this you should consider to use a plugin like Backbone Relational.

Related

Calling Model from Collection

Collection
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
console.log("Loaded");
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://domain.com/api/jobs?page='+this.page+''
},
page: 1
});
return Jobs;
});
Model
define([
'underscore',
'backbone'
], function(_, Backbone){
var JobFilterModel = Backbone.Model.extend({
defaults: {
T: '1',
PT: '1',
C: '1',
I: '1'
}
});
// Return the model for the module
return JobFilterModel;
});
In one of my view , i SET the models
var jobListFilterModelUpdate = new JobListFilterModel();
jobListFilterModelUpdate.set({value:isChecked});
I'm trying to retrieve the MODEL from Collection so i can send the correct QUERY with the URL.
Question 1
How do i retrieve from Model from Collection
Question 2
Will the retrieved collection be the updated Model with the data i Set in View?
When you declare a Collection you need to specify a model property, something like :
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://punchgag.com/api/jobs?page='+this.page+''
},
page: 1,
model: JobFilterModel
});
After creating a new model you need to add it to collection (assuming you have jobsCollection created):
var jobListFilterModelUpdate = new JobListFilterModel();
jobListFilterModelUpdate.set({value:isChecked});
jobsCollection.add(jobListFilterModelUpdate);
Answer 1
You can retrieve a model from a collection based on id, collection.get(id). Here JobFilterModel doesn't seem to have any id (you can set idAttribute property of Model to create custom id property). Backbone also creates unique ids at client side but I don't know how would they be of any help to you. If you want to retrieve a model based on any of model's property you can use collection.findWhere() or collection.where().
Answer 2
Yes. It will be but it depends on how link your View to Collection.

Backbone.js render collection in View, but can't access data

I have a Backbone Collection that represent a json objects and I'm trying to render this objects in the View that would iterate them into template. I think the breakpoint was that i can't read the json array into array
The collection return array
{
"calls": [
{
"callId": "173",
"company": "Company 1",
"status": "open",
"DueDate": "2013-06-10 00:00:00",
"EmployeeId": "12"
},
{
"callId": "170",
"company": "company 2",
"status": "done",
"DueDate": "2013-05-27 14:27:37",
"EmployeeId": "11"
},
{
"callId": "169",
"company": "Company 3",
"status": "open",
"DueDate": "2013-05-20 00:00:00",
"EmployeeId": "11"
}
]
}
Route
// Filename: router.js
define([
'jquery',
'underscore',
'backbone',
'app/collections/schedule',
'app/views/schedule',
'app/views/dashboard'
], function($, _, Backbone, ScheduleCollection, ScheduleView, DashboardView) {
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'dash': 'defaultRoute',
'schedule': 'scheduleRoute',
'accounts': 'accountsRoute',
'reports': 'reportsRoute',
// Default
'*actions': 'defaultRoute'
},
scheduleRoute: function() {
// Create a new instance of the collection
// You need to set the url in the collection as well to hit the server
var schedulecollection = new ScheduleCollection();
// Pass in the collection as the view expects it
var scheduleview = new ScheduleView({
collection: schedulecollection
});
//scheduleview.initialize();
// No need of calling render here
// as the render is hooked up to the reset event on collection
},
defaultRoute: function(actions) {
// We have no matching route, lets display the home page
DashboardView.render();
}
});
var initialize = function() {
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
Collection
// Filename: collections/schedule
define([
'jquery',
'underscore',
'backbone',
'app/models/schedule'
], function ($, _, Backbone, ScheduleModel) {
var ScheduleCollection = Backbone.Collection.extend({
model: ScheduleModel,
url: "http://sam-client:8888/sam-client/i/schedule",
initialize: function () {
//console.log('schedule collections loaded successfully');
}
});
return ScheduleCollection;
});
View
// Filename: views/schedule
define([
'jquery',
'underscore',
'backbone',
'text!templates/schedule.html'
], function ($, _, Backbone, ScheduleTemplate) {
var scheduleView = Backbone.View.extend({
el: $("#test"),
initialize: function () {
// Listen to the reset event which would call render
this.listenTo(this.collection, 'reset', this.render());
// Fetch the collection that will populate the collection
// with the response
this.collection.fetch();
},
render: function () {
var data = {
schedule: this.collection.models,
_: _
};
var compiledTemplate = _.template(ScheduleTemplate, data);
this.$el.html(compiledTemplate);
}
});
return scheduleView;
});
in The template
<ul>
<% _.each(schedule, function(call){ %>
<li><%= call.get("company") %> - <%= call.get("DueDate") %></li>
<% }); %>
</ul>
The problem is there is no data passed in the template in order to iterate if it
The behavior of Collection#fetch changed in Backbone 1.0:
fetch collection.fetch([options])
[...] When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, in which case the collection will be (efficiently) reset.
So just this:
this.collection.fetch()
will trigger 'add', 'remove', and 'change' events in Backbone 1.0+ rather than a single 'reset' event like older versions of Backbone. If you say:
this.collection.fetch({ reset: true })
then you'll get your 'reset' event.
The next problem is your JSON format. Backbone collections expect the incoming data to be a simple array of model attribute objects but your JSON has the array nested inside "calls". You can provide a parse method in your collection to unwrap things:
parse: function(response) {
return response.calls;
}
You'd want that method inside your ScheduleCollection.

Backbone.localstorage not saving anything, with no errors

I'm trying to implement backbone.localstorage (https://github.com/jeromegn/Backbone.localStorage) in my application, but for some reason it's including it but not actually saving anything to localStorage, but it's also not throwing any errors either.
Here is my model code (I am using RequireJS):
define([
'backbone',
'common',
'localstorage'
],
function(Backbone, Common) {
//Define the App Namespace before anything else
var APP = Common.app_namespace || {};
APP.Models.ExampleModel = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("ExampleModel"),
//Set up default values
defaults: {
"user_name" : "awesome name",
"field 2" : "awesome field"
}
});
}
);
And here is where I'm calling it (using Marionette's regions):
var exModel = new APP.Models.ExampleModel({
"user_name" : "name!",
"field2" : "field!"
});
main_app_layout.header.show(new APP.Views.ExampleView({model: exModel}));
In terms of require, the page is loading, but I just don't think it's doing anything. Can anyone help?
I think you need to call the exModel.save() method for it to save to localStorage.

Backbone.js Model does not inherit from collection

I have a model called score and a collection called scores. However, the model does not seem to be inheriting the localStorage property (or for that matter, any property) from the parent collection. Am I missing something here?
Running Backbone with RequireJS.
models/score.js
define([
'underscore',
'backbone',
'localstorage'
], function(_, Backbone, Store){
var ScoreModel = Backbone.Model.extend({
defaults: {
board_id: null,
ns_pair: null,
ew_pair: null,
ns_score: null
},
validate: function(attrs, options){
if( isNaN(attrs.board_id) || attrs.board_id < 1 ){
return 'Invalid Board ID!';
}
},
localStorage: new Store("ScoreCollection")
});
return ScoreModel;
});
collections/scores.js
define([
'underscore',
'backbone',
'models/score',
'localstorage'
], function(_, Backbone, ScoreModel, Store){
var ScoreCollection = Backbone.Collection.extend({
model: ScoreModel,
localStorage: new Store("ScoreCollection")
});
return ScoreCollection;
});
main.js
require.config({
paths: {
// Major libraries
jquery: 'libs/jquery/jquery.min',
underscore: 'libs/underscore/underscore.min',
backbone: 'libs/backbone/backbone.min',
// Require.js plugins
text: 'libs/require/text',
// Backbone.js plugins
localstorage: 'libs/backbone/localstorage',
// Just a short cut so we can put our html outside the js dir
// When you have HTML/CSS designers this aids in keeping them out of the js directory
templates: '../templates'
}
});
// Let's kick off the application
require([
'app'
], function(App){
App.initialize();
});
Backbone Models don't inherit from Backbone Collections. They're just extensions of the base Backbone.Model that you 'extend' with your own properties and methods. Collections, the same deal. You can specify that a collection's model is based on a particular model you've defined so that when collections are added to it, it uses the model constructor to create instances for each model in that collection, but there is no direct inheritance relationship there. You can define a property on a model that happens to be an instance of a collection if that suits your needs.

can't fetch properly collection using backbone.js

Please read till the end (i make a reference to console.log at the end)
the model:
// Spot model:
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var Spot = Backbone.Model.extend({
url: function(){
if (this.get('id') !== null){
return '/spot/' + this.get('id');
}
return '/spots';
},
idAttribute: "id",
defaults: {
id: null,
details:{
name: "school",
type: 4,
rank: 3,
comment: null
},
location:{
address: '',
lat: 70.345,
lng: 90.123
},
user:{
id: 12345,
name: "magneto"
}
},
parse: function(response){
/* var attrs = {
details: {},
location: {},
user: {}
};
attrs.id = response.id;
attrs.details.name = response.details.name;
attrs.details.type = response.details.type;
attrs.details.rank = response.details.rank;
attrs.location.lat = response.location.lat;
attrs.location.lng = response.location.lng;
return attrs; */
}
});
return Spot;
});
the collection:
// Spots collection:
define([
'jquery',
'underscore',
'backbone',
'models/Spot'
], function($, _, Backbone,spot_model){
var Spots = Backbone.Collection.extend({
model: spot_model,
url:
function() {
return '/projects/FE/spots';
},
parse: function(response){
/* var parsed = [],
key;
_.each(response, function (value) {
parsed.push(value);
}, this);
return parsed;*/
},
comparator: function(spot){
return spot.rank;
}
});
return Spots;
});
the view:
// inside our view named: SpotsView
define([
'jquery',
'underscore',
'backbone',
'mustache',
'icanhaz',
'views/spots/Spot',
'collections/Spots',
'text!../../../../templates/spots/spots.mustache!strip',
], function($,
_,
Backbone,
mustache,
ich,
SpotView,
Spots,
SpotsTemplate){
var SpotsView = Backbone.View.extend({
//el: $("#container"),
tagName: "ul",
initialize: function(){
console.log('initialize view');
console.log(this.collection);
_.bindAll(this,'render');
},
render: function(){
console.log(this.collection);
console.log('length: ' + this.collection.length);
console.log('models: ' + this.collection.models);
_.each(this.collection.models, function (spot) {
$(this.el).append(new SpotView({model:spot}).render().el);
}, this);
return this;
}
});
return SpotsView;
});
inside our app.js
var spots = new Spots({model: Spot}),
spotsview;
spots.reset();
console.log(spots.fetch());
console.log('spots:');
console.log(spots.length);
console.log(spots);
spotsview = new SpotsView({collection: spots});
Server outputs
// SERVER output(s) i tried:
{"id": 666,"details":{"name": "mark","type":4,"rank":3.1,"comment":"nothing"},"location":{"lat":70.123,"lng":90.321,"address":"5th avenue"},"user":{"id":333,"name":"wolverine"}}
// another one i tried:
[{"id": 666,"details":{"name": "mark","type":4,"rank":3.1,"comment":"nothing"},"location":{"lat":70.123,"lng":90.321,"address":"5th avenue"},"user":{"id":333,"name":"wolverine"}}]
// another one:
[{"id":"55","details":{"name":"Dan","type":"6","rank":"-9.9","comment":"myEx"},"location":{"lat":"40.780346","lng":"-73.957657","address":"78, West 86th Stree"}},{"id":"57","details":{"name":"Ron","type":"6","rank":"3.0","comment":"Butch"},"location":{"lat":"40.715569","lng":"-73.832428","address":"1344 Queens Boulevard"}},{"id":"58","details":{"name":"Monk's","type":"11","rank":"9.5","comment":"yesss"},"location":{"lat":"40.805443","lng":"-73.965561","address":"112th and broadway "}}]
// without using "parse" method in the collection and in the model (they are commented) i get:
d
_byCid: Object
_byId: Object
length: 0
models: Array[0]
__proto__: x
// when not commented (parse in both collection and model) i get:
_byCid: Object
_byId: Object
length: 6
models: Array[6]
__proto__: x
// despite it says 6, in the view you can see there are lines:
//console.log(this.collection); <--- returns the abovee
//console.log('length: ' + this.collection.length); <-- returns 0
//console.log('models: ' + this.collection.models); <--- none
I also tried removing all properties defenitions from the model. Still didn't work.
The return value content type is: application/json (verified) and is valid json.
I've read:
Backbonejs collection length always zero
but inspite of console.log , showing 0 length , also:
for(var i=0; i< this.collection.length; i++){
console.log(this.collection.get(i));
}
doens't work!
also i've read
Did backbone collection automatically parse the loaded data
Thanks a lot
Update:
I've probably solved it: i've removed even the "parse" declarations from the model and the collection and It worked: length: 6 SpotsView.js models: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object] Either way i would like to know the proper use , if i did right and HOW to use PARSE in both situations the right way (collection and model wise), what do you return in both (!). Tnx
Collection.fetch is an asynchronous operation, therefore when you fetch a collection, the ajax call will be sent and then your code will continue being run just like nothing happened.
spots.reset(); // emptied your collections
console.log(spots.fetch()); // fetch your collection (why is this inside a console.log???)
console.log('spots:'); // log some text, this will be executed right after your fetch call has been made
console.log(spots.length); // here the fetch call probably hasn't returned yet, so returns 0
console.log(spots); // same as above, so returns an empty collection
spotsview = new SpotsView({collection: spots}); // the collection might or might not get populated by the time you get to rendering so there is probably variation from program run to another
So how to fix this? In the view bind the render function to the collection's reset event (launched after each successful fetch or forced reset). This way the view will render only when it has something to show.
// view's initialize method
initialize: function() {
...
this.collection.on('reset', this.render);
...
}

Categories

Resources