Backbone.js视图的默认值?

时间:2011-10-18 14:30:40

标签: javascript backbone.js

我在Backbone.js工作,我想知道你是否可以像设置模型的默认值一样设置默认值?

10 个答案:

答案 0 :(得分:31)

您可以做的是在initialize功能中设置默认值。

defaults: {
  display: 'list'
},

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

这适用于普通选项,但不会覆盖任何特殊选项(Backbone也存储在视图对象上的选项 - ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName']

查看有效的演示:http://jsfiddle.net/dira/7MmQE/1/

答案 1 :(得分:26)

对于Backbone 1.1或更新版

方法A:在初始化

中使用_.defaults的OptionsInLiteral
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);
  }
});

方法B:使用viewOptions插件(或类似的)

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

感谢@BraveDave在评论中指出这一点。

对于1.1版之前的Backbone(历史参考,FYI)

Here is the backbone issue核心团队最有可能完全摆脱this.options_configure中的逻辑。

使用options属性并始终使用this.options

这个问题有很多混乱,甚至是一个高度赞成和接受的错误答案。希望这个答案能够证明一个真正正确的解决方案,并指出所有其他候选答案中的错误。

要与Backbone.View父类协调一致,您应该包含传递给options的对象文字的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);
  }
});

以下是一些示例以及它们登录到控制台的内容。

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}

这将正确利用Backbone.View._configure,从Backbone 1.0.0看起来像这样:

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

这意味着:

  • 如果您的视图对象文字包含options_configure会将这些文件正确地视为默认值,使用传递给构造函数的属性覆盖它们,并将最终结果对象设置为this.options。欢呼。这就是我们想要的。
  • 即使在没有参数的情况下调用视图构造函数,这也会起作用。欢呼。也是我们想要的。
  • 由于此处使用_.resultoptions属性可能是Objectfunction,如果它是一个函数,它将被调用并返回价值将被使用。

这也是可以接受的,并允许每个实例的默认值是唯一的。

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);
  }
});

以下是一些示例以及它们登录到控制台的内容。

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}

为什么你应该总是使用this.options

所以上面的内容很棒,如果你的视图的构造函数没有参数调用,你的initialize函数this.options内部将存在并且是正确的,但是options参数为initialize undefined函数将为initialize: function (options) { console.log(options.flavor); //BUG! options is undefined. Uncaught exeption. :-( console.log(this.options); //correct }

options

因此,当我定义初始化时,我甚至没有为函数指定options参数作为提醒而不使用它。通常,您希望忽略initialize的{​​{1}}参数,因为它无论如何都不包含默认值。

Buggy回答:_.extend(this.defaults,this.options)

这个答案有一个错误,即每次实例化实例时它都会无意中修改所有未来实例的默认值。

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回答: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

注意选项对象和特定于实例的默认值

这个问题的答案之一表明了这一点:

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

这几乎肯定不是你想要的和一个bug。如果你创建10个视图,它们将共享Backbone.Collection的同一个实例,因为在定义视图子类时只会创建1个实例。当您将模型添加到view9的集合并且它显示在所有视图中时,这肯定会让您感到困惑。您更可能想要的是每个视图实例的不同的新集合实例,为此,您需要使options成为上面示例中的函数。

执行此操作的正确方法摘要

  1. 使用options: {...}options: function () {...}
  2. 声明您的initialize没有任何参数
  3. this.options
  4. 的形式访问您的正当默认选项

    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
      }
    });
    

    工作jsfiddle

    http://jsfiddle.net/DUc25/

答案 2 :(得分:13)

对于Backbone 1.1或更新版

方法A:在初始化

中使用_.defaults的OptionsInLiteral
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);
  }
});

方法B:使用viewOptions插件(或类似的)

backbone.viewOptions

感谢@BraveDave在评论中指出这一点。

对于1.1版之前的Backbone(历史参考,FYI)

Here is the backbone issue核心团队最有可能完全摆脱this.options_configure中的逻辑。

使用options属性并始终使用this.options

这个问题有很多混乱,甚至是一个高度赞成和接受的错误答案。希望这个答案能够证明一个真正正确的解决方案,并指出所有其他候选答案中的错误。

要与Backbone.View父类协调一致,您应该包含传递给options的对象文字的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);
  }
});

以下是一些示例以及它们登录到控制台的内容。

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}

这将正确利用Backbone.View._configure,从Backbone 1.0.0看起来像这样:

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

这意味着:

  • 如果您的视图对象文字包含options_configure会将这些文件正确地视为默认值,使用传递给构造函数的属性覆盖它们,并将最终结果对象设置为this.options。欢呼。这就是我们想要的。
  • 即使在没有参数的情况下调用视图构造函数,这也会起作用。欢呼。也是我们想要的。
  • 由于此处使用_.resultoptions属性可能是Objectfunction,如果它是一个函数,它将被调用并返回价值将被使用。

这也是可以接受的,并允许每个实例的默认值是唯一的。

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);
  }
});

以下是一些示例以及它们登录到控制台的内容。

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}

为什么你应该总是使用this.options

