PDA

View Full Version : Destroy controller (Mitchell Simoens SubApp)



gostbuster
4 Sep 2012, 7:02 AM
Hello,

On a precedent topic ( http://www.sencha.com/forum/showthread.php?238444-Loading-classes-on-the-fly-from-server-(-widgets-style)&p=875315&viewfull=1 ), I was looking for a way to load dynamically classes from Ajax Request.

Browsing the forum, I found a very interesting project from Mitchell Simoens (you can find it here : https://github.com/mitchellsimoens/SubAppDemo ). Thank you Mitchell for sharing this.

Well, I studied it the all day and got it almost work on my application, I modified the code from Mitchell to get it work on my app.

Let me share this to you :

SubApplication class (modified, initial code from Mitchell)



Ext.define('Ext.apm.SubApplication', { //apm is my name space, I put this file in /myapp/apm
extend: 'Ext.app.Controller',


requires: [
'Ext.ModelManager',
'Ext.data.Model',
'Ext.data.StoreManager',
'Ext.ComponentManager',
// 'Ext.apm.EventBus' //forced to comment this to start...I'll speak of this later in the post
],


/**
* @cfg {Ext.app.Application} app Reference to the global Ext.app.Application instance
*/
/**
* @cfg {String} id ID of the SubApplication.
*/


/**
* @cfg {Array} controllers Array of Controller names to create.
*/
/**
* @cfg {Object} dependencies Object holding Arrays for CSS and JS files to load.
* JS files will automatically be destroyed once all JS files are loaded.
* CSS files will be automatically destroyed when SubApplication is destroyed
*/
dependencies : {
css : [],
js : []
},
/**
* @cfg {Boolean/Ext.LoadMask} loadMask true to use a Ext.LoadMask while loading dependencies.
* Can also accept a config Object of Ext.LoadMask
*/
loadMask : true,
/**
* @cfg {String} loadingCls CSS name(s) to apply to the message div of Ext.LoadMask
*/
loadingCls : '',
/**
* @cfg {String} loadingText Text to show in the Ext.LoadMask
*/
loadingText : 'Loading...',
/**
* @cfg {Boolean} loadingUseMsg true to show a message in the Ext.LoadMask
*/
loadingUseMsg : true,
/**
* @cfg {Number}removeJSFileDelay Time in milliseconds to delay destroying JS files after all have been loaded
*/
removeJSFileDelay : 100,


scope : undefined,


/**
* Function that will be called before the launch function but after all dependencies
* have been loaded and Controllers created.
* @property beforelaunch
* @type Function
*/
beforeLaunch : Ext.emptyFn,
/**
* Function that will be called after everything has been set up. Use this function
* to create the SubApplication views.
* NOTE: Return the main view as SubApplication will listen for it's destroy event to destroy the SubApplication.
* @property launch
* @type Function
* @return {Ext.Component} Return the main view as SubApplication will listen for it's destroy event to destroy
* the SubApplication.
*/
launch : Ext.emptyFn,


constructor: function(config){
config = config || {};
var me = this;
var app = config.app;

var controllers = Ext.Array.from(config.controllers);





Ext.apply(config, {
documentHead : Ext.getHead(),
id : config.id,


// eventbus : app.eventbus, //forced to comment


//private
//holds loaded JS files to remove after loading file
loadedJS : Ext.create('Ext.util.MixedCollection'),
//private
//holds loaded CSS files to remove on destruction
loadedCSS : Ext.create('Ext.util.MixedCollection'),
});



me.callParent(arguments);

Ext.apply(me, {
appControllers : controllers,
controllers : Ext.create('Ext.util.MixedCollection')
});

me.init();

},


init: function() {

var me = this,
dependencies = me.config.dependencies, //needed to add config, cuz of ST 2 ?
css = dependencies.css,
js = dependencies.js,
loadMask = me.config.loadMask,
cfg;

/* pour le masque on verra apres = I'll see later with that
if (loadMask) {
cfg = Ext.isObject(loadMask) ? loadMask : {
msg : me.loadingText,
msgCls : me.loadingCls,
useMsg : me.loadingUseMsg
};


me.loadMask = loadMask = Ext.create('Ext.LoadMask', Ext.getBody(), cfg);
loadMask.show();
}
***/
Ext.each(css, function(file) {
me.loadCSSFile(file);
});

Ext.each(js, function(file) {
me.loadJSFile(file);
});




},







loadCSSFile: function(file) {
var me = this,
head = me.config.documentHead, //needed to add .config
css = document.createElement('link');


Ext.apply(css, {
href : file,
rel : 'stylesheet',
type : 'text/css'
});


head.appendChild(css);
me.loadedCSS.add(Ext.get(css));
},


//ajout de fichier js (par utile je pense, on va voir);
loadJSFile: function(file) {
var me = this,
head = me.config.documentHead,
script = document.createElement('script');


Ext.apply(script, {
src : file,
type : 'text/javascript',

onload : Ext.Function.createDelayed(me.handleFileLoad, me.removeJSFileDelay, me, [script]),
onreadystatechange : function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {

me.handleFileLoad(script);
}
}
});


head.appendChild(script);
},


handleFileLoad: function(script) {
script.onload = null;
script.onreadystatechange = null;
script.onerror = null;


var me = this,
loadedJS = me.config.loadedJS;


loadedJS.add(Ext.get(script));
me.checkJSDependencyState();
},


checkJSDependencyState: function() {
var me = this,
dependencies = me.config.dependencies,
js = dependencies.js,
jsLen = js.length,
loadedJS = me.config.loadedJS,
loadedLen = loadedJS.getCount();


if (jsLen === loadedLen) {
loadedJS.each(function(file) {
me.removeFile(file);
loadedJS.remove(file);
});


me.onBeforeLaunch();
}
},


addController: function(controller, skipInit) {

if (Ext.isDefined(controller.name)) {
var name = controller.name;
delete controller.name;


controller.id = controller.id || name;


controller = Ext.create(name, controller);
}


var me = this,
controllers = me.controllers;
controllers.add(controller);


if (!skipInit) {
controller.init();
}


return controller;
},


removeController: function(controller, removeListeners) {
removeListeners = removeListeners || true;


var me = this,
controllers = me.controllers;


controllers.remove(controller);


if (removeListeners) {
var bus = me.eventbus;


bus.uncontrol([controller.id]);
}
},




addSubApplication: function(subapp) {

var me = this,
app = me.config.app,
subapps = app.subApplications;


subapps.add(subapp);


return subapp;
},


removeSubApplication: function(subapp) {
var me = this,
app = me.config.app, //config needed
subapps = app.subApplications;


subapps.remove(subapp);
},


removeFile: function(file) {
console.log('zermove file');
Ext.destroy(file);
},


onBeforeLaunch: function() {

var me = this,
app = me.config.app,
controllers = me.appControllers,
loadMask = me.config.loadMask,
controller, subapp;

if (app) {
Ext.each(controllers, function(controlName) {
controller = {
application : app,
name : controlName
};
controller = me.addController(controller);
});


delete me.appControllers;


Ext.applyIf(app, {
subApplications : Ext.create('Ext.util.MixedCollection')
});


subapp = me.addSubApplication(me);
}
me.beforeLaunch.call(me.scope || me);


/**on cache le mask
if (loadMask) {
loadMask.hide();
}
*/

var cmp =me.config.launch();//this line work
// var cmp = me.launch.call(me.scope || me);//this line doesn't work


if (cmp) {
me.cmp = cmp;
cmp.on('destroy', me.handleSubAppDestroy, me);
}
},


handleSubAppDestroy: function(cmp) {
var me = this,
app = me.app,
bus = app.eventbus,
appControllers = app.controllers,
controllers = me.controllers,
cssFiles = me.loadedCSS,
deleteThis = false,
appController, idx;


controllers.each(function(controller) {
me.removeController(controller);
});


cssFiles.each(function(file) {
me.removeFile(file);


cssFiles.remove(file);
});


me.removeSubApplication(me);


me.loadedCSS = null;
me.loadedJS = null;
me = null;
}
});


and here is how I use this class:



var me = this,
app = me.getApplication(),//changed for ST2

subapp = Ext.create('Ext.apm.SubApplication',{
application : app, //otherwise you get a getRouter of undefined error...
app : app,
id : 'MyWidget.controller.View',
loadMask : true,
loadingText : 'Loading Test...',

controllers : [
'MyWidget.controller.Controller'
],

dependencies : {
css : [
'http://myserver/mywidget/mycss.css'
],
js : [
'http://myserver/mywidget/mycode.js',//contains all the widget code
]
},

launch: function() {

var widgetPanel = Ext.create('AirformSuivi.view.Formmonitor', {
app : me
});

me.getPortal().add(widgetPanel); //portal is the panel

me.getPortal().setActiveItem(1);//with a layout card
}
});



Well no problem with that, that works ! I able to get code from server side, and I can instanciate the controller (=> events ok, etc...)

The issue is when removing Controller : the class EventBus doesn't exist in Sencha Touch 2 (that's why I commented it). Now I don't see how I can easily destroy the controller,

Any Ideas (Mitchell ?)

Thank you very much in advance

mitchellsimoens
6 Sep 2012, 4:16 AM
The reason it needs the EventBus is so that it can remove the listeners from the this.control call. In Sencha Touch 2, you need to remove the listeners from the event dispatcher on the Ext.app.Application. Check out the control method of Ext.app.Application to see how to remove it, looks pretty easy.

gostbuster
6 Sep 2012, 4:18 AM
Thank you Mitchell you're great !
I'm gonna look for what you said !
I'll give you news, thanks,

mvarshavsky
18 Sep 2012, 9:22 AM
So, something along the lines of (let's say in pseudo code)?


Ext.define('MY-Ext.app.Application',
override: 'Ext.app.Application'
uncontrol: (controller) ->
dispatcher = @getEventDispatcher()
#for each selector string
_.each(@getEventDispatcher().listenerStacks?.component, (target) ->
#listener stack corresponding to each map
_.each(target, (stack) ->
#find and remove listeners scoped to our controller
_.each(stack.getAll(), (listener) ->
if listener.scope == controller
stack.remove(listener.fn, listener.scope)
)
)
)
)

gostbuster
19 Sep 2012, 7:36 AM
Hi,
Did you find a solution, I tried some code, but the solution is not found...

Let me share this :
In the subapplication class, I rewrited the methode 'removeController'




removeController: function(controller, removeListeners) {
removeListeners = removeListeners || true;
var me = this,
controllers = me.controllers;



var dispatcher = MyApp.app.getEventDispatcher();


Ext.each(dispatcher.listenerStacks.component, function(target,index) {
//forced to use for loop to browse the object
for (var key in target) {
if (target.hasOwnProperty(key)) {
var mytarget = target[key];
for (var key2 in mytarget) {
if (mytarget.hasOwnProperty(key2)) {
var event = mytarget[key2];
Ext.each(event.listeners, function(listener,index) {
//the listener.scope doesn't exists !
console.log(listener.scope);
});
}
}

}
}

});



},



Actually I didn't found à scope parameter in listener object.
Some more help would be great.
Thank you in advance.

gostbuster
19 Sep 2012, 7:45 AM
Futhermore, I didn't find any control method for the Ext.app.Application class :(( (neither observable, controller, etC...)

mvarshavsky
19 Sep 2012, 9:43 AM
scope will be part of what's in Ext.event.ListenerStack

gostbuster
26 Sep 2012, 4:36 AM
Hello, I worked again with this and advanced... Here is the code which works, except that it 'kills' all the events and not only the designated controller events :



removeController: function(controller, removeListeners) {

removeListeners = removeListeners || true;


var me = this,
controllers = me.controllers;
var dispatcher =Myapp.app.getEventDispatcher();
Ext.each(dispatcher.listenerStacks.component, function(target,index) {

for (var key in target) {
if (target.hasOwnProperty(key)) {
var stack = target[key];
for (var key2 in stack) {
if (stack.hasOwnProperty(key2)) {
var listener = stack[key2];
var listeners=listener.getAll();
for(var i=0;i<listeners.length;i++)
{
var scope=listeners[i].scope;
var fn=listeners[i].fn;
listener.remove(fn, scope)
}
}
}
}
}

});
},


I'm now trying to remove only my subapp controller event....

Any clue is welcome
Thank you very much in advance

gostbuster
26 Sep 2012, 5:30 AM
Well, actually there is something I don't understand, this is a thing that mvarshavsky suggested :



if listener.scope == controller
stack.remove(listener.fn, listener.scope)

I translated it in my function :


removeController: function(controller) {
var me = this,
controllers = me.controllers;
var dispatcher = MyApp.app.getEventDispatcher();


Ext.each(dispatcher.listenerStacks.component, function(target,index) {
for (var key in target) {
if (target.hasOwnProperty(key)) {
var stack = target[key];
for (var key2 in stack) {
if (stack.hasOwnProperty(key2)) {
var listener = stack[key2];
var listeners=listener.getAll();
for(var i=0;i<listeners.length;i++)
{
var scope=listeners[i].scope;
var fn=listeners[i].fn;
This is never equal !

if(controllers==scope)
{
listener.remove(fn, scope)
}
}
}
}
}
}

});




}


I don't know how scope could be equal to controllers to remove..... I analysed the objects and this is not the same at all.... what am I missing ??