This is very similar to how we do things -- one 'top level' controller that's globally-accessible, and using "var self = this" to push data back into a controller when we're deep in callbacks. I'm not sure whether it's truly a "best" practice, but for us it hits a sweet spot where most of our variables and data are nicely scoped and non-global, but the one or two bits that almost all other components need to interact with occasionally (like that top-level controller, and information about authentication and preferences) are easily accessible without having to drill through a long list of accessors.
That said, we only use something like the global "app.contacts" for our one top-level controller and data which really needs to pervade the entire page: all of our subcontrollers and their local data are not globally-accessible. (E.g., if your Contacts controller were invoking further controls for things like pagination or tabs, we'd colloquially call those "sub-controllers".)
Sometimes our 'top' controller will keep a reference to its sub-controllers, but lately we've shifted to have them communicate via events instead. (Justin Meyer wrote an article about this; we've recently been using option 2, "widgets that produce events and you update the observe yourself", except we're using it for controller-controller interactions in addition to controller-observe interactions.)
So, instead of:
This has been working very well, so far -- especially for pages which have a lot of smaller controllers doing stuff, since we can broadcast an event to many targets at once (e.g., $('form').trigger('form.disable-while-submitting');, on a page with a dozen forms which cannot be submitted simultaneously) and since this lets us destroy controller instances or invoke new controllers on the fly without having to keep the 'top' controller in sync or worry about old refs.
In theory, it should be better/preferable for individual controllers to broadcast state- and interaction-related events (instead of "command-like" events), but either option seems (to me) like a good alternative to the "apps" global (or any other way to touch controller instances directly) for things other than the main top-level controller.
That said, we only use something like the global "app.contacts" for our one top-level controller and data which really needs to pervade the entire page: all of our subcontrollers and their local data are not globally-accessible. (E.g., if your Contacts controller were invoking further controls for things like pagination or tabs, we'd colloquially call those "sub-controllers".)
Sometimes our 'top' controller will keep a reference to its sub-controllers, but lately we've shifted to have them communicate via events instead. (Justin Meyer wrote an article about this; we've recently been using option 2, "widgets that produce events and you update the observe yourself", except we're using it for controller-controller interactions in addition to controller-observe interactions.)
So, instead of:
- this.createUserController = new FormController('#create-user', { ... });
- // ...
- this.createUserController.doSomethingSpecial();
- new FormController('#create-user', { ... });
- // ...
- this.element.find('#create-user').trigger('namespace.do-something');
This has been working very well, so far -- especially for pages which have a lot of smaller controllers doing stuff, since we can broadcast an event to many targets at once (e.g., $('form').trigger('form.disable-while-submitting');, on a page with a dozen forms which cannot be submitted simultaneously) and since this lets us destroy controller instances or invoke new controllers on the fly without having to keep the 'top' controller in sync or worry about old refs.
In theory, it should be better/preferable for individual controllers to broadcast state- and interaction-related events (instead of "command-like" events), but either option seems (to me) like a good alternative to the "apps" global (or any other way to touch controller instances directly) for things other than the main top-level controller.