Oleksandr Shyshkov
Oleksandr Shyshkov

Reputation: 147

Backbone not evaluates url function before request

I'm trying to use Backbone with REST API:

Here the code

My model:

var PagesModel = Backbone.Model.extend({
    idAttribute: 'Guid',
    initialize: function () {
        this.on('remove', this.destroy);
    },
    urlRoot: '/api/pages'
});

Collection:

var PagesCollection = Backbone.Collection.extend({
        model: PagesModel,
        url: '/api/pages'
    });

View:

var PagesView = Backbone.View.extend({
    el: '#pages',
    events: {
        'click .removePage': 'remove',
    },
    initialize: function (collection) {
        this.collection = collection;
        this.collection.fetch();
        this.template = $('#pages-template').html();
        this.collection.bind('change reset', this.render, this);

    },
    render: function () {
        var template = _.template(this.template);
        $(this.el).html(template({ pages: this.collection.toJSON() }));
        return this;
    },
    remove: function (e) {
        e.preventDefault();
        var id = $(e.currentTarget).closest('ul').data("id");
        var item = this.collection.get(id);
        this.collection.remove(item);
        $(e.currentTarget).closest('ul').fadeOut(300, function () {
            $(this).remove();
        });
    }
});

And here I'm starting up application:

$(function () {
    var pagesCollection = new PagesCollection();
    var pagesView = new PagesView(pagesCollection);
});

I'm clicking or Remove and in Network inspector see this link

http://localhost:54286/backbone/function%20()%20%7B%20%20%20%20%20%20var%20base%20=%20getValue(this,%20'urlRoot')%20%7C%7C%20getValue(this.collection,%20'url')%20%7C%7C%20urlError();%20%20%20%20%20%20if%20(this.isNew())%20return%20base;%20%20%20%20%20%20return%20base%20+%20(base.charAt(base.length%20-%201)%20==%20'/'%20?%20''%20:%20'/')%20+%20encodeURIComponent(this.id);%20%20%20%20}

instead of /api/pages/{guid}.

What I'm doing wrong?

Upvotes: 1

Views: 84

Answers (2)

Loamhoof
Loamhoof

Reputation: 8293

I still haven't figured fully why, but you can make it work by destroying your model after the end of its removal (Backbone does one last thing after triggering the remove event: destroy the collection's reference in the model).

But what's even better, is using directly the destroy function on the model, it will remove it from the collection automatically (use {wait: true} if needed).

Edit:
Finally managed to locate the source of the problem. It's rather simple in fact. To override the model's url (calculated with urlRoot but that doesn't matter), you can pass Model#destroy a url option when calling Backbone.sync (or something that'll call it).
Now you're thinking "but I don't!". But you do. The listener (Model#destroy in your case) is given 3 arguments. Model#destroy will take the first one (the model itself) as options.
And here's the fail (I think Backbone needs a patch to this): giving an url option to Backbone.sync is the only time _.result in not used to calculate the url. So you find yourself having as url the url property of your model, which is the function you see in your call.

Now, for a quickfix:

this.on('remove', this.destroy.bind(this, {}));

This will ensure the first argument of your Model#destroy call is {} (as well as binding the context).

Bear with me a little longer.

Now, if you're still willing to call Collection#remove before destroying your model, here's a little hack: because (as I stated above) the remove event is triggered before Backbone makes sure to remove the collection's reference in your model, you don't need the urlRoot property in your model. Indeed, the model won't be in the collection anymore, but Backbone will still take the collection's url into account to get the model's url (as the reference is still there).

Upvotes: 1

James
James

Reputation: 4737

Not a definitive answer, but just going by the code in your question and the backbone.js documentation, the problem may be that you named your method remove and this is getting in the way of the remove method in Backbone.View.

http://backbonejs.org/#View-remove

Update:

It also looks like the output you see in the network inspector is that the definition of the Backbone.Model.url function is being appended. Meaning url is not being properly called (Maybe the () is missing by the caller?). Are you overriding Backbone.sync anywhere in your application?

Upvotes: 0

Related Questions