仅将事件ajaxStart()和ajaxStop()附加到当前主干视图



我正在为正在构建的应用程序使用主干网。在这个应用程序中,我有一个主视图,它渲染了一个模板,里面有两个其他视图。一个标题视图和另一个包含一些内容的标题视图。头视图只是用于与内容视图交互,还具有特定的功能。

在标题模板和内容模板中,我有相同的代码,一个隐藏的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.elshowLoader()hideLoader()中都没有定义。

我以为ajaxStart()ajaxStop()连接到了this.elDOM,只有这个视图才能监听它。但我的headerView(除了加载的trick模板)显然接收到了事件并显示了加载程序。

为了确定这种行为,我在内容视图中注释掉了showLoader(),加载程序仍然显示在头视图中。

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

EDIT(在"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();
            }
        }
...

但是加载程序仍然显示在头视图模板中

附言:this.showLoader()不是一个打字错误,因为我想在当前主干视图中调用函数。

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

var f = o.m;
f();

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

f.call(o);

会使this成为您所期望的o。我应该提到的是,在大多数JavaScript环境中,您可以使用bind强制选择this,但我不想过于偏离主题。

重点是:

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

不足以确保showLoaderhideLoader将在正确的上下文中运行;我还假设showLoaderhideLoader末尾的括号只是打字错误。

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

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])

[…]有几个特殊选项,如果传递,将直接附加到视图:modelcollectionelidclassNametagNameattributes

你不需要做这样的事情:

$('#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({ ... }));

最新更新