Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default values of Backbone.js view?

Tags:

I'm working in Backbone.js and I was wondering if you can set default values much the same way that you can set the default values of a model?

like image 693
Phillip Whisenhunt Avatar asked Oct 18 '11 14:10

Phillip Whisenhunt


People also ask

Can we set default values for model in Backbone JS?

defaults() The Backbone. js defaults model is used to set a default value to a model. It ensures that if a user doesn't specify any data, the model would not fall with empty property.

What are views in Backbone JS?

The Backbone. js Views specify how your data looks like. They represent model's data to the users. They can be used with any JavaScript template library.

What is the default value of number in JavaScript?

Anyways, the default value of a variable in JavaScript is null or undefined .

What is backbone JS give its features?

Backbone. js allows developers to develop one page applications and front-end much easier and better using JavaScript functions. Backbone provides different types of building blocks like models, views, events, routers and collections for assembling client side web applications.


2 Answers

What you can do is to set your defaults in the initialize function.

defaults: {
  display: 'list'
},

initialize: function() {
  this.options = _.extend({}, this.defaults, this.options);
}

This will work for normal options, but would not override any special options (the ones that Backbone stores on the view object as well - ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'])

See a working demo: http://jsfiddle.net/dira/7MmQE/1/

like image 54
dira Avatar answered Sep 18 '22 13:09

dira


For Backbone 1.1 or newer

Approach A: OptionsInLiteral with _.defaults in initialize

var MyView = Backbone.View.extend({
  options: {
    enabled: true,
    align:   "left"
  },
  initialize: function (options) {
    #be sure to do the '|| {}' here so 'new MyView()' works
    this.options = _.defaults(options || {}, this.options);
  }
});

Approach B: Use the viewOptions plugin (or similar)

https://github.com/rotundasoftware/backbone.viewOptions

Thanks to @BraveDave for pointing this out in a comment.

For Backbone prior to version 1.1 (historical reference, FYI)

Here is the backbone issue where it seems like the core team is most likely to get rid of this.options and the logic in _configure altogether.

Use an options property and always use this.options

There is much confusion on this question and even a highly upvoted and accepted incorrect answer. Hopefully this answer demonstrates a truly correct solution as well as pointing out the bugs in all the other candidate answers.

To work in harmony with the Backbone.View parent class, you are supposed to include an options property of the object literal you pass to Backbone.View.extend.

var OptionsInLiteral = Backbone.View.extend({
  options: {flavor: "vanilla"},
  initialize: function (options) {
    console.log("OptionsInLiteral.initialize first argument", options);
    console.log("OptionsInLiteral.initialize this.options", this.options);
  }
});

Here are some examples and what they log to the console.

new OptionsInLiteral();
    //OptionsInLiteral.initialize first argument undefined
    //OptionsInLiteral.initialize this.options Object {flavor: "vanilla"}
new OptionsInLiteral({flavor: "chocolate"});
    //OptionsInLiteral.initialize first argument Object {flavor: "chocolate"}
    //OptionsInLiteral.initialize this.options Object {flavor: "chocolate"}
new OptionsInLiteral({flavor: "strawberry", sprinkles: true});
    //OptionsInLiteral.initialize first argument Object {flavor: "strawberry", sprinkles: true}
    //OptionsInLiteral.initialize this.options Object {flavor: "strawberry", sprinkles: true}

This will correctly take advantage of Backbone.View._configure, which as of Backbone 1.0.0 looks like this:

_configure: function(options) {
  if (this.options) options = _.extend({}, _.result(this, 'options'), options);
  _.extend(this, _.pick(options, viewOptions));
  this.options = options;
},

What this means is:

  • If your view object literal contains options, _configure will properly treat those as default values, override them with properties passed into the constructor, and set the final resulting object as this.options. Hurray. That's what we want.
  • This will work even if the view constructor is invoked without arguments. Hurray. Also what we want.
  • Because _.result is used here, the options property may be either an Object or a function, and if it's a function, it will be called and the return value will be used.

This is also acceptable and allows the defaults to be unique per instance.

var OptionsFunctionInLiteral = Backbone.View.extend({
  options: function () {
    return {
      flavor: "vanilla",
      created: Date(),
      collection: new Backbone.Collection()
    };
  },
  initialize: function (options) {
    console.log("OptionsFunctionInLiteral.initialize first argument", options);
    console.log("OptionsFunctionInLiteral.initialize this.options", this.options);
  }
});

Here are some examples and what they log to the console.

new OptionsFunctionInLiteral();
    //OptionsFunctionInLiteral.initialize first argument undefined
    //OptionsFunctionInLiteral.initialize this.options Object {flavor: "vanilla", created: "Wed Jun 19 2013 16:20:16 GMT-0600 (MDT)", collection: Backbone.Collection}
new OptionsFunctionInLiteral({flavor: "chocolate"});
    //OptionsFunctionInLiteral.initialize first argument Object {flavor: "chocolate"}
    //OptionsFunctionInLiteral.initialize this.options Object {flavor: "chocolate", created: "Wed Jun 19 2013 16:21:17 GMT-0600 (MDT)", collection: Backbone.Collection}
new OptionsFunctionInLiteral({flavor: "strawberry", sprinkles: true});
    //OptionsFunctionInLiteral.initialize first argument Object {flavor: "strawberry", sprinkles: true}
    //OptionsFunctionInLiteral.initialize this.options Object {flavor: "strawberry", created: "Wed Jun 19 2013 16:22:26 GMT-0600 (MDT)", collection: Backbone.Collection, sprinkles: true}

Why you should always use this.options

So the above is great with the caveat that if your view's constructor is called with no arguments, inside your initialize function this.options will exist and be correct but the plain options argument to the initialize function will be undefined.

initialize: function (options) {
  console.log(options.flavor); //BUG! options is undefined. Uncaught exeption. :-(
  console.log(this.options); //correct
}

Thus when I define my initialize, I don't even specify the options argument to the function as a reminder not to use it. In general you want to ignore the options argument to initialize because it doesn't contain the default values anyway.

Buggy answer: _.extend(this.defaults, this.options)

This answer has a bug in that it unintentionally modifies the defaults for all future instances every time an instance is instatiated.

var DefaultsExtendView = Backbone.View.extend({
  defaults: {flavor: "vanilla"},
  initialize: function (options) {
    console.log("initialize 1st argument", options);
    this.options = _.extend(this.defaults, this.options);
    console.log("initialize this.options", this.options);
  }
});

new DefaultsExtendView(); //OK
new DefaultsExtendView({flavor: "chocolate"}); //OK
new DefaultsExtendView(); //BUG! You get chocolate instead of vanilla

Buggy answer: if (options.foo)

var myView = Backbone.View.extend({
    foo: "default_value",

    initialize: function(options) {
        if(options.foo) {
            foo = options.foo;
        }
    }
});

new myView(); //BUG! options is undefined, uncaught exception
//TypeError: Cannot read property 'foo' of undefined

Beware of options object and instance-specific defaults

One of the answers to this question suggests this:

var DefaultsView = Backbone.View.extend({
  defaults: {
    collection: new Backbone.Collection()
  },
  initialize: function () {
    _.defaults(this.options, this.defaults);

Which is almost certainly not what you want and a bug. If you make 10 views, they will all be sharing the same instance of Backbone.Collection as there will just be 1 instance created when the view subclass is defined. This is sure to confuse you when you add a model to view9's collection and it shows up in all of the views. What you more likely want is a different new collection instance for each view instance, and to get that you need to make options be a function as in my example above.

Summary of the proper way to do this

  1. use options: {...} or options: function () {...}
  2. Declare your initialize without any arguments
  3. Access your properly-defaulted options as this.options

Example Boilerplate

var MyView = Backbone.View.extend({
  options: {flavor: "vanilla"},
  initialize: function () { //note no declared arguments
      //use this.options here as needed and all is well
  }
});

Working jsfiddle

http://jsfiddle.net/DUc25/

like image 24
Peter Lyons Avatar answered Sep 18 '22 13:09

Peter Lyons