Backbone view.render()中的jQuery不规则性

时间:2015-07-17 03:11:10

标签: jquery backbone.js backbone-views

在什么情况下会发生这种情况:

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

2 个答案:

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

http://jsfiddle.net/C9wew/7299/

答案 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',
    //...
});

或者,如果您出于某种原因需要hasOwnPropertytrue,请在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;
}
  1. 您通常不会弄乱集合的models属性。您通常使用其中一个mixed in Underscore functions来迭代集合(例如this.collection.each(function(model) { ... }))或使用toJSON将其序列化为原始数据。使用toJSON更常见于视图,因此您的模板不会意外更改任何内容。
  2. 上面提到了
  3. UserProfileView.template
  4. 上面提到
  5. $(this.el)
  6. 我不明白为什么你有一个renderThis内部函数,为什么不在render内做到这一点?
  7. 这样的事情会更加惯用:

    render: function() {
        var html = EJS.render(this.template, {
            data: this.collection.toJSON()
        });
        this.$el.html(html);
        return this;
    }
    

    当然,您需要修改模板以使用普通对象数组而不是模型数组。