Dmitry
Dmitry

Reputation: 93

Backbone.js $.ajax success callback inside a collection init not calling a function from the same collection

I'm trying to call loadPhotos, but I get an error saying that loadPhotos is not defined. I tried this.loadPhotos(); but then I get an error saying that the object doesn't have such a method. I'm pretty new at this and still trying to figure out what has access to what and such, and I'd greatly appreciate if someone could point me in the right direction. What am I doing wrong?

Here's my code:

    Album = Backbone.Collection.extend ({
    model: Photo,
    url: "/api/",
    albumName: "",
    initialize: function(models, options){
        options || (options = {});
        if (options.title) {
            this.albumName = options.title;
        };

        $.ajax({
            type: "GET",
            url: this.url,
            data: "album=" + this.albumName,
            dataType: "json",
            success: function(data){
                console.log(data);
                loadPhotos(data); // <<< the problem is right here
            },
            error: function(jqXHR, textStatus, errorThrown){
                console.log("FETCH FAILED: " + errorThrown);
            }
        });

    },

    loadPhotos: function(filenames){
        for (var i = 0; i < filenames.length; i++ ){

            var photo = new Photo( {fileurl: filenames[i] });
            var photoView = new PhotoView( { model: photo} );
            this.add(photo);

        }


    }


});

Upvotes: 3

Views: 6772

Answers (2)

loganfsmyth
loganfsmyth

Reputation: 161517

You are misunderstanding how scoping and this work in JavaScript.

Calling the function like

loadPhotos(data);

will look for a function called loadPhotos in the various scopes from your success callback up to the global scope, but no such function exists because the function is attached to your collection instance.

this.loadPhotos(data)

is closer however it entirely depends on what this is referencing. this is a special keyword in JavaScript and the object that it points at depends on how the function was called. In your case this depends entirely on the behavior of jQuery. You want to use a reference to the collection. Common names to make it easier to know that the variable references the object are self, that and sometimes _this.

The one way to do that is a save a reference to the object pointed to by the outer this that references the collection. If you save it into a variable, it will work fine.

initialize: function(models, options){
    // Stuff

    var self = this;

    $.ajax({
        type: "GET",
        url: this.url,
        data: "album=" + this.albumName,
        dataType: "json",
        success: function(data){
            console.log(data);

            // Call the function on the collection.
            self.loadPhotos(data);

        },
        error: function(jqXHR, textStatus, errorThrown){
            console.log("FETCH FAILED: " + errorThrown);
        }
    });

},

Another option is to do as @alnitak's answer says, and explicitly pass the proper callback context using the context attribute.

Upvotes: 4

Alnitak
Alnitak

Reputation: 339917

If you call showPhotos directly it will have the global context (i.e. window).

If you just pass a reference to this.showPhotos it won't work either because that won't establish that this is to be used as the context when it's subsequently called.

To fix that, you also need to ask jQuery to set the right value of this, which can be done with the context option:

$.ajax({
   ...,
   context: this,
   success: this.loadPhotos,
   error: ...
});

If your success does nothing except call loadPhotos, setting the context variable allows you to just pass the reference to that function directly (as above), instead of wrapping a call to it in another function body.

Upvotes: 4

Related Questions