Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Controller instances not available in connectOutlets of root state. #941

wants to merge 2 commits into from

8 participants


I'm not sure if this is intended, but controller instances are not available in the root state when the connectOutlets event is fired. This fiddle demonstrates.


The cause of this is due to Ember.StateManager automatically transitioning to the initialState upon creation. This triggers connectOutlets before injections happen.


This seems like it's worth fixing but I would like to see what @tomdale and @wycats think.


:+1: I worked around this by using a binding...

// initialState
connectOutlets: (router) ->
  rootView = Ember.ContainerView.create
    currentViewBinding: 'App.stateManager.applicationController.view'


@digitaltoad What are you trying to do in connectOutlets of the root state? None of the test cases show connectOutlets on the root state. I'm not so sure that's the convention even though the original gist shows it done that way.


Sorry, I should have added a test for that as well. I've been going by the original gist. In current apps, instead of placing setup code in the root connectOutlets, I've created a single state inside root that would house the rest of my app states so that I would have access to the controllers there. This seemed like the way root was supposed to work. I'm not sure of a better way to defer the start of the router without the flag that I've inserted that wouldn't break any apps that assumed the router would automatically transition to the root state.


Okay, disregard my first comment. I figured all of this out. I made you a gist. Let me know if that helps.


My problem arises from an example where the page is loaded at a state. For instance, I've extracted the navbar area. Where would the setup go for this. If the app loads at the home state, everything is fine. If the app loads at the profile state, the navbar is never loaded. Would the navbar have to be connected in every state or is there just a better way of handling this? This might of course just be a convention issue.


You're not gaining anything by using connectOutlets for that. The real power of connectOutlet is passing a context in. A navbar likely has no context. If any part of it is dynamic, you probably want to just bind to something on applicationController, or have your own navbarController. Just use the Handlebars view helper. see here

Does that make sense?


This does indeed make sense. I had gone down the route of using a view helper before I learned about outlets. However, the question remains, if connectOutlets is still fired in the root state, should everything be wired and ready at that point?


@digitaltoad This doesn't merge cleanly. Do you mind rebasing?


This pull request passes (merged 20d17d2 into d71677b).


This looks good and would solve some annoyances for me as well. @wycats, @tomdale, what do you guys think?


I agree that this is an important fix, but I think this hacks around the problem instead of addressing the core issue. Instead of special-casing the router to not automatically transition into the initialState, we should just make sure that the initialState's connectOutlets call back doesn't happen until the state manager has finished initializing.


@tomdale So are you suggesting we delay the transitionEvent? This seems like you run into the same problem.


I had tried to figure out a way to do this without a hack. Maybe if I revisit I can come up with something else. My first thought was indeed to only fire the initial transitionEvent after the state manager was completely initialized (including injections).


To clarify, both the initialState and the transitionEvent (connectOutlets) are handled by the StateManager class. Wherever we make the change, we're going to have to "hack" around it in the router. Delaying the initialState makes sense to me since the router really doesn't get kicked off until the first route call.


I wanted to use connectOutlets on root state for an identical reason: setting up other main areas of the app that get their own controllers. For now, I'm doing

 {{view App.SearchView controllerBinding="App.router.searchController"}}

But I'd like to see this usage of connectOutlets in 1.0. @tomdale you seem OK statemanger being changed so the transition can be delayed?

@wycats wycats was assigned

Sorry to pester, but were we going to pull this PR or wait to implement @tomdale's suggestion of making a statemanager's root state transitionEvent not trigger until the manager is done being initialized?


What's the state of this? I'm using trek's hack in the meantime but would be nice to reference at least applicationController in the root connectOutlets


Since routing is something that's been design by @wycats and @tomdale I'd really like to hear their opinion on the correct solution for this.


A solution to this is definitely desired and is targeted for Router v2.

@wycats wycats closed this
@knusul knusul referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
3  packages/ember-application/lib/system/application.js
@@ -107,7 +107,7 @@ Ember.Application = Ember.Namespace.extend(
namespace = this, controller, name;
if (!router && Ember.Router.detect(namespace['Router'])) {
- router = namespace['Router'].create();
+ router = namespace['Router'].create({ autoInitialState: false });
this._createdRouter = router;
@@ -166,6 +166,7 @@ Ember.Application = Ember.Namespace.extend(
+ router.initialize();
location.onUpdateURL(function(url) {
12 packages/ember-states/lib/state_manager.js
@@ -373,6 +373,8 @@ require('ember-states/state');
Ember.StateManager = Ember.State.extend(
/** @scope Ember.StateManager.prototype */ {
+ autoInitialState: true,
When creating a new statemanager, look for a default state to transition
into. This state can either be named `start`, or can be specified using the
@@ -383,6 +385,16 @@ Ember.StateManager = Ember.State.extend(
set(this, 'stateMeta', Ember.Map.create());
+ var initialState = get(this, 'initialState'),
+ autoInitialState = get(this, 'autoInitialState');
+ if (autoInitialState) {
+ this.initialize();
+ }
+ },
+ initialize: function() {
var initialState = get(this, 'initialState');
if (!initialState && getPath(this, 'states.start')) {
9 packages/ember-states/tests/state_manager_test.js
@@ -240,6 +240,15 @@ test("it triggers setup on initialSubstate", function() {
ok(grandchildSetup, "sets up grandchild");
+test("it does not automatically transition to a default state when autoInitialState is false", function() {
+ stateManager = Ember.StateManager.create({
+ autoInitialState: false,
+ start: Ember.State.create()
+ });
+ equal(get(stateManager, 'currentState'), undefined, "does not transition to initial state");
test("it throws an assertion error when the initialState does not exist", function() {
raises(function() {
Something went wrong with that request. Please try again.