4

我正在为我正在构建的应用程序使用主干。在这个应用程序中,我有一个主视图,它呈现一个模板,里面有 2 个其他视图。一个标题视图和另一个带有一些内容的视图。标题视图仅用于与内容视图交互,并且也具有特定的功能。

在标题模板和内容模板中,我有相同的代码,一个隐藏的 DIV,带有一个加载器图像,在进行 ajax 调用时显示。我遇到的问题是,当我第一次加载应用程序时(或者当我刷新内容视图时),内容视图正在从 ajax 请求中加载一些数据,但是加载器同时显示在标题和内容中模板(例如如果 ajaxStart() 是未附加到视图的全局事件。

这是内容视图设置:

App.View.Content = Backbone.View.extend({
        type:'test',
        template: twig({
            href: '/js/app/Template/Content.html.twig',
            async: false
        }),
        block:{
            test:twig({
                href: '/js/app/Template/block/test.html.twig',
                async: false
            })
        },
        list:[],

        showLoader: function(el){
            console.log('loader: ', $('#ajax_loader', el));
            $('#ajax_loader', el).show();
            console.log('Ajax request started...');
        },

        hideLoader: function(el){
            $('#ajax_loader', el).hide();
            console.log('Ajax request ended...');
        },

        initialize: function(params)
        {
            this.el   = params.el;
            this.type = params.type || this.type;
            var self  = this;

            this.el
                .ajaxStart(function(){self.showLoader(self.el);})
                .ajaxStop(function(){self.hideLoader(self.el);});

            this.render(function(){
                self.list = new App.Collection.ListCollection();
                self.refresh(1, 10);
            });
        },

    refresh:function(page, limit)
    {
        var self = this;
        console.log('Refreshing...');

        $('#id-list-content').fadeOut('fast', function(){
            $(this).html('');
        });

        this.list.type  = this.type;
        this.list.page  = page || 1;
        this.list.limit = limit || 10;

        this.list.fetch({
            success: function(data){
                //console.log(data.toJSON());

                $.each(data.toJSON(), function(){
                    //console.log(this.type);
                    var tpl_block = self.block[this.type];
                    if (tpl_block != undefined) {
                        var block = tpl_block.render({
                            test: this
                        });
                        $(block).appendTo('#id-list-content');
                    }
                });

                $('#id-list-content').fadeIn('fast');
            }
        });
    },

    render: function(callback)
    {
        console.log('Rendering list...');
        this.el.html(this.template.render({

        }));

        if (undefined != callback) {
            callback();
        }
    }
});

如您所见,我正在使用一段丑陋的代码来附加 ajaxStart / ajaxStop 事件:

this.el
    .ajaxStart(function(){self.showLoader(self.el);})
    .ajaxStop(function(){self.hideLoader(self.el);});

我以前是这样的:

this.el
    .ajaxStart(self.showLoader())
    .ajaxStop(self.hideLoader());

但是无论出于何种原因,我仍然未定义,this.el并没有在showLoader()and中定义hideLoader()

我当时在想,ajaxStart()并且ajaxStop()被附加到this.elDOM 上,只有这个视图才能听到它。但是我的 headerView 具有完全相同的设置(加载的树枝模板除外)显然接收到事件并显示加载器。

为了确保这种行为,我showLoader()在内容视图中注释掉了,加载器仍然显示在标题视图中。

我不知道我做错了什么:(

编辑(在“mu太短”的回答之后):

我的内容视图现在看起来像这样:

showLoader: function(){
            //this.$('#ajax_loader').show();
            console.log('Ajax request started...');
        },

        hideLoader: function(){
            this.$('#ajax_loader').hide();
            console.log('Ajax request ended...');
        },

        initialize: function(params)
        {
            var self  = this;

            console.log(this.el);

            _.bindAll(this, 'showLoader', 'hideLoader');

            this.$el
                .ajaxStart(this.showLoader)
                .ajaxStop(this.hideLoader);

            this.render(function(){
                self.list = new App.Collection.List();
                self.refresh(1, 10);
            });
        },
...

render: function(callback)
        {
            console.log('Rendering post by page...');
            this.$el.html(this.template.render({

            }));

            if (undefined != callback) {
                callback();
            }
}

和我的标题视图:

...
showLoader: function(){
            this.$('#ajax_loader').show();
            //console.log('Ajax request started...');
        },

        hideLoader: function(el){
            this.$('#ajax_loader').hide();
            console.log('Ajax request ended...');
        },

        initialize: function(params)
        {
            var self = this;
            _.bindAll(this, 'showLoader', 'hideLoader');

            this.$el
                .ajaxStart(this.showLoader)
                .ajaxStop(this.hideLoader);

            this.models.Link = new App.Model.Link();
            this.render();
        },

        render: function(callback)
        {
            this.$el.html(this.template.render({
                data: []
            }));

            if (undefined != callback) {
                callback();
            }
        }
...

但是加载器仍然显示在标题视图模板中

PS:this.showLoader()不是错字,因为我想在当前主干视图中调用该函数。

4

1 回答 1

3

JavaScript 函数的上下文 (AKA this) 取决于函数的调用方式,而不是定义函数的上下文。鉴于这样的事情:

var f = o.m;
f();

当您o.m通过普通函数调用时fthis内部o.m通常是全局上下文(window在浏览器中)。您也可以使用applyandcall来选择不同的this,这样:

f.call(o);

会做出你所期望this的那样。o我应该提一下,您可以强制选择在大多数 JavaScript 环境中this使用bind,但我不想走得太远。

关键是:

this.el
    .ajaxStart(this.showLoader)
    .ajaxStop(this.hideLoader);

不足以确保showLoader并且hideLoader将在正确的环境中运行;我还假设您在末尾的括号showLoader只是hideLoader拼写错误。

在 Backbone 应用程序中强制上下文的最常见方法是_.bindAll在您的initialize:

initialize: function(params) {
    _.bindAll(this, 'showLoader', 'hideLoader');
    //...

这基本上取代了或多或少等同于您的包装器的东西this.showLoaderthis.hideLoader

function() { self.showLoader(self.el) }

一旦你有了它_.bindAll,这个:

this.el
    .ajaxStart(this.showLoader)
    .ajaxStop(this.hideLoader);

会正常工作。


顺便说一句,你不需要这样做:

this.el = params.el;

在您的initialize, Backbone为您执行此操作

构造函数/初始化 new View([options])

[...] 有几个特殊选项,如果通过,将直接附加到视图:modelcollectionelidclassName和。tagNameattributes

你不需要做这样的事情:

$('#ajax_loader', el).show();

或者,Backbone 会在您的视图中为您提供一个$方法,该方法可以在不隐藏el参数列表末尾的情况下执行相同的操作;这样做:

this.$('#ajax_loader').show();

在 Backbone 中更惯用。

此外,this.el不一定是 jQuery 对象,所以不要这样做:

this.el.html(this.template.render({ ... }));

在您的 中render,请改用缓存的this.$el

this.$el.html(this.template.render({ ... }));
于 2012-08-27T08:59:48.803 回答