backbone.js change event only triggering the first once - backbone.js

I've got a backbone.js model which contains a calendar. The user can go back and forth in the calendar and I can get the events for the selected calendar day.
In my Model, I have
initialize: function(){
this.on('change:date',this.get_cal());
},
get_cal: function(){
alert('get calendar');
this.fetch(...
}
and in my view I have
cal_date: function(move){
Myapp.cal.attributes.date.setDate(Myapp.cal.attributes.date.getDate()+move);
}
when the date changes, I expected backbone to trigger the change event, and get the calendar events for the new date. Unfortunately, that isn't happening.
I've also tried putting the printed date into the model as
Myapp.cal.set({print_date: formatted_date});
thinking that maybe backbone is missing the update because I'm not calling 'set', or because it sees a date object and thinks that it already had a date object and therefore didn't change.
I've also tried to trigger the change with Myapp.cal.trigger('change'), in the view but that didn't work either. Nor did removing the calendar events by calling Myapp.cal.cal_events.refresh() where cal_events is the collection holding the days events.
Do you see what's wrong here?

I think the issue is with the parentheses after get_call on this line:
this.on('change:date',this.get_cal());
You should remove them because they call get_call right in initialize instead of making them an event handler.

Related

Dynamically change DateInterval in Dojo Calendar

Sorry for the mistakes, I'm French.
I use the Dojo calendar and there are some buttons "Today" "4 Days", "Week", "Month"... Is there a javascript function which do like if I clicked on one of these buttons, and thus changes the DateInterval property ? Or there is an other way to do that ?
Thank your for your help.
So if I understand correctly, you want to change the date interval without having to click one of these buttons?
You can do that with the dateInterval property which accepts, according to the API documentation, the values: day, week and month (I have no clue how to get the "4 weeks").
This means you can change them by using:
registry.byId("myCalendar").set("dateInterval", "day");
I also made an example JSFiddle (doesn't render properly, but the button does what you want). You can of course change the initiator (I created a seperate button), but it can be anything.
EDIT: As requested in the comments, you can determine which "view" is chosen by using the following event handler:
registry.byId("test").set('onTimeIntervalChange', function(e) {
console.log(e);
});
However, this event will always be executed when the time interval changes, so it will probably also be called when you click a custom button like in my previous example. I have no clue if there's an event handler more suitable to your situation, but the API documentation will probably be very helpful to you.
Anyways, I updated the JSFiddle which you can find here.
To get the four day calendar set this
registry.byId("myCalendar").set("dateInterval", "day");
and then this
registry.byId("myCalendar").set("dateIntervalSteps", 4);

Backbone model listener stops working as soon as the model is saved

In the initialize function of my backbone View, I created the following listener:
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
This successfully causes my saveChanges function to be called the first time a model is added/removed from the 'clusters' Collection. There's only one line in that saveChanges function:
this.model.save();
Once that is invoked, adding/removing clusters no longer invokes the "add" or "remove" event. Why would saving the model destroy the listener? Can that be prevented, or is there a way to re-establish the listener?
Or is there something fundamental I'm not understanding about Models and/or Collections...?
Let's break down your code:
You write
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
Which is equal to
var clusters = this.model.get('clusters');
this.listenTo(clusters, 'add remove', this.saveChanges);
Now I only assume that after you get that event you set() a new clusters object inside your model.
The issue here is that your view still listens to events from that same old clusters object, which is not relevant anymore - your model deals with another object!
Another case could be that Backbone clears your view's event handlers from its model when it's being removed.. Could easily answer for sure if you'd share the whole code.

Trigger backbone.js change when value doesn't change

It's a known feature of backbone.js that when you set data that hasn't changed it won't fire the change event, nor will it go through validations. I however need the change event to fire as I'm storing a JSON response from an AJAX call which stores results of backend validation. If the user keeps submitting the form while leaving the same field empty, the backend validation will return the same JSON result and when I save it to the model it won't trigger the change event.
A few things I've tried within the AJAX success callback where I set the data into the model:
Attempted Solution #1
t.model.unset('fieldErrors',{silent: true});
t.model.set({fieldErrors: JSONResponse});
Attempted Solution #2
t.model.set({fieldErrors: null},{silent: true});
t.model.set({fieldErrors: JSONResponse});
Neither of these results in the change event firing a second time when the call is made and the user has the same JSONResponse.
Manually trigger the change event:
t.model.trigger('change', t.model);
or
t.model.trigger('change:fieldErrors', t.model, newFieldErrorsValue);
this.model.set({fieldErrors: JSONResponse}, {silent:true});
this.model.trigger('change:fieldErrors');
see this conversation:
Can I force an update to a model's attribute to register as a change even if it isn't?

backbone model with an array/object property: infinite 'change' event triggered after sync()?

My backbone.js model has an array property. I bound the change event to save().
After sync() (triggered by save(), my app server returns an identical JSON, but backbone thinks the array has been changed (due to a different reference to the array I guess?), and trigger changes again. Then an infinite loop occurs.
save() -> sync() -> triggered `change` -> save()...
What shall I do?
Idea: I can bind the change event to a function that checks if the changed attributes are of type object/array, and do a deep comparison and call save only if the array/object really changed. If true then save()?
Thanks!
Try the Edge version of Backbone (master branch) this behavior changed after 0.9.9 - see https://github.com/documentcloud/backbone/pull/2004
Backbone has a special option on many methods to prevent just this sort of issue: silent:true. If you pass that option to your save method, the resulting sync won't trigger a change event.
So, if you want to set your change event handler to save silently, something like:
changeHandler: function() {
this.save({silent:true});
}
should do the trick.

Does backbone trigger .add when a collection is .fetched?

I am converting my backbone app so that it starts communicating with the server, previously I had just been populating the collection with test data using .add()
I have tied some events to the collections add event. So every time an item is added to the collection I can render the view and update some statistics.
it appears that as soon as i add the .fetch() call to get data from the server the add events stop.
for example
var PayableCommitmentCollection = Backbone.Collection.extend({
model:PayableCommitment,
url:"/cc/account/contributions/",
initialize: function() {
this.bind("add",this.setInitialAmount,this);
}
}
this.SetInitialAmount() is never called after fetch creates the models in the collection.
I also have 2 views that are watching for items to be added to this collection that are now not updating.
My obvious work around is to write my own AJAX call so that I can add the items the same way I have been during development so far, however I'm sure backbone has the smarts to help me out here.
Can anyone suggest a way i can bind to the completion of fetch, or to make it stimulate the add event.
The fetch method accepts a hash of options. One of these options can be the "add" option, which calls add on the collection instead of reset.
collection.fetch({ add: true });
From the fine manual:
fetch collection.fetch([options])
Fetch the default set of models for this collection from the server, resetting the collection when they arrive.
And a reset:
reset collection.reset(models, [options])
Adding and removing models one at a time is all well and good, but sometimes you have so many models to change that you'd rather just update the collection in bulk. Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset" event at the end.
So a fetch will trigger a single "reset" event rather than a bunch of "add" events. You need a collection-wide version of setInitialAmount that you can bind to "reset".
In Backbone 1.0, Collection#fetch has this to say:
fetch collection.fetch([options])
Fetch the default set of models for this collection from the server, setting them on the collection when they arrive.
[...]
The behavior of fetch can be customized by using the available set options. For example, to fetch a collection, getting an "add" event for every new model, and a "change" event for every changed existing model, without removing anything: collection.fetch({remove: false})
So, if you're using 1.0+ then all you need to do is call your fetch with the remove: false option.

Resources