Search code examples
javascriptbackbone.js

Backbone Fetch success callback not executing when fetch response is an empty array


I have Person model and I am retrieving a person info inside a view. The success callback FetchSuccess executes when the response has an object. But when response is empty, the callback is not called. Any Guess?

Models.Basic = Backbone.Model.extend({
    parse: function(response) {
        return response;
    }
});

Models.PersonModel = Backbone.Model.extend({
    url: function() {
        return '/person/' + this.data.id;
    }
});

Backbone.View.extend({

    template: Templates['template'],

    initialize: function(options) {
        this.id = options.id;
        _.bindAll(this, 'FetchSuccess');

        this.personModel = new Models.PersonModel();
        this.model = new Models.Basic();
        this.fetchData();
        return this;
    },

    render: function() {
        this.$el.append(this.template(this.model.toJSON()));
    },

    fetchData: function() {
        this.personModel.data = {
            id: this.id
        };
        this.personModel.fetch({
            context: this,
            success: this.FetchSuccess
        });
    },

    FetchSuccess: function() {
        this.model.set({
            name: this.personModel.get('name');
        });
        this.render();
    }
});

Solution

  • this.personModel = new Models.PersonModel();
    

    This is a Backbone Model, not a collection.

    this.personModel.fetch({
        reset: true, // this doesn't exist on model
        success: this.FetchSuccess
    });
    

    You can't fetch a model without an id. Also, the model, when fetching, expects an object to be returned.

    If you want to fetch a specific person, give an id to the model, then fetch.

    this.personModel = new Models.PersonModel({ id: "id_here" });
    // ...
    this.personModel.fetch({
        context: this,
        success: this.FetchSuccess
    });
    

    Here's the code with the corrections

    // parse isn't needed if you're not going to parse something
    Models.Basic = Backbone.Model.extend({});
    
    Models.PersonModel = Backbone.Model.extend({
        urlRoot: 'person/', // this handles putting the id automatically
    });
    
    Backbone.View.extend({
        template: Templates['template'],
    
        initialize: function(options) {
            this.id = options.id;
    
            // pass the id here
            this.personModel = new Models.PersonModel({ id: this.id });
    
            this.model = new Models.Basic();
            this.fetchData();
    
            // makes no sense in the initialize since it's never called 
            // manually and never used to chain calls.
            // return this; 
        },
    
        render: function() {
            // render should be idempotent, so emptying before appending
            // is a good pattern.
            this.$el.html(this.template(this.model.toJSON()));
    
            return this; // this is where chaining could happen
        },
    
        fetchData: function() {
            // This makes no sense unless you've stripped the part that uses it.
            // this.personModel.data...
    
            this.personModel.fetch({
                context: this, // pass the context, avoid `_.bindAll`
                success: this.onFetchSuccess,
                error: this.onFetchError
            });
        },
    
        onFetchSuccess: function() {
            this.model.set({
                name: this.personModel.get('name')
            });
            this.render();
        },
        onFetchError: function() { this.render(); }
    
    });
    

    You could catch the error with the error callback, or just do nothing and render by default, and re-render on fetch.

    You could also listen to the model events (inside the initialize):

    this.listenTo(this.personModel, {
        'sync': this.FetchSuccess,
        'error': this.onFetchError
    });
    this.personModel.fetch();