Fix events ajaxStart () and ajaxStop () only for the current view of the trunk

I am using the backbone for the application that I am creating. In this application, I have a main view that displays a template with two other views inside. One heading and another with some content. The header view is simply used to interact with the content view and has certain functions.

In the header template and content template, I have the same piece of code, a hidden DIV with the image of the bootloader that is displayed when making an ajax call. The problem is that when I download the application for the first time (or when I update the content view), the content view loads some data from the ajax request, but the loader appears both in the header and in the template content (for example, if ajaxStart () is a global event that is not tied to a view.

Here is the content setting:

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(); } } }); 

As you can see, I use the ugly part of the code to attach the ajaxStart / ajaxStop event:

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

I use it like this:

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

But for some reason, which is still undefined at my end, this.el not defined in showLoader() and hideLoader() .

I thought that ajaxStart() and ajaxStop() were attached to the this.el DOM and that only this view would be able to listen to it. But my View header, which has exactly the same setting (except for the loaded branch template), apparently receives the event and shows the loader.

To make sure of this, I commented on showLoader() in the content view and the loader is still showing in the header view.

I don't know what I'm doing wrong :(

EDIT (after responding with "mu too short"):

my content view now looks like this:

 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(); } } 

and my title:

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

But the loader is still showing in the header view template

PS: this.showLoader() not a typo, since I wanted to call a function in the current trunk mode.

+4
source share
1 answer

The context (AKA this ) for a JavaScript function depends on how the function is called, and not on the context in which the function is defined. Given something like this:

 var f = om; f(); 

When you call om through the regular f function, this inside om will usually be the global context ( window in the browser). You can also use apply and call to select another this to have this:

 f.call(o); 

would do this o , which you expect from him. I should mention that you can force your choice of this use bind in most JavaScript environments, but I don't want to get too looped.

The fact is that it is:

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

not enough to guarantee that showLoader and hideLoader will work in the right context; I also assume that the brackets that you had at the end of showLoader and hideLoader were just typos.

The most common way to enforce context in a Backbone application is to use _.bindAll in initialize :

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

This essentially replaces this.showLoader and this.hideLoader with something more or less equivalent to your wrappers:

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

Once you have this _.bindAll , this is:

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

will work fine.


By the way, you do not need to do this:

 this.el = params.el; 

in initialize , Backbone does this for you :

constructor / initialization new View([options])

[...] There are several special options that, if passed, will be bound directly to the view: model , collection , el , id , className , tagName and attributes .

And you do not need to do such things:

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

either, Backbone gives you a $ method in your view that does the same without hiding the el at the end of the argument list; doing it as follows:

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

more idiomatic in backbone.

Also, this.el will not necessarily be a jQuery object, so don't do this:

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

in render , use the cached this.$el instead:

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

All Articles