If I have done my homework correctly, I have come to learn that Backbone does not have built-in save event that is triggered when a model is saved using the model's save method (even though there is a destroy event).
I have also learned that Backbone has a nifty mechanism for creating custom events using the Backbone.Events object. Using the latter works, but I have the impression that it is not fine-grained enough for my needs.
My setup is as follows. I have a table (view) built up of rows (views) with each row having a reference to a model. When the model is saved, I'd like to update/render the row to reflect the changes.
How does one go about creating a save event that is triggered when a model is saved so that the table row (view) that has a reference to that model is updated?
In other words, I'd like to be able to do the following:
this.model.bind('save', this.render);
Just 3 days ago, a commit was made to Backbone that triggers a sync event when the model is successfully saved. This commit hasn't been release yet, though, so you will need to download the source code from the github account if you want to use it.
View = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'onModelSaved');
this.model.bind('sync', onSuccessCallback);
},
onModelSaved: function(model, response, options) {
//perform your after save logic
}
});
As of Backbone.js 1.0.0 you have sync event that is triggered if the model is saved successfully.
this.listenTo(this.model,'sync', this.render);
Note that, the change:attribute is fired first for relevant attributes if there is change in value of the attribute, followed by the change event then followed by the sync event.
sync event is fired irrespective of the change in model. It is to denote that the model is now in sync with server values.
Also these event fire only if the values are valid. i.e models.validate should not return any errors for these values got from the server.
Related
I am quite experienced with JS and Backbone, but want your help brainstorming a best practise solution to the following problem:
There are two views. They sit side-by-side running concurrently in the page.
Both of these views are passed the same instance of a model.
Both of these views have actions which can update the model.
How, using Backbone's event driven programming, do you think would be the best way of distinguishing the events fired from view to view.
A possible scenario could be:
The user performs an action which updates the model in an invalid way on View A. But as the user did not perform it on View B they should not be notified there.
You can use intermediary objects to manage the flow between a repository (the object where the connection to the database takes place) and the instantiation / manipulation of views.
Instead of binding a model to a view from within the view, you can let the intermediary object (general purpose controller) make verifications, if required manipulations, before re-rendering the view with the modified model or collection.
Through the use of an event aggregator, you can trigger events from views from within the view, and listen to the event from within the "controller" object, who will in turn perform the required custom actions.
Using custom events seems simple enough. Just treat each view's actions as if they were different and name the events differently for each view (example this.trigger('view1:event1'), this.trigger('view2:event2')).
Make sure the views are subscribed to these specific custom events to avoid collisions on actions that would trigger events on both views (like attribute updates).
I have a Backbone Collection that users are performing CRUD-type activities on. I want to postpone any changes from being propagated back to the server — the Collection.sync() should not happen until the user initiates it (like POSTING a form).
As it stands, I have been able to implement on-the-fly updates with no issue (by calling things like Model.destroy() on the models when deleted, or Collection.add() to add new models to the collection. As I understand, I could pass the {silent:true} option to my models, preventing .sync() from being called during .add()/.destroy(), but from what I can tell, that could lead to some headaches later.
I have considered overriding Backbone.sync, but I am not sure if that is the best route — I feel like there is some way to hook into some events, but I am not sure. Of course I have read through the Backbone docs, annotated source, and relevant SO questions before posting this, but I have hit a wall trying to extrapolate for this particular situation.
Eventually I will need to implement this in many places in my application, which is why I am concerned about best-practices at this stage. I am looking for some guidance/suggestions/thoughts on how to proceed with preventing the default behavior of immediately syncing changes with the remote server. Any help is appreciated — thank you for your time!
EDIT:
I went with Alex P's suggestion of refactoring: in my collection I set up some attributes to track the models that have been edited, added, or deleted. Then, when the user triggers the save action, I iterate through the lists and do the appropriate actions.
The first step is to ensure that your collection is being synchronised when you suspect it is. Collection.add() shouldn't trigger a Collection.sync() by default (it's not mentioned in the method documentation or the list of events, and I couldn't see a trigger in the annotated source).
Model.destroy() does trigger a sync(), but that shouldn't be a surprise - it's explicitly defined as "destroying a model on the server", and that sync() is performed on the model, not the collection. Your destroyed models will be removed from any collections that contain them, but I wouldn't expect those collections to sync() unless explicitly asked.
If your collections really are sync()ing when you're not expecting them to, then the most likely culprit is an event listener somewhere. Have you added any event listeners that call sync() for you when they see add or remove events? If your collection should sync() only on user interaction, can you remove those event listeners?
If not, then passing {silent: true} into your methods might be a viable approach. But remember that this is just stopping events from being emitted - it's not stopping that code from running. If something other than an event listener is triggering your sync()s, then preventing those events from being emitted won't stop them.
It would also be worth considering a wider refactor of your app. Right now you modify the collection and models immediately, and try to delay all sync()s until after the user clicks a button. What if you cached a list of all models to destroy & items to add, and only performed the actions when the button is clicked? Storing the model IDs would be sufficient to destroy them, and storing the collection ID and model ID would let you add items. It also means you don't have to fetch() the collection again if the user decides not to save their changes after all.
I have an object:
NAMESPACE.SOMEOBJECT.VALUE = 0
Some other foreign script owns and changes that value based on an event that I don't have access to. I know that the value is changed by different events on different pages. I can't edit the .js that controls that object. Does anyone have any idea how i can listen to that specific object and then run a function if that value changes?
I cannot add a new library or any external script. I'm using Backbone, require.js and jQuery.
If I were to use a backbone.model and add a reference to this object in my model, and then in my view I were to listenTo my model, would that tell me if that object has been updated? If so, sow would I go about doing this?
I DO have access to an ID on the page that allows me to access a DIV that has the value that I'm looking for. Keep in mind, that every time my object value updates, I need to know if it's been updated.
Unfortunately there's no way to 'add a reference to this object in the model' as this is just another way of saying that something should 'listen' for changes on the object. In order for a model to trigger a change event, one of its attributes has to change and this cannot happen by any means other than calling the model's set method or (re-)fetching (changed) data from the server-side.
How about polling the object instead of listening for an event? You could use setInterval to periodically query the relevant value and track its changes. In the event that the value has changed you can invoke the desired behaviour.
You could also wrap this functionality in a Backbone model if this is a good fit for your app's design: In the model's initialize / constructor use setInterval to start polling the value periodically and use set to copy it onto one of the model's attributes. Backbone will take care of tracking state for you and the model will trigger a change event if (and only if) the value changes.
Hope this helps.
I want to run some view code when the model that the view is bound to changes.
My view has an observer on the controller model, like so:
App.SomeView = Em.View.extend
modelDidChange: (()->
# do stuff
).observes('controller.model')
When the model changes, modelDidChange is called twice.
Why is that?
Is there a better/different way to achieve what I'm trying to do here?
Using Ember 1.3.0.
Are you sure that your view needs to know when the model changes? The view's template gets its properties from the controller, and should update automatically when they change. You certainly want to avoid any observers if you can.
But I don't know much about your use case, so assuming that it's necessary, I would try a debouncer. It's likely that the model isn't changed twice, the event is just fired twice. Ember has a LOT of action that happens in between user hooks, so the model is probably just set to the same value twice. Use a debouncer to have your method called just once.
Ember.beginPropertyChanges();
# model updates
Ember.endPropertyChanges();
The first time it fires because you made a change in your browser and that change gets sent to the Ember data store which triggers the observer callback. The store then makes an update request to the backend which responds with an updated model, when the store receives this updated model it triggers the observer callback once again.
I am trying to create a web page with form that once user change any field, the validation and update commit immediately rather than letting user to click on submit button. I am using Knockout.js and mapping plugin. I know I can achieve this by creating a computed field for each original fields, but this kind of work is tedius and dumb, is there good practice to create a general listener to listen on any changes in any fields and update backend respectively?
In order to subscribe to any change you could use ko.toJS() method. Actually it allows to walk through object graph and unwrap observables. As your probably know when you use ko.computed it subscribes to all reads of observables fields and re-evaluate on every change. So if you use code like this:
ko.computed(function() {
ko.toJS(viewModel);
// update data on server
});
Also you should pay attention that this piece of code will update data on server right after initialization. It could be easily avoided. Please checkout this example: http://jsfiddle.net/UAxXa/embedded/result/
Also I think you might want to send only changed data to server. You could incorporate ko.editbales plugin ( https://github.com/romanych/ko.editables ) and some KO under-hood knowledge. Please checkout this sample: http://jsfiddle.net/romanych/RKn5k/
I hope it could help you.
You've got several options but if you want a single listener, one good way is to use the same code I used to create a change tracker. It simply listens for the observable changes. Its about 19 lines of code. You can grab this and instead of using it for change tracking, just wire in a method that saves the changes when they occur.
NuGet http://nuget.org/packages/Knockout.ChangeTracker
Codeplex http://kochangetracker.codeplex.com/
To Setup change tracking, add this tracker property to your view model:
viewModel.tracker = new ChangeTracker(viewModel);
Hook these into your view to determine when changes occur:
viewModel.tracker().somethingHasChanged();
Hook this into your view model when you want to reset state in functions (ex: load, save):
viewModel.tracker().markCurrentStateAsClean;
Optionally, you can pass your own hashFunction for state tracking, too.