I defined the add handler on my collection:
var BookmarksCollection = Backbone.Collection.extend({
initialize: function() {
this.on('add', function(obj) {
console.log(obj.get('title') + ' added');
});
},
url: '/bookmarks',
model: Bookmark
});
And it's being called from one of my views:
var AddView = Backbone.View.extend({
el: $('#entry'),
events: {
'click #submit': 'submitHandler'
},
submitHandler: function(event) {
if($('input').val()) {
var newBookmark = new Bookmark;
newBookmark.set({url: $('input').val()});
console.log('Save!');
newBookmark.save({
success: function(newBookmark) {
console.log('Success!');
myBookmarks.add(newBookmark);
myBookmarks.trigger('add', newBookmark);
}
});
}
}
});
However the add handler on my collection is never triggered. In fact, not even the success function in the view seems to be called.
But the newBookmark gets saved anyway, so no errors happening.
What am I missing?
NodeJS respond with 201 (Created) when a new bookmark is posted.
Maybe backbone expects a 200 (OK) to trigger success?
EDIT: I tried triggering the event from outside the success function and it works.
How can I tweak backbone to react to a 201 (Created)?
I think the issue is that he first argument of save should be the data you are trying to save. The second is the options which can include the success and error callbacks.
newBookmark.save({dataiwanttosave: "hi"}, {
success: function(newBookmark) {
console.log('Success!');
myBookmarks.add(newBookmark);
myBookmarks.trigger('add', newBookmark);
}
});
Related
I`m building a simple backbone application, and have a problem with success callback function in my View.
Here is a code
var EditUser = Backbone.View.extend({
el: '.page',
render: function(option){
var that = this;
if(option.id){
that.user = new User({id : option.id});
that.user.fetch({
success:function(user){
var template = _.template($("#edit-user-template").html());
that.$el.html(template({user: user}));
}
});
}else{
var template = _.template($('#edit-user-template').html());
that.$el.html(template({user: null}));
}
},
events:{
'submit .edit-user-form': 'saveUser',
'click .delete': 'deleteUser'
},
saveUser: function(ev){
var userDetails = $(ev.currentTarget).serializeObject();
var user = new User();
user.save(userDetails,{success: function(){
router.navigate('',{trigger:true});
},
error: function(e){console.log(e);}
});
return false;
},
deleteUser:function(ev){
this.user.destroy({
success: function(){
router.navigate('',{trigger:true});
}
})
return false;
},
wait:true
});
On the SaveUser function,query send to the server correct, but after this, success callback function is not called, for navigating to the app home page.
The same problem appear with deleteUser method.
Any ideas what is the problem? Thanks!
It could be related to the response type from your server, the expected response is a JSON object that will be set on your attributes, but if the response is different as "text" for example, the parse fails.
Here is a fiddle for demo using Mock request
https://jsfiddle.net/gvazq82/rdLmz2L2/1/:
$.mockjax({
url: "hello.php",
responseTime: 0,
//responseText: 'A text response from mock ajax'
responseText: '{"a": "a"}'
});
In this example, the error function is been called that is not happening in your case, Is it possible your app defines some default behavior for "Ajax" calls?.
I need more information to be able to determinate this issue, but hope this give you some guidance with your problem.
In the function jsonRequest below, I can log this.model to the console, but I can't call this.model.fetch(), which I thought was the appropriate way to make a request to my server (at localhost:8080/jsonapi). The error it gives me is can't call fetch of undefined. Is there something I'm doing wrong in the code below?
var MyModel = Backbone.Model.extend({
url: 'jsonapi',
});
var MyView = Backbone.View.extend({
el: '#blahblah',
initialize: function(){
},
events: {
'click #start' : 'callJsonRequest',
},
callJsonRequest: function(){
setInterval(this.jsonRequest, 1000);
},
jsonRequest: function(){
console.log("jsonrequest", this.model); //this logs the model
this.model.fetch();
},
});
window.myModel = new MyModel();
window.startStop = new StartView({model: myModel});
You likely need to use bind to make sure this is the context of your View object.
As you mentioned in the comments, you can do:
setInterval(this.jsonRequest.bind(this), 1000);
Seems like this should be obvious, but there seem to be so many different examples out there, most of which cause errors for me, making me think they are out of date. The basic situation is that I have a MessageModel linked to a MessageView which extends ItemView, MessageCollection linked to a MessageCollectionView (itemView: MessageView). I have a slightly unusual scenario in that the MessageCollection is populated asynchronously, so when the page first renders, it is empty and a "Loading" icon would be displayed. Maybe I have things structured incorrectly (see here for the history), but right now, I've encapsulated the code that makes the initial request to the server and receives the initial list of messages in the MessageCollection object such that it updates itself. However, I'm not clear, given this, how to trigger displaying the view. Obviously, the model shouldn't tell the view to render, but none of my attempts to instantiate a view and have it listen for modelChange events and call "render" have worked.
I have tried:
No loading element, just display the CollectionView with no elements on load, but then it doesn't refresh after the underlying Collection is refreshed.
Adding modelEvents { 'change': 'render' } to the view --> Uncaught TypeError: Object function () { return parent.apply(this, arguments); } has no method 'on'
I also tried this.bindTo(this.collection..) but "this" did not nave a bindTo method
Finally, I tried, in the view.initialize: _.bindAll(this); this.model.on('change': this.render); --> Uncaught TypeError: Object function () { [native code] } has no method 'on'
Here is the code
Entities.MessageCollection = Backbone.Collection.extend({
defaults: {
questionId: null
},
model: Entities.Message,
initialize: function (models, options) {
options || (options = {});
if (options.title) {
this.title = options.title;
}
if (options.id) {
this.questionId = options.id;
}
},
subscribe: function () {
var self = this; //needed for proper scope
QaApp.Lightstreamer.Do('subscribeUpdate', {
adapterName: 'QaAdapter',
parameterValue: this.questionId,
otherStuff: 'otherstuff',
onUpdate: function (data, options) {
console.log("calling sync");
var obj = JSON.parse(data.jsonString);
self.set(obj.Messages, options);
self.trigger('sync', self, obj.Messages, options);
}
});
},
});
Views.MessageCollectionView = Backbone.Marionette.CollectionView.extend({
itemView: Views.MessageView,
tagName: 'ul',
// modelEvents: {
// 'change': 'render'
// },
onAfterItemAdded: function (itemView) {
this.$el.append(itemView.el);
}
});
var Api = {
subscribe: function (id) {
var question = new QaApp.Entities.Question(null, { id: id });
question.subscribe();
var questionView = new QaApp.Views.QuestionView(question);
QaApp.page.show(questionView);
}
};
I am very grateful for all the help I've received already and thanks in advance for looking.
Try this:
var questionView = new QaApp.Views.QuestionView({
collection: question
});
Backbone collection 0.9.9 - add event not work
After update backbone to 0.9.9, i have a problem with (add).
(function () {
var Model = Backbone.Model.extend({
initialize: function () {
console.log('initialize Model');
}
});
var Collection = Backbone.Collection.extend({
model: Model,
url: "/json.json",
initialize: function () {
console.log('initialize Collection');
}
});
var View = Backbone.View.extend({
initialize: function () {
console.log('initialize View');
}
});
var collection = new Collection([
{
"id" : "001",
"name" : "Дарья",
"text" : "1 Вопрос - Ответ 1"
}
]);
collection.on('add', function () {
console.log('edd event', this)
});
collection.fetch({
add: true,
//silent: false,
success: function (model) {
console.log('fetch')
}
});
}());
console.log('edd event', this) - not work (
in old versions it works
It would appear that the add option to collection.fetch is no longer supported.
From 0.9.2 source (collection.fetch):
collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
From 0.9.9 source (collection.fetch):
var method = options.update ? 'update' : 'reset';
collection[method](resp, options);
if (success) success(collection, resp, options);
So by default collection.fetch will cause a reset on the collection, and the resetevent will be fired. If you pass option update:true, and update will be performed.
According to documentation for the new collection.update method, update will trigger the add events for added models, so the following should work:
collection.fetch({
update: true,
success: function (model) {
console.log('fetch')
}
});
Just test and be aware, that the new update method will also trigger remove and changeevents, if models are removed or changed.
You need to remove the commented out line because it won't propagate the add events otherwise (see line 827 of the backbone.js source). So the following should work
collection.fetch({
add: true,
silent: false,
success: function (model) {
console.log('fetch')
}
});
I'm not sure if that's a change from previous versions :)
From what I gather from checking out the Backbone 0.9.9 source, the add -option does nothing with fetch unless you add in the update - option as well. source
So to do something useful with it do the following:
collection.fetch({
add: true,
update: true, // this is necessary as well
//silent: false,
success: function (model) {
console.log('fetch')
}
});
This is also the cause for your problem. When you fetch, the Collection automatically defaults to the reset -function after fetching. reset silences the add -events and opts to just trigger a reset event as is evident from the Backbone.js source
if (models) this.add(models, _.extend({silent: true}, options));
So use the update option if you want the add -events and do not want to empty the collection before adding the new models. If you must have the reset functionality AND the add event, then you might have to write some custom implementation of reset.
I have a collection and I need to access a model in the collection when a route is fired:
App.Houses = Backbone.Collection.extend({
model: App.House,
url: API_URL,
})
App.houseCollection = new App.Houses()
App.houseCollection.fetch();
App.HouseDetailRouter = Backbone.Router.extend({
routes: {
'': 'main',
'details/:id': 'details',
},
initialize: function() {
},
main: function() {
App.Events.trigger('show_main_view');
},
details: function(id) {
model = App.houseCollection.get(id);
console.log(model);
App.Events.trigger('show_house', model);
},
});
The result of that console.log(model) is undefined. I think that this is the case because the collection has not finished the fetch() call?
I want to attach the model to the event that I am firing so that the views that respond to the event can utilize it. I might be taking a bad approach, I am not sure.
One of the views that responds to the event:
App.HouseDetailView = Backbone.View.extend({
el: '.house-details-area',
initialize: function() {
this.template = _.template($('#house-details-template').html());
App.Events.on('show_house', this.render, this);
App.Events.on('show_main_view', this.hide, this);
},
events: {
'click .btn-close': 'hide',
},
render: function(model) {
var html = this.template({model:model.toJSON()});
$(this.el).html(html);
$(this.el).show();
},
hide: function() {
$(this.el).hide();
App.detailsRouter.navigate('/', true);
}
});
EDIT: Somewhat hacky fix: See details()
App.HouseDetailRouter = Backbone.Router.extend({
routes: {
'': 'main',
'details/:id': 'details',
},
initialize: function() {
},
main: function() {
App.Events.trigger('show_main_view');
},
details: function(id) {
if (App.houseCollection.models.length === 0) {
// if we are browsing to website.com/#details/:id
// directly, and the collection has not finished fetch(),
// we fetch the model.
model = new App.House();
model.id = id;
model.fetch({
success: function(data) {
App.Events.trigger('show_house', data);
}
});
} else {
// if we are getting to website.com/#details after browsing
// to website.com, the collection is already populated.
model = App.houseCollection.get(id);
App.Events.trigger('show_house', model);
}
},
});
Since you are using neither the callbacks nor events to know when the collection's fetch call completes, perhaps fetching the collection is generating an error, or the model you want is not included in the server response, or you are routing to the view before fetch has completed.
As to your approach, here are some miscellaneous tips:
better to pass the model to the view in the view's constructor's options parameter. render() takes no arguments and I think it is unconventional to change that.
Always return this from render() in your views
You can move your this.template = _.template code to the object literal you pass to extend. This code only needs to be run once per app load, not for each individual view
For now the simplest thing may be to instantiate just a model and a view inside your details route function, call fetch on the specific model of interest, and use the success callback to know when to render the view.