Opened 10 years ago

Closed 7 years ago

#10467 closed defect (wontfix)

Methods advised with AOP do not handle dojo.connect listeners correctly

Reported by: dgreene Owned by: Eugene Lazutkin
Priority: high Milestone: future
Component: Dojox Version: 1.3.2
Keywords: AOP aspect advice advise connect listener dojox.lang.aspect dojo.connect Cc:
Blocked By: Blocking:

Description

There seems to be an issue when using AOP and dojo.connect together. I have a class with a function on it, and I create an advice for the function. This advice runs for all instances of the class (i.e. it is applied to the class' prototype). Later in my code, I create an instance of the class and I connect to that instance's function using dojo.connect. Theoretically the connect should only run for that one instance of the class. However, what happens is that the connect is run for every subsequent instance of the class that is created. See the example below:

<html>
<head>
	<script type="text/javascript" src="js/dojoroot/dojo/dojo.js" djConfig="isDebug:true"></script>
	<script type="text/javascript">
		dojo.require("dojox.lang.aspect");
		
		//Declare a simple class that has a simple function doStuff()
		dojo.declare("Example", null, {
			id: '',
			constructor: function(params) {
				dojo.mixin(this, params);
			},
			
			doStuff: function() {
				console.log(this.id, " did stuff!");
			}
		});
		
		function onLoad() {
			dojo.addOnLoad(function() {
				
				//Create some advice for the function
				dojox.lang.aspect.advise(Example, "doStuff", {
					after: function() {
						console.log("doStuff (AOP after)");
					}
				});
				
				//Create a new instance of the class
				var a = new Example({id: 'a'});
				//Connect to the advised function, but note the connection should only apply for the instance a
				dojo.connect(a, "doStuff", "handleStuff");
				
				//Create another instance but do not connect to this instance
				var a2 = new Example({id: 'a2'});
				
				//When a is called, the advice should run and the connect should run
				callDoStuff(a);
				
				//When a2 is called, only the advice should run. 
				//However the connect, which was applied only to a, runs here too.
				callDoStuff(a2);
			});
		}
		
		function callDoStuff(obj) {
			//put the function call inside a console grouping to make it easier to see what gets called when
			console.group("Calling doStuff on", obj.id);
			obj.doStuff();
			console.groupEnd();
		}

		function handleStuff() {
			console.log("handleStuff (dojo.connect)");
		}
	</script>
</head>
<body onload="onLoad()">
</body>
</html>

The crux of the problem is that the _listeners array is shared between the aspect and the connect. The advice should apply to all instances whereas the connect should only apply to a specific instance. I initially noticed this confusing behavior when trying to create advice for the onLoad of ContentPane? and then also connecting to onLoad of a specific ContentPane? instance. The behavior happens in both FF3.5 and IE6 using Dojo 1.3.2.

P.S. Here is a workaround I am using for the time being (basically just wrap the aspect in a new function so that the _listeners array can't be reused):

//call after creating the advice, and before adding a dojo.connect
fixAspectForConnect(Example, "doStuff");

//wrap the function in a new method
function fixAspectForConnect(obj, methodName) {
	obj = obj.prototype;
	var old = obj[methodName];
	if (old) {
		obj[methodName] = function() {
			var c = arguments.callee;
			if (c.old) {
				c.old.apply(this, arguments);
			}
		};
		obj[methodName].old = old;
	}
}

Change History (3)

comment:1 Changed 10 years ago by bill

Component: GeneralEvents
Owner: changed from anonymous to Eugene Lazutkin

comment:2 Changed 9 years ago by Eugene Lazutkin

Component: EventsDojox
Milestone: tbdfuture
Status: newassigned

Next time please attach files rather than pasting code directly in the ticket.

comment:3 Changed 7 years ago by bill

Resolution: wontfix
Status: assignedclosed

Probably we should close this as wontfix, since this code is superceded by dojo/aspect which presumably doesn't have an issue.

Note: See TracTickets for help on using tickets.