SnareChops
SnareChops

Reputation: 13347

Dynamically set backbone collection url returns models without attributes

I have a backbone mobile application that is tied to a Rails web service. All models of other objects are loaded and displayed correctly, but this particular one does not. The issue I am having is that Item gets created, but does not load the attributes. I have confirmed correct json output of the rails api. The difference between this collection and all other collections of this application is that this collection has a parentID that must be used in the url to load the correct items (category_id).

Now... the funny thing is that if I remove the {category_id: options.attributes[0].category_id} argument to the constructor call of ItemCollectionView and hard code a category_id directly into the url, it works! (however I need to assign this dynamically based on the category of the parent view.

Here are my classes:

export class ItemCollectionView extends Backbone.View {
    public template: string;
    public collection: itemsImport.Items;

    constructor(options?: Backbone.ViewOptions){
        this.collection = new itemsImport.Items({category_id: options.attributes[0].category_id});
        super(options);
    }
    ...
    public addOne(item: itemImport.Item): void{
        var view: itemItemImport.ItemItemView = new itemItemImport.ItemItemView({el: this.el, model: item});
        //Breakpoint right here shows that item.attributes.id = undefined
        view.render();
    }
}

export class Items extends Backbone.Collection {
    public url: string;

    constructor(attributes?: any, options?: any){
        this.url = 'http://localhost:3000/categories/' + attributes.category_id + '/items';
        this.model = itemImport.Item;
        super(attributes, options);
    }
}

In my debugging I can confirm that:

options.attributes[0].category_id == 1;

and

this.url == 'http://localhost:3000/categories/1/items';

and the response from that url is:

[{"id":1,"category_id":1,"title":"Item 1","description":null,"active":true,"comment":null,"extra":null,"deleted":"0","url":"http://localhost:3000/categories/1/items/1.json"},{"id":2,"category_id":1,"title":"Item 2","description":null,"active":true,"comment":null,"extra":null,"deleted":"0","url":"http://localhost:3000/categories/1/items/2.json"}]

which you can see is a correct json response.

So my question is: What am I doing wrong? || What is the correct way to dynamically pass variables into collections to set the correct url at runtime?

Thank you for your help

Upvotes: 1

Views: 881

Answers (3)

AntonyG
AntonyG

Reputation: 891

It is possible to define a dynamic url property to your Backbone model in your Typescript code. This would be better than using the url parameter in all your calls to fetch().

You could either do it using an ECMAScript 5 getter:

export class Items extends Backbone.Collection {
    get url(): string {
        return '/categories/' + this.get('category_id') + '/items';
    }

    constructor(attributes?: any, options?: any){
        this.model = itemImport.Item;
        super(attributes, options);
    }
}

or by setting it directly on the prototype:

export class Items extends Backbone.Collection {
    constructor(attributes?: any, options?: any){
        this.model = itemImport.Item;
        super(attributes, options);
    }
}
Items.prototype.url = function() { 
    return '/categories/' + this.get('category_id') + '/items'; 
};

The second solution will work on all browser.

Upvotes: 0

SnareChops
SnareChops

Reputation: 13347

The other answer selected will work if using javascript, but if using TypeScript the compiler will complain that url must be a property, not a method. The solution I found (which will work in javascript as well) is to set the url argument on the fetch() method of the collection like below

export class ItemCollectionView extends Backbone.View {
    public template: string;
    public collection: itemsImport.Items;

    constructor(options?: Backbone.ViewOptions){
        this.collection = new itemsImport.Items({category_id: options.attributes[0].category_id});
        super(options);
    }

    public initialize(options?:any): void {
        this.listenTo(this.collection, 'add', this.addOne);
        this.listenTo(this.collection, 'reset', this.addAll);
        this.collection.fetch({url: this.collection.seturl(options.attributes[0].category_id)});
    }

    ...

}

I hope this helps future users looking for this functionality in Backbone

Upvotes: 0

Eugene Glova
Eugene Glova

Reputation: 1553

Define url as function like in second case here

var Notes = Backbone.Collection.extend({
    url: function() {
        return this.document.url() + '/notes';
    }
});

And check network tab if you really load correct url.

Upvotes: 1

Related Questions