@macu’s suggestion is a great one, and it will get you pretty far.
Here are some things we’re discovering as we go down this path:
Idempotent Loading
You’ll want some way of remembering which code has been loaded already. The Container
and IndexRoute
are both decent places to record this. Or you can build a ModuleLoaderService
that remembers what’s been loaded. That’s particularly useful if modules can declare hard dependencies on other modules.
May-Fail & Delay-Load Modules
You may find cases where a module is optional or at least non-critical. That is, without it, some feature of the app won’t work, but you don’t necessarily want to block the user from using the rest of the app if it’s not available. Alternatively, you might want to let the app boot without the module, then fill in the missing pieces later. There are a few tools that will help here.
{{outlet}}
s in your layout
In your root view, you might have {{outlet recent-tweets}}
. Then, in the recent_tweets.js
module, you get a reference to that root view and call connectOutlet('recent-tweets', RecentTweetsView.create());
to inject the actual content.
Null controllers & services
If module A
wants some information from module B
(say, a BController
), but B
isn’t guaranteed to be loaded before A
, A
can define a NullBController
that B
replaces. For example, A
calls container.register('controller:b', NullBController);
. Then, B
calls container.unregister('controller:b'); container.register('controller:b', RealBController);
.
The problem here is that if A
has already asked for an instance of this, it’s hard for B
to reach in to A
to swap in the real one. The user may not see the B
functionality until they leave the current route and re-enter it. Liberal use of events (particularly on the router) may help with this; we haven’t explored that option.
Hot-Upgrading Templates & Views
This is about as tricky as the previous. Say someATemplate
has {{outlet b-content}}
. The ARoute
can call this.render('someBTemplate', { outlet: 'b-content' });
, but if B
isn’t loaded, render
won’t be able to find someBTemplate
and will raise an assertion error. A
can compensate by defining a nullSomeBTemplate
. When B
loads, it might be able to
- unregister the
nullSomeBTemplate
- register the real one
- ask the router for the current route
- call
route.render('someBTemplate', { outlet: 'b-content' })
to upgrade the view
Summary
I think there are promising things in this area, but we don’t yet have all the answers. I’d love to see the community build an add-on that solves some of the above problems. I’d be happy to help.