PDA

View Full Version : Getting hold of registered listeners in ST



mankz
30 Oct 2012, 1:25 AM
I'm wondering if there's a preferred way of getting a list of the listeners observing an Observable.

My case is this:

1. I have a store, not belonging to me
2. A component which observes the store is created, then destroyed
3. After component destroy, I want to verify that store has no extra listeners attached when comparing it to its state before creating the component.

I'm able to do this quite easily with a few hacks in Ext JS:


StartTest(function(t) {
t.diag('SchedulerPanel should clean up any listeners attached to its stores');

var eventStore = t.getEventStore();
var resourceStore = t.getResourceStore();
eventStore.setResourceStore(resourceStore);

// HACK reading private events object
var eventListeners = {};
for (var o in eventStore.events) {
if (eventStore.events[o] && eventStore.events[o].listeners && eventStore.events[o].listeners.length) {
eventListeners[o] = eventStore.events[o].listeners.length;
}
}
var resourceListeners = {};

for (o in resourceStore.events) {
if (resourceStore.events[o] && resourceStore.events[o].listeners && resourceStore.events[o].listeners.length) {
resourceListeners[o] = resourceStore.events[o].listeners.length;
}
}
var s = t.getScheduler({
eventStore : eventStore,
resourceStore : resourceStore
});

// Should clean all listeners
s.destroy();

t.diag('Scheduler not rendered');

t.isDeeply(eventStore.hasListeners, eventListeners, 'Listeners cleaned up on eventStore');
t.isDeeply(resourceStore.hasListeners, resourceListeners, 'Listeners cleaned up on resourceStore');

s = t.getScheduler({
renderTo : Ext.getBody(),
eventStore : eventStore,
resourceStore : resourceStore
});

s.destroy();

t.diag('Scheduler rendered then destroyed');

for (var o in eventStore.events) {
if (eventStore.events[o] && eventStore.events[o].listeners && eventStore.events[o].listeners.length) {
t.is(eventStore.events[o].listeners.length, eventListeners[o], 'Listener ' + o + ' ok');
}
}

for (o in resourceStore.events) {
if (resourceStore.events[o] && resourceStore.events[o].listeners && resourceStore.events[o].listeners.length) {
t.is(resourceStore.events[o].listeners.length, resourceListeners[o], 'Listener ' + o + ' ok');
}
}

s = t.getScheduler({
orientation : 'vertical',
renderTo : Ext.getBody(),
eventStore : eventStore,
resourceStore : resourceStore
});

s.destroy();

t.diag('Scheduler vertical rendered then destroyed');

for (var o in eventStore.events) {
if (eventStore.events[o] && eventStore.events[o].listeners && eventStore.events[o].listeners.length) {
t.is(eventStore.events[o].listeners.length, eventListeners[o], 'Listener ' + o + ' ok');
}
}

for (o in resourceStore.events) {
if (resourceStore.events[o] && resourceStore.events[o].listeners && resourceStore.events[o].listeners.length) {
t.is(resourceStore.events[o].listeners.length, resourceListeners[o], 'Listener ' + o + ' ok');
}
}
});

mitchellsimoens
1 Nov 2012, 5:48 AM
Some things I see


var store = new Ext.data.Store({
fields : ['foo']
});

/**
* Add a load listener
*/
store.on('load', function() {});

/**
* Do some individual check to see if there is a listener for the event
*/
console.log(store.hasListener('load')); // true
console.log(store.hasListener('datachanged')); // false
console.log(store.hasListener('destroy')); // true

/**
* Do a manual lookup of all listeners.
* This is basically the steps hasListener does only you get access
* to the actual listener
*/
var dispatcher = store.getEventDispatcher(),
observableId = store.getObservableId(),
targetType = store.observableType,
stacks = dispatcher.listenerStacks,
map = stacks[targetType],
event, listenersMap, order, listeners, l, lLen, listener;

if (map) {
map = map[observableId];

if (map) {
for (event in map) {
if (map.hasOwnProperty(event)) {
listenersMap = map[event].listeners;

for (order in listenersMap) {
if (listenersMap.hasOwnProperty(order)) {
listeners = listenersMap[order];
l = 0;
lLen = listeners.length;

for (; l < lLen; l++) {
listener = listeners[l];

//got each listener now
}
}
}
}
}
}
}

/**
* Or just remove all listeners
*/
store.clearListeners();
console.log(store.hasListener('load')); // false
console.log(store.hasListener('datachanged')); // false
console.log(store.hasListener('destroy')); // false

mankz
1 Nov 2012, 5:53 AM
That's some serious amount of code :). Will dig around to see if we can make something generic which we can use for our testing, thanks for the help mate!

mitchellsimoens
1 Nov 2012, 6:14 AM
What I usually do is have a getStoreListeners method which returns an object of event : function. I execute this to bind the listeners to the store and in the destroy I then use it also to unbind the listeners.


getStoreListeners : function() {
var me = this;

return {
load : me.onStoreLoad,
refresh : me.onStoreRefresh,
datachanged : [
me.onStoreDataChanged,
me.onStoreDataChanged2
]
};
}

I have a mixin which has the method to bind/unbind listeners (not crazy about my name tho :) ):


affectStoreListeners : function(add) {
var func = add ? 'on' : 'un',
store = this.getStore(),
listenerMap = this.getStoreListeners(),
event, fn, f, fLen;

for (event in listenerMap) {
if (listenerMap.hasOwnProperty(event)) {
fn = listenerMap[event];

if (Ext.isArray(fn)) {
f = 0;
fLen = fn.length;

for (; f < fLen; f++) {
store[func](event, fn[f], this);
}
} else {
store[func](event, fn, this);
}
}
}
}

It could be better like to support event options which would be trivial to do but has served me quite well.

So in destroy you co do this.affectStoreListeners(false)l