在什么情况下会发生这种情况:
Maybe (Maybe a)
我实例化了这样的视图:
this.$el.append(ret); //doesn't work
$(this.el).append(ret); //works
所以也许我应该使用$ el而不是el来实例化视图。为什么Backbone会做出这种区分,这很疯狂。
我之前应该提到这一点,但这是我正在做的一件非标准事情。让我恼火的一件事是Backbone不允许出现“默认情况”。在视图中。这是错误的 - 例如 - 您可能想要一个视图的默认集合,但如果您在构造函数中传入一个集合,那么它将覆盖默认值。很合理。所以基本上我试图手动复制Backbone.View的Backbone.Model中的内容。
所以这里是视图,它的乐趣是为了你的快乐:
new allViews.UserProfile({el: '#main-content-id',collection: collections.users}),
这里是每个视图运行的非标准函数,并且在view.initialize()函数中调用此函数:
var UserProfileView = Backbone.View.extend({
defaults: function () {
return {
model: null,
collection: collections.users,
childViews: {}
}
},
events: {
},
constructor: function () {
this.givenName = '@UserProfileView';
Backbone.View.apply(this, arguments);
},
initialize: function (opts) {
Backbone.setViewProps(this, opts); //has side effects
},
render: function () {
var data = this.collection.models;
renderThis.bind(this)(UserProfileView.template);
function renderThis($template) {
var ret = EJS.render($template, {
users: data
});
//this.$el.append(ret);
$(this.el).append(ret);
}
return this;
}
},
{ //class properties
template: template
}
);
所以我认为正在发生的事情是,当我作为一个选项传递el时,它会过早地被添加到视图中,然后Backbone过早地调用
Backbone.setViewProps = function(view,options){
var opts = options || {};
var temp = _.defaults({}, opts, _.result(view, 'defaults'));
for(var prop in temp){
if(temp.hasOwnProperty(prop) && prop !== undefined){
if(temp[prop]!==undefined){
view[prop] = temp[prop];
}
}
}
};
来自Backbone源中的内部
this.setElement(_.result(this, 'el'));
反过来调用
_ensureElement: function() {
if (!this.el) {
var attrs = _.extend({}, _.result(this, 'attributes'));
if (this.id) attrs.id = _.result(this, 'id');
if (this.className) attrs['class'] = _.result(this, 'className');
this.setElement(this._createElement(_.result(this, 'tagName')));
this._setAttributes(attrs);
} else {
this.setElement(_.result(this, 'el'));
}
},
答案 0 :(得分:1)
我完全不确定你要做什么,但我会在评论中回答你的问题:
我认为有人可以帮助我的最好方法是帮助创造一个更好的 获取视图
的默认属性的方法
和
令我恼火的一件事是Backbone不允许这样做 视图中的“默认值”。
我没有看到是什么阻止你在骨干视图上定义默认值并允许使用可传入的options参数覆盖:
var View = Backbone.View.extend({
initialize: function(options){
this.defaults = {
name: "default",
color: "blue"
};
this.attributes= $.extend({}, this.defaults, options);
}
})
您可以在实例化视图时传递它们来覆盖这些属性:
var view = new View({name: "Alex", color: "red"});
答案 1 :(得分:1)
您的setViewProps
功能可能是您遇到问题的根源。您在创建视图时将el
传递到视图中。这意味着options
看到的setViewProps
中会有el
,特别是el
将是字符串'#main-content-id'
。然后,您的setViewProps
会有效地说:
view.el = '#main-content-id`;
一切都崩溃了。 el
property是一个DOM节点:
el
view.el
所有视图始终都有DOM元素( el 属性),[...]。
this.el
可以从DOM选择器字符串或Element中解析出来; [...]。
el
可以从选择器字符串解析,但它仍然应该是DOM元素。 el
选项可以是选择器字符串,但el
属性应该是DOM元素。
请注意,文档说:
所有视图始终都有DOM元素
因此,在调用 el
之前,传递给视图的字符串el
选项到DOM元素initialize
属性的转换会发生。这意味着您的setViewProps
无意中用字符串覆盖了el
属性(DOM元素)。
您不应盲目地假设传递给视图的所有选项都应该复制到视图属性:某些属性(例如el
)需要特殊处理,有些属性不应该被复制(例如意外render
选项)。如果你真的必须这样做,那么你应该将你复制的属性列入白名单:
var temp = _.result(view, 'defaults') || { };
for(var prop in temp) {
//...
}
您也可以将此功能直接附加到Backbone.View的原型:
Backbone.View.prototype.setViewProps = function(options) {
// use `this` instead of `view` in here
};
然后说:
this.setViewProps(options);
在initialize
内。
这里还有其他奇怪的事情发生。
不要这样做:
constructor: function () {
this.givenName = '@UserProfileView';
Backbone.View.apply(this, arguments);
}
constructor
更多是关于支持CoffeeScript class V extends Backbone.View
的AFAIK,而不是你应该做的任何事情。 Backbone中的“真实”构造函数是initialize
,Backbone将在适当的时间调用它。如果你想要一个givenName
属性,那么把它留在原型上,其他一切都不是特定于实例的:
var UserProfileView = Backbone.View.extend({
givenName: '@UserProfileView',
//...
});
或者,如果您出于某种原因需要hasOwnProperty
说true
,请在initialize
中分配:
initialize: function() {
this.givenName = '@UserProfileView';
//...
}
在视图实例上使用for...in
应该非常罕见,所以我将其保留在原型上。
同样地,将template
附加到“类”有点奇怪,您应该将其留在原型上并将其称为this.template
。
你的render
有点奇怪:
render: function () {
var data = this.collection.models;
renderThis.bind(this)(UserProfileView.template);
function renderThis($template) {
var ret = EJS.render($template, {
users: data
});
$(this.el).append(ret);
}
return this;
}
models
属性。您通常使用其中一个mixed in Underscore functions来迭代集合(例如this.collection.each(function(model) { ... })
)或使用toJSON
将其序列化为原始数据。使用toJSON
更常见于视图,因此您的模板不会意外更改任何内容。UserProfileView.template
。$(this.el)
。renderThis
内部函数,为什么不在render
内做到这一点?这样的事情会更加惯用:
render: function() {
var html = EJS.render(this.template, {
data: this.collection.toJSON()
});
this.$el.html(html);
return this;
}
当然,您需要修改模板以使用普通对象数组而不是模型数组。