Backbone.RelationalModel using requireJs - backbone.js

I would like to use RelationalModel using requireJs.
Here my code(*)
When I run my module, I get the following warning message:
Relation=d;
no model, key or relatedModel (function (){a.apply(this,arguments)},
"tasks",
undefined).
My questions are:
1) what does the warning message means?
2) relatedModel and collectionType are well defined in my relations or should I export the model and the collection in define call?
(*)
define([
'backbone',
'relationalModel'
], function (Backbone) {
"use strict";
var User = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'tasks',
relatedModel: 'Task',
collectionType: 'TaskCollection',
reverseRelation: {
key: 'hasUser',
includeInJSON: 'id'
// 'relatedModel' is automatically set to 'User'; the 'relationType' to 'HasOne'.
}
}]
});
return User;
});

Look at this question: Creating nested models with backboneJS + backbone-relational + requireJS
By the way, exports.ModuleModel = ModuleModel; doesn't work for me. Instead, I use window.ModuleModel = ModuleModel. Yes, it's a bit ugly, but it works.

Related

Backgrid:render isn't being called

Backgrid is rendering
<table class="backgrid"></table>
but nothing else. Breakpoints in Backgrid:render() are not reached. I'm a Backbone newbie adapting someone else's code and so am not sure exactly what should be happening but LayoutManager:render() is called..it just never seems to get to Backgrid... The data I want to display are being fetched and look as if they are in the right format...but have to admit that it's difficult to tell once they've been wrapped up in a Backbone collection. Any pointers for how to debug/why Backgrid's render is not being called gratefully received.
Code below:
ListenView.js
define([
'backbone',
'underscore',
'backgrid',
'app/models/PersonModel',
'app/collections/PersonCollection',
'app/views/PersonListView',
'hbs!app/templates/listen_template'
],
function(
Backbone,
_,
Backgrid,
Person,
PersonCollection,
PersonListView,
listenTemplate
) {
App = window.App || {};
var ListenView = Backbone.View.extend({
template: listenTemplate,
initialize: function(params) {
//fetch the list of seen people
this.model.attributes.seenPeople.fetch( {
success: function(coll, resp) {
//console.log(coll);
}
});
},
afterRender: function() {
//initialise person view
console.log("creating Backgrid");
this.seenPeopleView = new Backgrid.Grid({
columns: [{
name: "email",
label: "Email",
cell: "string"
},{
name: "id",
label: "ID",
cell: "integer"
}, {
name: "title",
label: "Title",
cell: "string" }
],
collection: this.model.attributes.seenPeople
});
this.seenPeopleView.render();
$('#seen-people-list').append(this.seenPeopleView.el);
}
On the success method from the fetch you should call afterRender.
var self=this;
this.model.attributes.seenPeople.fetch( {
success: function(coll, resp) {
self.afterRender();
}
});
Instead of creating backgrid instance in view (this.seenPeopleView) create instance as
var grid = new Backgrid.Grid({...<your columns and collection related code>..});
Then Render the grid and attach the root to your HTML document as
$('#seen-people-list').append(grid.render().el);
Hope it will work :)

Listen in Backbone view to nested Backbone Relational model events

Say I have a Backbone Relational model representing a crowd - it has a related collection of People, each of model type Person:
CrowdModel = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'People',
relatedModel: 'PersonModel',
collectionType: 'Backbone.Collection'
}]
});
Each Person has a related collection of Children, a child also being of model type Person:
PersonModel = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'Children',
relatedModel: 'PersonModel',
collectionType: 'Backbone.Collection'
}]
});
Now, in my view, I'd like to listen to a change event - let's call it "Hungry" - on any of the models.
CrowdView = Backbone.View.extend({
initialize: function () {
this.listenTo(this.model.get("People"), 'change:Hungry', this.hungryChange);
},
hungryChange: function(model, val, options) {
if(val) console.log("Somebody's hungry!");
}
});
This will fire if any of the People are hungry, but I'm looking to find a way in the view to also listen for the hungry event on any of the Children. Is there some way of bubbling this up?
jsfiddle here: http://jsfiddle.net/fnj58/1/
Sorry for the contrived example - this really has to do with a model representing a tree of checkboxes, but it's easier to describe this way.
You should bubble the event from the Person.
Add handler for childHungry in CrowdView init function
CrowdView = Backbone.View.extend({
....
initialize: function () {
this.listenTo(this.model.get("People"), 'childHungry', function(){
console.log("Someone's child is hungry");
});// listening to child hungry
this.listenTo(this.model.get("People"), 'change:Hungry', this.hungryChange);
},
});
Person model should listen to his children and trigger that he has a child hungry
PersonModel = Backbone.RelationalModel.extend({
....
initialize: function(){
this.listenTo(this.get("Children"), 'change:Hungry', this.childHungry);
},
childHungry: function(){
this.trigger("childHungry");
}
By the Way: if you don't want the Crowd to distinguish between a child hungry or a person hungry you could also just trigger change:Hungry on the above childHungry function and keep your versiono CrowdView (See http://jsfiddle.net/fnj58/2/)

Backbone collection.findWhere issue - Erroring on collection

I am writing a backbone (with require) application and need to search through a collection to pull out the first model (I'm using a unique id so there will only be one).
The issue I'm having is that I'm getting an error:
Uncaught TypeError: Object [object Object] has no method 'findWhere'
When it get to the line with the findWhere command.
The view initialization is:
initialize: function (models) {
this.locationCollection = new LocationCollection();
this.locationCollection.fetch();
this.render();
},
I then access the locationCollection later in another method, the first line of the method is where the error occurs:
createCrate: function (eventname) {
var setLocationList = this.locationCollection.findWhere({ Name: $('#location').val() });
console.log($('#location').val());
console.log(setLocationList);
},
Here is the declaration code the LocationCollection:
define([
'jquery',
'underscore',
'backbone',
'model/LocationModel'
], function ($, _, Backbone, LocationModel) {
var LocationCollection = Backbone.Collection.extend({
model: LocationModel,
url: "/api/locations"
});
return LocationCollection;
});
I can access the items in this.localCollection elsewhere in the view and output them into a backbone typeahead extension, so the collection has been populated.
Any idea why this collection cannot call findWhere?
_.findWhere was introduced in Underscore 1.4.4
and Backbone proxied it in Backbone 1.0
Make sure you have the adequate versions and your error should go away.

Backbone Association not working with fetch

I am using Backbone-Associations from
github-Backbone associations
https://github.com/dhruvaray/backbone-associations
for nested models to work with backnone.
I am getting an error while calling fetch() on a model. the response json from server exactly matches the model definitions. i cant figure out what i am missing...
Versions:
Backbone: 0.9.2
Backbone-associations: 0.2.0
TypeError: this.attributes[relationKey].off is not a function
this.attributes[relationKey].off("all");
//this is the code i am using..
var ProductVariation = Backbone.AssociatedModel.extend({
defaults: {
ImageUrl:'',
Desc:''
}
});
var Product = Backbone.AssociatedModel.extend({
relations: [{
type: Backbone.Many,
key: 'ProductVariations',
relatedModel: ProductVariation
}],
url: '/GetProductById',
defaults: {
CategoryId: 0,
ProductVariations: [],
Summary: ''
}
});
am i missing something? any help appreciated...
Oops it's my mistake...
Actually I was using an old version of BackboneJS, but thinking that I was using the latest..
Which doesn't have off and on methods on a collection, instead they use bind and unbind respectively.
Replacing the old one with new one is the fix.

Creating nested models with backboneJS + backbone-relational + requireJS

I am new to BackboneJS and I am stuck with nested relations using Backbone-relational Model with RequireJS -I think I runned into circular issues. Any help will be highly appreciated!
I have the following model and collection:
/* Module Model at models/module*/
define([
'jquery',
'underscore',
'backbone',
'backboneRelational',
], function($, _, Backbone) {
var ModuleModel = Backbone.RelationalModel.extend({
urlRoot: 'api/module',
_radius: 50,
relations: [{
type: Backbone.HasMany,
key: 'children',
relatedModel: 'ModuleModel',
collectionType: 'ModuleCollection',
reverseRelation: {
key: 'parent_id',
includeInJSON: 'id'
}
}],
url: function() {
return this.id? 'api/module/' + this.id : 'api/module';
}
});
return ModuleModel;
});
/* Module Collection */
define([
'jquery',
'underscore',
'backbone',
'models/module'
], function($, _, Backbone, ModuleModel) {
var ModuleCollection = Backbone.Collection.extend({
model: ModuleModel,
url: 'api/modules'
});
return ModuleCollection;
});
When I initialize the object ModuleModel, it throws the following error:
Relation=child; no model, key or relatedModel (function (){ parent.apply(this, arguments); }, "children", undefined)
Could you point me to the right direction?
This looks like a scoping issue. During initialization of ModuleModel it wants to create a hasMany relation with itself, but it can't find itself and it will give you grief in form of said error:
http://jsfiddle.net/yNLbq
Once the object is reachable from the current scope things start to work out:
http://jsfiddle.net/jDw5e
A possible solution would be to give models and collection a namespace for themselves which can be reached from the current scope.
Hope this helps.
I came across this problem from here:
RequireJS + BackboneRelational + Self-Referential. He seems to have inherited some of his problems from this thread so I thought I might add my dime.
First, since you're using RequireJS, there are no global variables. You can't simply supply the name of the object, you need to supply actual object references for relatedModel and collectionType.
Your trickiest issue is that ModuleModel's relatedModel is actually ModuleModel itself, which won't be defined when you assign it to relatedModel (using the AMD model). You have to defer assignment until after ModuleModel is assigned.
Finally, you need to resolve the circular reference. dokkaebi is on the right track when he suggests using exports, but his implementation actually misuses exports. When exporting, attach the object directly to exports as he suggests, but when you import it you need to reference the module to use it, not exports.
This should work:
ModuleModel.js
define(['exports', 'ModuleCollection'], function (exports, Module) {
'use strict';
var ModuleModel = Backbone.RelationalModel.extend({
urlRoot: 'api/module',
_radius: 50,
relations: [{
type: Backbone.HasMany,
key: 'children',
// ModuleModel is undefined; this line is useless
// relatedModel: ModuleModel,
// no globals in require; must use imported obj ref
collectionType: Module.Collection,
reverseRelation: {
key: 'parent_id',
includeInJSON: 'id'
}
}],
url: function() {
return this.id? 'api/module/' + this.id : 'api/module';
}
});
// Now that `ModuleModel` is defined, we can supply a valid object reference:
ModuleModel.prototype.relations[0].relatedModel = ModuleModel;
// Attach `Model` to `exports` so an obj ref can be obtained elsewhere
exports.Model = ModuleModel;
});
ModuleCollection.js
define(['exports', 'ModuleModel'], function(exports, Module) {
'use strict';
var ModuleCollection = Backbone.Collection.extend({
// must reference the imported Model
model: Module.Model,
url: 'data.php' // <-- or wherever
});
// Attach `Collection` to `exports` so an obj ref can be obtained elsewhere
exports.Collection = ModuleCollection;
});
Main.js
define(['ModuleCollection'], function(Module) {
'use strict';
var modules = new Module.Collection();
modules.fetch().done(function() {
modules.each(function(model) {
console.log(model);
});
});
});
From a comment in backbone-relational.js v0.5.0 (line 375):
// 'exports' should be the global object where 'relatedModel' can be found on if given as a string.
If you require the special 'exports' value as a dependency in your define call, and then place your module onto the exports object before you return, then you can reference that module as a string or as a member of exports.
in ModuleModel.js:
define(['exports', 'use!backbone', 'use!backbone-relational'], function(exports, Backbone) {
var ModuleModel = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasMany,
key: 'groups',
relatedModel: 'ModuleModel',
collectionType: 'ModuleCollection'
}
]
});
exports.ModuleModel = ModuleModel;
return ModuleModel;
});
and in ModuleCollection.js:
define(['exports', 'use!backbone'], function(exports, Backbone) {
var ModuleCollection = Backbone.RelationalModel.extend({
url: '/api/v1/module/';
model: exports.ModuleModel;
});
exports.ModuleCollection = ModuleCollection;
return ModuleCollection;
});
I ran into the same problem a while back and followed the approach used by Andrew Ferk for his question: Backbone-relational submodels with RequireJS. The problem arises because you're defining models as Require modules so they don't exist on the global scope, where backbone-relational can look for them. Instead of using global scope (beats the purpose of Require) or exports (bit tricky with the relations), you can define a scope for your models and tell backbone-relational to look for models in it, with addModelScope().
//modelStore.js - A scope in which backbone-relational will search for models
//Defined separately so you can access 'modelStore' directly for your models instead of requiring 'app' every time.
define(['app'], function(app) {
app.modelStore = {};
Backbone.Relational.store.addModelScope(app.modelStore);
return app.modelStore;
}
By the way you should shim your Backbone dependency and not need to require jQuery and Underscore for it.
//ModuleModel (The ModuleModel module. Nice.)
define(['modelStore', 'backbone', 'backboneRelational'], function(modelStore, Backbone {
modelStore.ModuleModel = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasMany,
key: 'groups',
relatedModel: 'ModuleModel', //Not modelStore.ModuleModel
collectionType: 'ModuleCollection'
}
]
});
return modelStore.ModuleModel;
});
Kinda late for this now but hope it helps others.

Resources