1

I'm trying to use Backbone.localStorage as a backend to an app.

I wrote a Model called Era, and a corresponding EraView. Pressing enter in the EraView causes the model to be saved, and this is where I get the error:

Uncaught Error: A "url" property or function must be specified 
urlError     backbone.js:1509
_.extend.url     backbone.js:515
_.result     underscore-min.js:1060
Backbone.sync     backbone.js:1410
Backbone.sync     backbone.localStorage.js:188
_.extend.sync     backbone.js:276
_.extend.save     backbone.js:476
karass.EraView.Backbone.View.extend.close     era.js:61
karass.EraView.Backbone.View.extend.updateOnEnter     era.js:75

Here is the code to the EraView

var karass = karass || {};

// Era Item View

// the DOM element for an era item
karass.EraView = Backbone.View.extend({
    tagName: 'li',
    className: 'era',
    template: _.template( $('#era-template').html() ),

    // The DOM events specified to an item
    events: {
        'dblclick .edit-input': 'edit',
        'keypress .edit-input': 'updateOnEnter',
        //'blur .edit': 'close',
    },

    // The EraView listens for changes to its model, re-rendering. Since there's
    // a one-to-one correspondence between an era and a EraView in this karass, 
    // we set a direct reference on the model for convenience.
    initialize: function(){
        _.bindAll(this);
        this.model.on('change', this.render, this);
    },

    // Re-renders the era item to the current state of the model and
    // updates the reference to the era's edit input within the view
    render: function(){
        this.$el.html( this.template(this.model.attributes));
        this.$era_start = this.$('.era-start');
        this.$era_end = this.$('.era-end');

        this.$era_start.attr('disabled', true);
        this.$era_end.attr('disabled', true);

        return this;
    },

    // Switch this view into editing mode, displaying the input field
    edit: function(){
        this.$('.edit-input').removeAttr('disabled');

        this.$el.addClass('editing');
        this.$('.edit-input').addClass('editing');
    },

    // Close the editing mode, saving changes to the era
    close: function(){
        this.$('.edit-input').attr('disabled', true);

        var start = this.$era_start.val().trim();
        var end = this.$era_end.val().trim();

        if(start && end){
            this.model.save({from: start, until: end});
        }

        this.$el.removeClass('editing');
        this.$('.edit-input').removeClass('editing');

        this.trigger('close');
    },

    updateOnEnter: function(e){
        if(e.which !== ENTER_KEY && (!this.$era_start.val().trim() || !this.$era_end.val().trim())){
            return;
        }

        this.close();
    }
});

And this is the code for the era model:

var karass = karass || {};

karass.Era = Backbone.Model.extend({

    defaults: {
        from: '', 
        until: ''
    },

});

I thought I didn't need a url while using localStorage.

Edit: I forgot to mention that while this behavior occurs in the Era/EraView itself, it also occurs in the Name model, which extends Era. Name in turn belongs in a Names collection. I don't know if this makes a difference, but I figured I add it.

Edit 2: The Names collection looks like this:

karass.Names = History.extend({

    model: karass.Name,

    localStorage: new Backbone.LocalStorage('karass-names'),

});

Edit 3: I posted all the code on jsfiddle: http://jsfiddle.net/herrturtur/CRv6h/

lowerkey
  • 8,105
  • 17
  • 68
  • 102

1 Answers1

1

You don't need an url while using localStorage. But you need to set the localStorage property on your model or on your collection (if you set the localStorage on a collection the models inside the collection will "inherit" this setting):

karass.Era = Backbone.Model.extend({

    localStorage: new Backbone.LocalStorage("EraCollection"), 
    // the EraCollection should be an unique name within your app.

    defaults: {
        from: '', 
        until: ''
    },

});

If you don't setup the localStorage property the plugin falls back to the default ajax sync so you get the uri missing exception.

nemesv
  • 138,284
  • 16
  • 416
  • 359
  • yup, did that. (Should have posted the Names collection code as well) – lowerkey Feb 02 '13 at 16:04
  • Wait, era isn't a collection. It's a model that is extended by Name, which in turn goes into a Names collection. This Names collection has the localStorage property defined. – lowerkey Feb 02 '13 at 16:24
  • Sorry but I don't following you which class of your extend what and belongs to which collection... what is true that from the exception message that in your `EraView` the object in `this.model` does not have a `localStorage` property nor belongs to a collection which does has `localStorage`. You should double check the all of your collections have a `localStorage` and you only if pass a model to a view that model is inside in a collection. – nemesv Feb 02 '13 at 16:28
  • Otherwise you need to post your full model/collection hierarchy and also what you pass into your EraView as model... – nemesv Feb 02 '13 at 16:31
  • Era extends Model; Name extends Era; Names: collection of Name, has localStorage property; So you're saying each and every model has to belong to a collection? this.model in the EraView is basically a Name object, which belongs to a collection with a localStorage property. – lowerkey Feb 02 '13 at 16:33
  • Something is not correctly wired up in your code... The localStorage plugin checks for the `model.localStorage` or if its undefined then it check for `model.collection.localStorage` try to log these two values `console.log(this.model.localStorage) if (model.collection) console.log(this.model.collection.localStorage)` before calling `this.model.save` – nemesv Feb 02 '13 at 16:37
  • @lowerkey the code in your fiddle is wired correctly. You just don't call save anywhere... I've replaced the `set` calls to `save` calls in this fiddle: http://jsfiddle.net/pLBjb/ and it stores your models in the local storage. You can check in chrom dev console. Or just create a few entries and reload the page... – nemesv Feb 02 '13 at 17:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23840/discussion-between-lowerkey-and-nemesv) – lowerkey Feb 02 '13 at 17:15