I've spent about 5 hours on this problem, so I thought I'd share it with the community. If you can point to something that I'm doing wrong, I'd love to know what it is. Otherwise, maybe this post will save somebody else the agony I just went through to figure this out.
Problem: my nav bar buttons' click event listeners were not firing. Actually, they were sometimes firing, but eventually, they would stop firing.
All my code is highly modularized in CommonJS modules, per best practice. Now you might argue that my parasitic inheritance pattern is not best practice, but I've had much success with it, and I like it, so I'm sticking with it.
Another app I built used nav bar buttons created inside of a CommonJS module, and had no problems with them. But this new app was not behaving. Naturally, I started pulling the apps apart to see what the difference was between the apps.
The culprit turned out to be something so ridiculously subtle that it took me 5 hours to isolate it.
See my sample code below. FooWin would normally be in a separate module, but for simplicity, I put it inline.
The main application code calls the FooWin() constructor two different ways -- once with an object literal passed in as the first argument, and once with an object variable passed in.
If I pass an object literal, the events stop firing (try clicking the nav buttons -- you'll get console output for a couple of clicks, but then nothing). If I pass a variable, the events work as expected.
If you look inside the module, you can see that I'm adding the rightNavButtons property to the params array. So if I'm calling it with a variable, a side effect of the constructor is that there is now a reference to my nav buttons in the app.js scope. If I pass an object literal, there is no reference to those buttons in the app.js scope. I suspect this has something to do with the bad behavior; perhaps my event listeners are getting garbage collected?
But the thing I don't get is that button b3 doesn't suffer from this -- there's no reference to b3 in the app.js scope, and it works perfectly. Proper module behavior would mandate that the references to variables, controls, and eventListeners be maintained, even if they are completely contained within the scope of the module.
So I suspect this is a bug in the Titanium core.
function FooWin (params) { var _self; var b1 = Ti.UI.createButton ({ title: "1" }); b1.addEventListener ('click', function (e) { Ti.API.info ("1 clicked"); }); var b2 = Ti.UI.createButton ({ title: "2" }); b2.addEventListener ('click', function (e) { Ti.API.info ("2 clicked"); }); params.rightNavButtons = [b1, b2]; _self = Ti.UI.createWindow(params); var b3 = Ti.UI.createButton ({ title: "3" }); b3.addEventListener ('click', function (e) { Ti.API.info ("3 clicked"); }); _self.add (b3); return _self; } var main_win = Ti.UI.createWindow ({ title: "test", backgroundColor: '#ddd' }); var navwin = Ti.UI.iOS.createNavigationWindow ({ statusBarStyle: Ti.UI.iPhone.StatusBar.LIGHT_CONTENT, window : main_win }); // instantiate a FooWin, passing an object literal; nav buttons may respond // to a few clicks, but will stop at some point var foowin1 = new FooWin ({ title: "foo", backgroundColor: '#fff' }); var button1 = Ti.UI.createButton ({ top: 100, title: 'Foo - object literal' }); button1.addEventListener ('click', function() { navwin.openWindow (foowin1); }); // instantiate a FooWin, passing an object variable; nav buttons work as expected var params = { title: "foo", backgroundColor: '#fff' }; var foowin2 = new FooWin (params); var button2 = Ti.UI.createButton({ top: 200, title: 'Foo - object variable' }); button2.addEventListener ('click', function() { navwin.openWindow (foowin2); }); main_win.add (button1); main_win.add (button2); navwin.open ();