Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I state that a model is currently in a "fetching" state and how should I create "on fetch complete" events to bind to?

Tags:

backbone.js

Please note this is a question with regard to Backbone.js. This is not a "how do I do this" kind of question, as I'm pretty sure I can create my own, but I'm not sure my solution would be all that "clean". The problem is a bit more conceptual. I'm trying to understand if there's already a convention to do this, maybe not even in Backbone, but something more general.

This is the state of things that I'm trying to deal with.

  1. View is created but a related collection, though created, may not be initialized yet. The view needs some code to be executed when the model is initialized. If the model is already initialized, that code can be executed immediately. (Likewise, if the model is not initialized, the code has to "wait" or "be queued up" until the model is initialized,)
  2. The collection may be directly set up by a reset invocation or a fetch invocation.

I realize in Backbone, there is a reset event I can bind to. But I only want to bind to the reset event if reset hasn't been called yet, and I only want to bind to it "once", so I might want to do something where I bind to the reset event, and within the event handler, unbind that same handler.

I'd also like to handle the case (lets call it requirement #3, although it's not strictly needed for where I'm at now) that the collection may need to be set or to fetch it's data more than once.

Can somebody provide me with some sample code that meets the two requirements above (and preferably all 3 mentioned)? Or at least discuss a few approaches I might take? Thank you.

Edit:

Yes, this may need a specific circumstance to describe. I also might want somebody to tell me if this just smells fishy.

The specific circumstance is, I have a route, and I have a view that renders a select control, and actually another view, lets call it a "routing view", that tells views it controls how to navigate and how to reset themselves from the navigated route. I don't know if a view is good for this, it just seemed better than the code I had previously had for this was strewn all over the place.

So, Say I come in as a user, and I am presented with the ability to select a market from the select view and a few other things. The routing view binds to the actual select control's on change event so that when the user changes the select control, a new route is navigated to, as in

http://localhost/SomePage.aspx#!/event-search/market/san-angelo/date/2012-01-04

(with other values added, as you can see.)

The routing view also takes care of binding from the navigation back to the views (or widgets as they were) so when the url has "/date/2012-01-04", my date picker has 1/4/2012 as the value, or if my market has "/market/santa-fe", the select would show "Santa Fe". I've also set up in the routing the ability to search based upon the current route. This also allows me to hit the back button and forward button and see my previous searches and the controls would always match my original selection at the time the navigation was made. Very cool.

The problem arises when the page first loads. Currently my select control is bound to a model which also happens to provide data for another view on the page (admittedly, this might not be the best idea, but since we already have some views using that data in depth on the page, it's not so insane to piggyback the select view off of that). That data is fetched via ajax and is not available when the page loads. However, the "routing view" is initialized on when the page loads. So when it tries to select the (literal) option pertaining to the current route, the option isn't even rendered yet. This results in no error, it just means that the current market isn't selected in the select box.

As it stands currently, the code execution will always happen in this order, so binding to the reset event might work, but in the future, I might conceivably move code around such that there's a chance the model may actually have received all its data by the time the router view is initialized, maybe to speed up page load or something. So in that case, I cannot bind to reset because the model has already been reset and I just need need to select the value on the current select view... that is, if it's already been rendered.

Hrmm.

Something tells me I'm overthinking the "wrong" things and underthinking the "right" ones.

like image 434
JayC Avatar asked Jan 26 '12 18:01

JayC


2 Answers

I've hit a similar issue with collections. Hopefully the following code snippets, while not exactly what you're looking for, might help.

In the collection, I hook into its own reset event, and mark it as fetched:

return Backbone.Collection.extend({

    initialize: function() {
        this.isFetched = false;
        this.bind('reset', this.onReset, this);
    },

    onReset: function() {
        this.isFetched = true;
    }

});

Then in the view, I can either render the whole collection, or render a 'loading' message:

return Backbone.View.extend({

    render: function () {
        if (this.myCollection.isFetched) {
            this.myCollection.each(function (x) {
                // Render single item.
            });
        } else {
            // Set a loading animation.
        }
    }

});

Obviously the view needs to hook into the collection's reset event, and call render() when it fires.

like image 144
stusmith Avatar answered Oct 12 '22 07:10

stusmith


Your view should display the current state of your collection/models. So binding to reset in your view's initialize method (and leaving it bound) seem to satisfy your requirements. When the collection is reset via the reset or fetch methods, your view will be notified when the model's are ready.

I realize in Backbone, there is a reset event I can bind to. But I only want to bind to the reset event if reset hasn't been called yet, and I only want to bind to it "once", so I might want to do something where I bind to the reset event, and within the event handler, unbind that same handler.

I think this is the key to your question. You are right, you should only bind to reset once. And you should probably not be unbinding to it. If your collection resets, you would want your view to respond, right? If you create your view with an already fetched and full collection, binding to reset shouldn't cause a problem.

Maybe if that doesn't clarify things, you can provide more details about your situation.

EDIT: I am trying to understand you situation. Your select control is bound to a model, and selecting its value when notified that the model is ready (via reset or change event). If at some point, you have an initialized model when you create the view, you will know that it is initialized, and you can do the same selection code that you now do in the reset handler.

For example, maybe you are creating your view and calling render on it, adding it to the page. At the end of render you could call a function updateUI or something (the same function you call during the reset handler). If the model is filled during that initial render, it will select the values in the UI; if not, it will do nothing and try again on reset...

Of course, if some things on your page are not ready (waiting for your async ajax) you will need to show a spinner or disable things until you get your reset or change event.

EDIT2: One generality that may apply: Backbone is an MVC framework. MVC is all about separation of concerns. The concern of the view is to render the state of the model. If the model is not initialized when the view first renders, it should reflect that (showing nothing, or an "I am empty" type message), updating the UI when it receives the reset event (in your case, selecting the region). If the model is initialized when the view first renders, it should render and update the UI in the same way. In backbone it can get a bit confused, since the view.js also plays the role of controller, which is responsible for translating the UI changes into actions on the model.

like image 23
Paul Hoenecke Avatar answered Oct 12 '22 07:10

Paul Hoenecke