In the following example how would I access the model who triggered the "change:guid" event in the callback function?
Entry = Backbone.Model.extend({
defaults: {
db: 'not specified',
guid: 'not specified'
},
initialize: function ()
{
this.on("change:guid", function (e) {
alert(" guid changed!!!!!");
//access model here
});
}
});
"e" should be the model. According to the documentation the following parameters can be used in the callback:
"change:[attribute]" (model, value, options)
http://backbonejs.org/#Events-catalog
Example:
Entry = Backbone.Model.extend({
defaults: {
db: 'not specified',
guid: 'not specified'
},
initialize: function () {
this.on("change:guid", function (model, value, options) {
console.log(model);
})
this.set('guid', 123);
}
});
var entry = new Entry()
If you take a look at the console, there will be the model.
Try it:
http://jsfiddle.net/r7hXS/
Related
The thing is that I have a circular dependecy between some Backbone modules so I have to use "exports" as Requirejs scpecifies in its documentation http://requirejs.org/docs/api.html#circular. So the model 'A' will look like this:
define(function(require, exports) {
var B = require('B');
var A = Backbone.Model.extend({
});
exports.model = A;
});
And the collection 'B' like this:
define(function(require, exports) {
var A = require('A');
var B = Backbone.Model.extend({
model: A.model
});
exports.model = B;
});
The problem here is that by the time I have to specify the collection 'B' model property, the model 'A' isn't yet defined. This is the error I'm getting when I try to set the collection with models like this:
B.collection.set([{id: 1}, {id: 2}]);
Uncaught TypeError: 'undefined' is not an object (evaluating 'targetModel.prototype') (http://127.0.0.1:9999/bower_components/backbone/backbone.js:689)
Any ideas on how should I solve this problem?
From the example, it's not clear that B actually depends on A. If it's just a model:collection relationship, it might make sense to remove the dependency of the model on its collection. If it's at all possible to break the circular dependency, I would strongly encourage you to do so.
If the back-reference is truly required, though, one option might be to move the resources into the same module and do a sort of lazy export:
define(function() {
var lazyThings = {
A: null,
B: null
};
lazyThings.A = Backbone.Model.extend({
collection: things.B
});
lazyThings.B = Backbone.Collection.extend({
model: A
});
return lazyThings;
});
Alternatively, you could return lazyThings.B and later access the model from its prototype:
require('b', function (B) {
var A = B.prototype.model; // A
});
Finally, requirejs could be made to work by calling the respective dependencies lazily (i.e., after the modules are resolved):
// B
define(['a'], function (A) {
return function () {
return Backbone.Collection.extend({
model: A()
});
}
});
// A
define(['b'], function (B) {
return function () {
return Backbone.Model.extend({
model: B()
});
}
});
The following works for me, try to make it clear as possible.
You have a model, you have a collection. In order for them to both depend on each other + avoid a circular dependency, you need a 3rd "mediator" dependency. It's convenient in Backbone to have a model and easily lookup what collection it belongs to, and vice versa, but the problem of course is they have a circular dependency.
So before we had:
+model
+collection
__________
= circular
and after:
+model
+collection
+mediator
________
= OK
//collection
define([
'#allModels',
'#BaseCollection',
'#AppDispatcher',
'#allFluxConstants',
'app/js/flux/flux-helpers/collectionUpdater'
],
function (allModels, BaseCollection, AppDispatcher, allFluxConstants, collUpdater) {
var dispatchCallback = function (payload) {
return true;
};
var BaymaxComponentCollection = BaseCollection.extend({
model: allModels['BaymaxComponent'],
collectionName:'baymax-component',
url: '/baymax_component',
batchURL: '/batch/baymax_component',
initialize: function (models, opts) {
this.dispatchToken = AppDispatcher.register(dispatchCallback);
},
// collection is sorted by original insertion order.
comparator: 'order'
});
return new BaymaxComponentCollection();
});
//model
define([
'#BaseModel',
'#ModelCollectionMediator',
'#AppDispatcher'
],
function ( BaseModel, MCM) {
var BaymaxComponent = BaseModel.extend({
idAttribute: 'id',
urlRoot: '/baymax_component',
collectionName: 'baymax-component',
defaults: function () { //prevents copying default attributes to all instances of UserModel
return {}
},
initialize: function (attributes, opts) {
//*** the following line is crucial ***
this.collection = MCM.findCollectionByName(this.collectionName);
},
validate: function (attr) {
return undefined;
}
},
{ //class properties
});
return BaymaxComponent;
});
//mediator
define(function (require) {
return {
findCollectionByName: function (name) {
var allCollections = require('#allCollections');
return allCollections[name];
}
};
});
I have set up a simple model with 2 instance methods. How can I call those methods in lifecycle callbacks?
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
// Instance methods
doSomething: function(cb) {
console.log('Lets try ' + this.doAnotherThing('this'));
cb();
},
doAnotherThing: function(input) {
console.log(input);
}
},
beforeUpdate: function(values, cb) {
// This doesn't seem to work...
this.doSomething(function() {
cb();
})
}
};
It looks like custom defined instance methods were not designed to be called in lifecycle but after querying a model.
SomeModel.findOne(1).done(function(err, someModel){
someModel.doSomething('dance')
});
Link to example in documentation - https://github.com/balderdashy/sails-docs/blob/0.9/models.md#custom-defined-instance-methods
Try defining the functions in regular javascript, this way they can be called from the entire model file like this:
// Instance methods
function doSomething(cb) {
console.log('Lets try ' + this.doAnotherThing('this'));
cb();
},
function doAnotherThing(input) {
console.log(input);
}
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
},
beforeUpdate: function(values, cb) {
// accessing the function defined above the module.exports
doSomething(function() {
cb();
})
}
};
doSomething and doAnotherThing aren't attributes, are methods and must be at Lifecycle callbacks level. Try something like this:
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
},
doSomething: function(cb) {
console.log('Lets try ' + "this.doAnotherThing('this')");
this.doAnotherThing('this')
cb();
},
doAnotherThing: function(input) {
console.log(input);
},
beforeCreate: function(values, cb) {
this.doSomething(function() {
cb();
})
}
};
On Second place, you're trying send to console this.doAnotherThing('this') but it is an instance of model so you can't pass it like parameter on the "Lets try" string. Instead of it try to exec this function apart and will work
In Backbonejs, is it possible to initialize event handlers in a loop even when the handlers make use of the functions in the outer model?
For e.g. how can I get something like the below to work?
var MyModel = Backbone.Model.extend({
eventHandlers: {
onerror: function(e) {
this.myErrorHandler(e); // doesnt work
},
},
initialize: function () {
var myObj = {}
_.each(eventHandlers, function(value, key) {
myObj[key] = value;
}, this);
},
myErrorHandler: function(e) {
console.error('my error handler', e);
}
});
How do I get the Model's error handler function (myErrorHandler) to be called from the event handlers that are declared in a sub object? Or is there some other way to achieve this?
TIA
Do you mean to execute each handler on event defined by its key, bound to the model instance?
var MyModel = Backbone.Model.extend({
eventHandlers: {
error: function(e) {
this.myErrorHandler(e); // doesnt work
}
},
initialize: function () {
var _this = this;
_.each(this.eventHandlers, function(value, key) {
_this.on(key, _(value).bind(_this));
});
},
myErrorHandler: function(e) {
console.error('my error handler', e);
}
});
But what if you want to have several handlers for the same event?
I have a server side class:TopicsListModel
with properties as follows:
public class TopicsListModel
{
public TopicsListModel()
{
ChildTopics = new HashSet<TopicsListModel>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<TopicsListModel> ChildTopics { get; set; }
}
There is a function which returns a List,
public JsonResult SearchTopic(String topic)
{
var topics = LandingManager.SearchTopics(topic);
//return Json(topics);
return new JsonResult { Data = topic, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
I need to add this to the Backbone model and collection. I am a newbie to Backbone and I am struggling as you can guess. Any help would be greatly appreciated.
I want to build a model which will store data like:
{ name: "Model1", id: 1, submodels: [{ name: "Submodel1", id: 2 }, { name: "Submodel1", id: 2 }] }
I am unable to do so, I am having trouble setting up the basic collection like that, the ASP.NET MVC code part which returns the data I have shared. Sharing whatever I have done in the Backbone:
TestBB = function () {
if (!window.console) window.console = { log: function () { } };
var treeModel = Backbone.Model.extend({});
var treeSubModel = Backbone.Model.extend({});
var treeCollection = Backbone.Collection.extend({
model: treeSubModel,
initialize: function () {
console.log('Collection Initialized');
}
});
var treeView = Backbone.View.extend({
el: $('#tree-view'),
initialize: function () {
// this.collection.bind("render", this.render, this);
// this.collection.bind("addAll", this.addAll, this);
// this.collection.bind("addOne", this.addOne, this);
_.bindAll(this);
},
render: function () {
console.log("render");
console.log(this.collection.length);
$(this.el).html(this.template());
this.addAll();
var template = _.template($("#template").html(), {});
this.el.html(template);
},
addAll: function () {
console.log("addAll");
this.collection.each(this.addOne);
},
addOne: function (model) {
console.log("addOne");
view = new treeView({ model: model });
$("ul", this.el).append(view.render());
},
events: { "click #btnFind": "doFind" },
doFind: function (event) { alert('event fired'); }
});
return {
TreeView: treeView
};} (Backbone);
Please suggest.
It isn't a case of 'binding' the collection to the back-end, as such: you fetch a collection from the server to read, and save (via sync) to write.
Firstly you'll need to let the collection know where the data resides by setting the url property. See: http://backbonejs.org/#Collection-url.
Secondly you'll need to actually retrieve the data using the fetch method. See http://backbonejs.org/#Collection-fetch. Note this is asynchronous, so you'll need to wait for the success callback.
To save data, use the save method on individual models. See http://backbonejs.org/#Model-save.
Solved
I figured out the approach and defined the model and populated it!
Is there a way to prevent backbone.js collection of removing my model when it gets detroyed?
Everytime it happens I receive a "remove" trigger event from collection
collection.on("remove", this.handleRemove)
already tried passing some arguments but nothing
model.destroy({silent: false, remove: false})
The solution is to override the Backbone model destroy function. I made this on an abstract model with success and callback strategy:
Parameter "data" corresponds to the original parameter "resp".
destroy: function(successCallback, errorCallback)
{
var options = { wait: true };
var model = this;
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
var destroy = function()
{
model.trigger('destroy', model, model.collection, options);
};
options.success = function(data)
{
if ('SUCCESS' === data.responseCode)
{
if (options.wait || model.isNew())
destroy();
successCallback(data);
if (!model.isNew())
model.trigger('sync', model, data, options);
}
else
{
errorCallback(data);
}
};
if (this.isNew())
{
options.success();
return false;
}
var xhr = this.sync('delete', this, options);
if (!options.wait)
destroy();
return xhr;
}