-
23 Jul 2012 11:25 PM #1
getController() events called multiple times (Eventbus problem) BUG
getController() events called multiple times (Eventbus problem) BUG
FIX
Hello guys,Code:Ext.override(Ext.app.Controller,{ control: function() { if ( ! this.blockControl ) { this.blockControl = true; this.callParent(arguments); } } });
I found a bug when using getController() to get a controller instance and setting event's in the control() method of a controller. When a controller is called for the first time (this.getController('controller1')), everything is fine, but when it's called again it fires all event's twice. If you then call it again, all event's are called three times!
I think this is a serious bug, maybe I am doing something wrong, so I will post a sample code:
Application:
Main controller:Code:Ext.application({ name: 'Radiaal', appFolder: Ext.baseUrl + 'assets/js/radiaal-app/app', controllers: ['Main'], launch: function() { Ext.create('Radiaal.view.Viewport', { renderTo: 'extjs_app_wrapper' }); } });
Customers Controller:Code:Ext.define('Radiaal.controller.Main', { extend: 'Ext.app.Controller', onLaunch: function() { var self = this; // URL Routing and mapping G.Path.route('gebruikers', 'users'); G.Path.route('klanten', 'customers'); G.Path.root('#/dashboard'); // This URL listener, listens for hash changes in the URL. It will dispatch it and convert it into // "controller/action/params" format. G.Path.listen(function(controller, action, params) { controller = controller.capitalize(); var exists = OSP.Radiaal.fileExists('assets/js/radiaal-app/app/controller/' + controller + '.js'); if ( exists ) { var controllerObj = self.getController(controller); if ( ! action ) { return controllerObj.index(); } else if ( typeof controllerObj[action] === 'function' ) { return controllerObj[action].apply(controllerObj, params); } } self.getController('NotFound').index(); }); } });
Now, when we have a URL like this: http://localhost/extjs/#/customers it will call the Customers controller. If you then, navigate to any other URL (say: http://localhost/extjs/#/user) and then switch back to http://localhost/extjs/#/customers ALL event's within this.control() are called twice (because getController('Customers') is called for the second time)! I hope I'm doing something wrong here, I've searched the forums but didn't find any solution yet.Code:Ext.define('Radiaal.controller.Customers', { extend: 'Ext.app.Controller', views: [ 'customers.Index', ] index: function() { // Changes the actual page to the new view OSP.Radiaal.setPage('customersindex'); // Event's setup this.control({ 'customersindex button[action=create]': { click: function(button) { // This is a custom Window that will me shown when the user clicks the button. var w = Ext.create('Radiaal.view.customers.index.CreateCustomer'); w.show(); } },
Thanks!
Steffen
-
24 Jul 2012 12:02 AM #2
There's not really enough information. First off, what Ext version are you using?
Secondly, what is the "G" object in your application? When does it make a call to getController? Please put together a test case without any external dependencies.
Also, not really convinced the bug is on our side. If you look at /examples/app/simple and change the users controller:
You'll see init is only fired once no matter how many times you get the user controller.Code:Ext.define('AM.controller.Users', { extend: 'Ext.app.Controller', stores: ['Users'], models: ['User'], views: ['user.Edit', 'user.List'], refs: [ { ref: 'usersPanel', selector: 'panel' } ], init: function() { console.log('init fired'); this.control({ 'viewport > userlist dataview': { itemdblclick: this.editUser }, 'useredit button[action=save]': { click: this.updateUser } }); }, editUser: function(grid, record) { console.log('Getting controller'); this.getController('Users'); }, updateUser: function(button) { } });Evan Trimboli
Sencha Developer
Twitter - @evantrimboli
Don't be afraid of the source code!
-
24 Jul 2012 12:30 AM #3
I'm using Ext JS 4.1.1 and the G.Path object is a Javascript library to dispatch the hash in the URL. It works very nice with Ext JS.
Hmm, I think that the problem is that I setup the listeners in the index() method. This method is called every time you will call controller/index. The controller will then have those events doubled.
Is there any way to prevent this? I mean, I setup the view in the method, but also the listeners, is there some trick that those listeners won't be appended? I can't leave the call to index() away.
I will try some stuff, if I got a good solution I will post it here.
Thanks!
-
24 Jul 2012 12:36 AM #4
Why not just set a flag on the controller?
Evan Trimboli
Sencha Developer
Twitter - @evantrimboli
Don't be afraid of the source code!
-
24 Jul 2012 12:40 AM #5
I could do that, but it's a very big application. So to set those flags every time is not really efficient. Maybe I could override the Ext.app.Controller and check there if control settings already has been set? If so, then skip the current control() action.
I will look into this, thanks evant!
-
24 Jul 2012 9:01 PM #6
steffen,
I think you have a design flaw in the way that your index() method registers event listeners (via control()) each time it is triggered. getController() will return the cached instance once the controller has been created, but your index() method will register new listeners each time it is called.
Standard Ext design pattern is that you register events in the controller's init() method. It should also be noted that Ext currently has no strategy in place to unregister events when a controller is destroyed. Controllers are meant to be created once (on application startup) and exist during the entire lifecycle of the application.
Of course in larger applications this is most likely not gonna work. See the forum discussion on 'big applications', 'sub applications' and 'mvc'.
In our current application we don't register controllers with the application but create and destroy them ourselves (make sure you call init() after instantiating a controller, or use getController() alternatively). When you destroy a controller you need to unregister its listeners. See this thread for how to implement an 'uncontrol' method.
Of course you can also register listeners in index(), anyway, you will have to take care of cleaning them up properly once you navigate away from 'index'.
Also, a simple boolean flag on the controller, like evant suggested, e.g. this.indexEventsRegistered=true could take care to avoid registering listeners multiple times (since getController will always return the same controller instance).
However, at some point you should probably destroy the controller and clean up the listeners anyway.
Looks like we can't reproduce the issue or there's a problem in the test case provided.


Reply With Quote