我正在为正在构建的应用程序使用主干网。在这个应用程序中,我有一个主视图,它渲染了一个模板,里面有两个其他视图。一个标题视图和另一个包含一些内容的标题视图。头视图只是用于与内容视图交互,还具有特定的功能。
在标题模板和内容模板中,我有相同的代码,一个隐藏的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(除了加载的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
)。您也可以使用apply
和call
来选择不同的this
,因此:
f.call(o);
会使this
成为您所期望的o
。我应该提到的是,在大多数JavaScript环境中,您可以使用bind
强制选择this
,但我不想过于偏离主题。
重点是:
this.el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
不足以确保showLoader
和hideLoader
将在正确的上下文中运行;我还假设showLoader
和hideLoader
末尾的括号只是打字错误。
在主干应用程序中强制上下文的最常见方法是在initialize
:中使用_.bindAll
initialize: function(params) {
_.bindAll(this, 'showLoader', 'hideLoader');
//...
这基本上用一种或多或少相当于你的包装的东西取代了this.showLoader
和this.hideLoader
:
function() { self.showLoader(self.el) }
一旦你有了_.bindAll
,这个:
this.el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
会很好用的。
顺便说一句,你不需要这么做:
this.el = params.el;
在你的initialize
中,Backbone为你做这件事:
构造函数/初始化
new View([options])
[…]有几个特殊选项,如果传递,将直接附加到视图:
model
、collection
、el
、id
、className
、tagName
和attributes
。
你不需要做这样的事情:
$('#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({ ... }));