所以上面的内容很棒,如果你的视图的构造函数没有参数调用,你的initialize函数this.options内部将存在并且是正确的,但是options参数为initialize undefined函数将为initialize: function (options) { console.log(options.flavor); //BUG! options is undefined. Uncaught exeption. :-( console.log(this.options); //correct }

options

因此,当我定义初始化时,我甚至没有为函数指定options参数作为提醒而不使用它。通常,您希望忽略initialize的{​​{1}}参数,因为它无论如何都不包含默认值。

Buggy回答:_.extend(this.defaults,this.options)

这个答案有一个错误,即每次实例化实例时它都会无意中修改所有未来实例的默认值。

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回答: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

注意选项对象和特定于实例的默认值

这个问题的答案之一表明了这一点:

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

这几乎肯定不是你想要的和一个bug。如果你创建10个视图,它们将共享Backbone.Collection的同一个实例,因为在定义视图子类时只会创建1个实例。当您将模型添加到view9的集合并且它显示在所有视图中时,这肯定会让您感到困惑。您更可能想要的是每个视图实例的不同的新集合实例,为此,您需要使options成为上面示例中的函数。

执行此操作的正确方法摘要

  1. 使用options: {...}options: function () {...}
  2. 声明您的initialize没有任何参数
  3. this.options
  4. 的形式访问您的正当默认选项

    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
      }
    });
    

    工作jsfiddle

    http://jsfiddle.net/DUc25/

答案 3 :(得分:2)

var DefaultsView = Backbone.View.extend({
  defaults: {
    collection: new Backbone.Collection()
  },
  initialize: function () {
    _.defaults(this.options, this.defaults);
    // Ensures keys with special meaning (model, collection, id, className, etc.), are attached directly to the view
    Backbone.View.prototype._configure.apply(this, arguments);
  },
  render: function () {
    console.log(this.collection);
  }
});

var view = new DefaultsView();

view.render();

答案 4 :(得分:2)

尝试鸭子打孔。 来自Backbone的源代码:

var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    this._configure(options || {});
    this._ensureElement();
    this.initialize.apply(this, arguments);
    this.delegateEvents();
};

我们将覆盖_configure

// Save _configure method
var _oldConfigure = Backbone.View.prototype._configure;

Backbone.View.prototype._configure = function(options){
    _.defaults(options, this.defaults); 
    _oldConfigure.call(this, options);
};

现在Backbone.View的行为与Backbone.model在默认情况下的行为相同,你甚至不需要在构造函数/初始化方法中做任何事情。

答案 5 :(得分:1)

通过Backbone.View的解释,它说

  

constructor / initializenew查看([options])

     

创建新视图时,您传递的选项将附加到   以 this.options 查看,以供将来参考。有几个特别的   如果传递,将直接附加到视图的选项:模型,   收藏 el ID className tagName 属性< / strong>即可。如果是看法   定义一个初始化函数,它将在视图中被调用   首先创建。如果您想创建一个引用的视图   元素已经存在于DOM中,将元素作为选项传递:new   查看({el:existingElement})。

我想知道为什么视图中没有使用默认值,方法与模型和集合中使用的默认值相同。

答案 6 :(得分:0)

正确的解决方案与dira相似。只需在视图规范中指定选项对象,即可包含选项对象的默认值:http://jsfiddle.net/h3cAU/1/

var ViewDefaultsDemo = Backbone.View.extend({
  options: {
     display: 'list'
  },

  render: function() {
    console.log(this.options.display)
  }
});

有关其他信息,请参阅View._configure的来源:https://github.com/documentcloud/backbone/blob/0.9.10/backbone.js#L1332-L1334

答案 7 :(得分:0)

这个堆栈对我来说有点误导。彼得·莱昂斯的回答似乎是当前正确的答案,尽管没有最多的选票。

来自Backbone文档...

创建新视图时,您传递的选项 - 在合并到视图中已存在的任何默认选项后 - 将作为this.options附加到视图以供将来参考。

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

我能够通过在类定义中实现选项来使其工作。

MyScope.MyView = Backbone.View.extend({
    options: {
        default_prop: 'value1',
        another_default: 'value2'
    }
}

答案 8 :(得分:0)

如果我正确理解了这个问题,你可以用这种方式设置默认值:

scope.MyView = Backbone.View.extend({
    x: 'x',
})

var obj = new scope.MyView({
    y: 'y',
});

console.log( this.options );
// output: { x: 'x', y:'y' }

问题以及行为未完全反映View构造函数文档中所述的内容,即复合对象未完全复制。这是因为_configure使用了underscore.js _.extend而且它不是递归的。

这意味着如果你做这样的事情:

scope.MyView = Backbone.View.extend({
    x: {
        a: 'a',
    }
})

var obj = new scope.MyView({
    x: {
        b: 'b',
    }
});

console.log( this.options );
// output: { x: { b:'b' } }

无法按预期工作。如果将属性传递给视图并具有默认属性,则可能会发生这种情况。您传递的属性将被覆盖。

答案 9 :(得分:0)

使用the backbone.viewOptions plugin,它完全符合您的要求:

// Add the view options plugin functionality to all our views.
Backbone.ViewOptions.add( Backbone.View.prototype );

MyView = Backbone.View.extend( {
    options : [
        "type", // normal options are declared like so
        { "label" : "OK" } // options with defaults are declared like so
     ],

    initialize : function( options ) {
        this.setOptions( options ); // attaches declared options to the view

        console.log( this.label ); // outputs "OK"
        console.log( this.type ); // outputs "button"
    }
} );

new MyView( { type : "button" } );