Подтвердить что ты не робот

Значения по умолчанию для представления Backbone.js?

Я работаю в Backbone.js, и мне было интересно, можно ли устанавливать значения по умолчанию так же, как вы можете установить значения по умолчанию для модели?

4b9b3361

Ответ 1

Что вы можете сделать, так это установить настройки по умолчанию в функции 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/

Ответ 2

Для базовой версии 1.1 или более новой

Подход A: OptionsInLiteral с _.defaults при инициализации

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 (историческая ссылка, FYI)

Вот основная проблема, где, похоже, основная команда, скорее всего, избавится от 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. Ура. Это то, что мы хотим.
  • Это будет работать, даже если конструктор вида вызывается без аргументов. Ура. Также мы хотим.
  • Поскольку здесь используется _.result, свойство options может быть либо Object, либо function, и если это функция, она будет вызываться и будет использоваться возвращаемое значение.

Это также приемлемо и позволяет уникальным по умолчанию уникальным для каждого экземпляра.

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 будет be undefined.

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

Таким образом, когда я определяю свою инициализацию, я даже не указываю аргумент options функции как напоминание о том, чтобы не использовать ее. В общем случае вы хотите игнорировать аргумент options для initialize, потому что он все равно не содержит значений по умолчанию.

Багги ответ: _.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

Багги ответ: 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);

Это почти наверняка не то, что вам нужно, и ошибка. Если вы сделаете 10 представлений, все они будут использовать один и тот же экземпляр Backbone.Collection, поскольку будет создан только один экземпляр, когда будет определен подкласс представления. Это обязательно смущает вас, когда вы добавляете модель в коллекцию view9, и она появляется во всех представлениях. Скорее всего, вам нужен другой новый экземпляр коллекции для каждого экземпляра представления, и для этого вам нужно сделать options функцией, как в моем примере выше.

Резюме правильного способа сделать это

  • используйте options: {...} или options: function () {...}
  • Объявить initialize без каких-либо аргументов
  • Доступ к параметрам с правильной дефолтностью как this.options

Пример 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

Для базовой версии 1.1 или более новой

Подход A: OptionsInLiteral с _.defaults при инициализации

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 (историческая ссылка, FYI)

Вот основная проблема, где, похоже, основная команда, скорее всего, избавится от 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. Ура. Это то, что мы хотим.
  • Это будет работать, даже если конструктор вида вызывается без аргументов. Ура. Также мы хотим.
  • Поскольку здесь используется _.result, свойство options может быть либо Object, либо function, и если это функция, она будет вызываться и будет использоваться возвращаемое значение.

Это также приемлемо и позволяет уникальным по умолчанию уникальным для каждого экземпляра.

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 будет be undefined.

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

Таким образом, когда я определяю свою инициализацию, я даже не указываю аргумент options функции как напоминание о том, чтобы не использовать ее. В общем случае вы хотите игнорировать аргумент options для initialize, потому что он все равно не содержит значений по умолчанию.

Багги ответ: _.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

Багги ответ: 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);

Это почти наверняка не то, что вам нужно, и ошибка. Если вы сделаете 10 представлений, все они будут использовать один и тот же экземпляр Backbone.Collection, поскольку будет создан только один экземпляр, когда будет определен подкласс представления. Это обязательно смущает вас, когда вы добавляете модель в коллекцию view9, и она появляется во всех представлениях. Скорее всего, вам нужен другой новый экземпляр коллекции для каждого экземпляра представления, и для этого вам нужно сделать options функцией, как в моем примере выше.

Резюме правильного способа сделать это

  • используйте options: {...} или options: function () {...}
  • Объявить initialize без каких-либо аргументов
  • Доступ к параметрам с правильной дефолтностью как this.options

Пример 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/

Ответ 4

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

Ответ 5

Попытка перфорации утки. Исходный код 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 относительно значений по умолчанию, и вам даже не нужно ничего делать в методе конструктора/инициализации.

Ответ 6

По пояснениям Backbone.View говорится:

constructor/initializenew View ([опции])

При создании нового представления параметры, которые вы передаете, прикреплены к как this.options, для дальнейшего использования. Существует несколько специальных параметры, которые, если они будут переданы, будут прикреплены непосредственно к представлению: модель, , el, id, имя класса, tagName и /STRONG > . Если вид определяет функцию инициализации, она будет вызываться, когда вид сначала создан. Если вы хотите создать представление, которое ссылается на элемент уже в DOM, передайте элемент в качестве опции: new View ({el: existingElement}).

Я понимаю, почему значения по умолчанию не используются в представлении так же, как в Модели и коллекции.

Ответ 7

Правильное решение похоже на dira's. Просто указание объекта options в представлении spec будет содержать значения по умолчанию для объекта options: 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

Ответ 8

Этот стек немного вводит меня в заблуждение. Ответ Питера Лайсона кажется правильным, хотя и не имеет наибольшего количества голосов.

Из документов Backbone...

При создании нового представления параметры, которые вы передаете, после объединения в любые параметры по умолчанию, уже присутствующие в представлении, прикрепляются к представлению как this.options для справки в будущем.

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

Мне удалось заставить его работать, реализовав опции в определении класса.

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

Ответ 9

Если я правильно понял вопрос, вы можете установить значения по умолчанию таким образом:

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' } }

работает не так, как ожидалось. Вероятно, это произойдет, если вы передадите атрибуты в представление, имеющие атрибуты по умолчанию. Атрибуты, которые вы передадите, будут перезаписаны.

Ответ 10

Используйте плагин backbone.viewOptions, который делает именно то, что вы ищете:

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