Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom "fetch" events on Models and Collections #1468

Closed
wants to merge 1 commit into from

Conversation

spacenick
Copy link

...found this pretty useful to add "generic" loader views on any collections/models.

Simple example, a generic loader that can be quickly binded to any collection, and will do the job.

var Loader = Backbone.View.extend({

    tagName:'img',
    attributes:{
        src:'loader.gif'
    },

    initialize:function() {
        _.bindAll(this,'hide','show')
        this.collection.on('reset',this.hide);
        this.collection.on('fetch',this.show);
    },

    hide:function() {
        $(this.el).hide();
    },
    show:function() {
        $(this.el).show();
    }

})

… I found this pretty useful to add "generic" loader views on any collections/models.


Simple example, a generic loader that can be quickly binded to any collection, and will do the job.

var Loader = Backbone.View.extend({

	tagName:'img',
	attributes:{
		src:'loader.gif'
	},

	initialize:function() {
		_.bindAll(this,'hide','show')
		this.collection.on('reset',this.hide);
		this.collection.on('fetch',this.show);
	},

	hide:function() {
		$(this.el).hide();
	},
	show:function() {
		$(this.el).show();
	}

})
@tbranyen
Copy link
Collaborator

tbranyen commented Jul 3, 2012

To accompany this, here is a post I wrote recently about a fetch event:

http://tbranyen.com/post/how-to-indicate-backbone-fetch-progress

@braddunbar
Copy link
Collaborator

I like to do this with a custom sync. I find it to be a bit more generic and usually I want to do the same for any request, not just fetch.

var Model = Backbone.Model.extend({
  sync: function(method, model, options) {
    model.trigger('syncing', model, options);
    Backbone.Model.prototype.sync.apply(this, arguments);
  }
});

@kshaff03
Copy link

kshaff03 commented Jul 9, 2012

Too funny. I just came here to request this feature and it was right here on the front page. I would like for a generic Fetch event to be added to Model as well. I realize that I can add a callback to the fetch() function but this is not nearly as flexible. Please pull this in.

@tbranyen
Copy link
Collaborator

tbranyen commented Jul 9, 2012

@braddunbar that's a much better idea. what are your thoughts on adding this simple event?

@braddunbar
Copy link
Collaborator

@tbranyen I'd be for it, though I think there would need to be some consideration for multiple in-flight requests. Also, this is likely related, at least tangentially, to #1325.

@conorjpower
Copy link

I came across this while looking for an event to determine when a model is loaded from the server rather than changed. While 'syncing' is good it would be most helpful to know when the sync was completed successfully. See http://stackoverflow.com/questions/12038192/render-a-view-for-a-model-on-first-load-not-on-every-change/. Extending @braddunbar I used:

Backbone.Model.prototype.sync = function(method, model, options) {

var succ = options.success;
var customSuccess = function(resp, status, xhr) {
     //call original
    succ(resp, status, xhr);
    model.trigger('synced', model, options);
}
options.success = customSuccess;
Backbone.sync(method, model, options);

}

To overwrite with a custom success which triggers the event and then delegates to the original success. Any potential gotchas with this approach?

@kshaff03
Copy link

After reading conorjpower's post, I went and looked at this pull request again. My primary use case is when I have a model that I need to fetch from the server and once it has been successfully fetched, render it or perform some processing. The way this pull request is written, it fires prior to the request actually succeeding which doesn't help me. conorjpower's suggestion looks like it would satisfy my requirements.

@wookiehangover
Copy link
Collaborator

@kshaff03 did you know that you can use the deferred object issued by $.ajax (if you're using jQuery, obvs) to chain callbacks and have a definite state of a given fetch? I've used this pattern a lot for situations when I need to bind one-off functionality to a fetch, usually while initializing a model or collection.

Usually, it's something like this:

var model = Backbone.model.extend({
  url: '/some/resource',
  initialize: function(){
    this.dfd = this.fetch();
    this.dfd.done(function(){
      // your one-time binding to fetch()
    });
  }
});

The other benefit of assigning the fetch() deferred as a model property is that you can easily access & bind to it from other places in your code. Check out this post for more details on this technique.

@kshaff03
Copy link

@wookiehangover Thanks. I didn't know you could do this, however, I would still prefer an event driven approach. The approach above assumes that you want the fetch to be called from within the model itself. In most cases, my models are controlled by a parent object and it will be that parent object that will decide when that fetch is executed. I love the event driven approach because of its flexibility (can be handled either within or outside of the model) and allows me to decouple my code and keep my business logic in my models and my view logic in the views. Naturally, I could take your code above and wrap it within my own fetch method and call it myFetch() or something like that and raise a custom event but then I have to do this to every other model as well. Way easier to just tweak backbone or extend it.

There are several ways to do something similar to this and I've managed to make things work on a case by case basis. However, I would think that having an event that signals that your model object has been successfully retrieved from the server would be a reasonable request...

@conorjpower
Copy link

An additional update on the code above:

var succ = options.success;
var customSuccess = function(resp, status, xhr) {
    //edit here
    model.trigger('synced', model, options);
    //call original
    succ(resp, status, xhr);
}
options.success = customSuccess;
Backbone.sync(method, model, options);

I found that it was better to trigger the event before calling the original success method. This is important if you want the synched to be fired before the change event.

@jashkenas
Copy link
Owner

If we add this -- what's the best name for the event? syncing/sync/error is a bit of an awkward trinity, but I guess it works...

@braddunbar
Copy link
Collaborator

I've been using syncing, though maybe request, send, or busy would be better? I particularly like request because of the way is sounds like a sentence. "On request, do the following..."

@spacenick
Copy link
Author

It seems that people using this little patch are using the "syncing" word a lot, as what I've seen on some blogs. "request" sounds good to me aswel but can be kinda disturbing if you use another persistence strategy (websockets for instance)

@akre54
Copy link
Collaborator

akre54 commented Dec 6, 2012

@braddunbar this is great as long as there's some way to determine the current sync state of the model, since I often times need to check the sync status (is it unsynced? syncing? error'd? synced?) outside of an evented system similar to a finite-state machine. It'd be easy enough to implement (see Chaplin's implementation)

@jashkenas jashkenas closed this in ec97a1c Dec 10, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants