【问题标题】:Attach the events ajaxStart() and ajaxStop() only to the current backbone view仅将事件 ajaxStart() 和 ajaxStop() 附加到当前主干视图
【发布时间】:2012-08-21 17:49:09
【问题描述】:

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

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

我在想ajaxStart()ajaxStop() 被附加到this.el DOM,并且只有这个视图才能听它。但是我的 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() 不是错字,因为我想在当前主干视图中调用该函数。

【问题讨论】:

    标签: ajax backbone.js backbone-views


    【解决方案1】:

    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 末尾的括号只是拼写错误。

    在 Backbone 应用程序中强制上下文的最常见方法是在您的 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,骨干网does that for you

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

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

    而且你不需要做这样的事情:

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

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

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

    在 Backbone 中更惯用。

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

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

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

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

    【讨论】:

    • 感谢您的宝贵回复,但加载程序仍显示在标题模板中:(
    • 据我在文档 (api.jquery.com/ajaxStart) 中读到的内容,ajaxStart() 是一个全球性事件。每次有ajax请求时都会触发它。你不能做$(DOM).ajaxStart(function(){console.log('triggered')}); 并期望ajax 请求被DOM 元素触发。我对吗?我的问题有什么解决方案:( ?
    • @maxwell2022:对,$(x).ajaxStart() 是一个谎言,因为它的真正含义是$(anything_at_all).ajaxStart()。您必须通过$.ajax 参数或通过返回的jqXHR 绑定到您想要的特定AJAX 调用。
    • 因为它是在我获取它时神奇地触发 ajax 调用的集合,所以我无法将它与 $.ajax 绑定,可以吗?此外,如果每个视图有 20 个 ajax 调用,我只想触发一个绑定到视图中的事件以显示相同的加载器。
    • 集合的fetch 具有您可能可以使用的成功和错误回调。如果您想添加某种“开始获取”事件,您还可以提供自己的 syncfetch 方法。我通常会在执行fetch 之前安装加载程序,然后在fetch 完成获取后调用render 时加载程序会消失。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多