How do you set up controller selections when routi

2019-07-21 06:23发布

If I have a route to a state deep in the application, how do I ensure that the proper controller set up has been done although I'm going to enter the inner state directly?

For example,

  • state A
    • state a, representRoute: 'a'
    • state B
      • state b, representRoute: 'a/b'
      • state C
        • state c, representRoute: 'a/b/c'
        • state D
          • state d, representRoute: 'a/b/c/d
          • state E

As you can see you can route directly to states 'a', 'b', 'c' or 'd', but what you can't see is that normally you would go between these states by selecting an item in a controller, which would then trigger a state transition to the deeper state. The problem then is that when you go directly to state 'd' none of your controllers' selections are set up.

So far I've used enterStateByRoute in state 'a' to set the selection of the first controller and then had to use enterStateByRoute in state 'b' to do the selection of the first and second controllers, etc. all the way to enterStateByRoute in state 'd' to do the selection of each controller all the way along. This is quite wasteful, because I end up repeating the same code in each enterStateByRoute.

What is the best way to set the controller selection to match the directly routed state?

标签: sproutcore
1条回答
放我归山
2楼-- · 2019-07-21 06:33

I was able to improve the situation greatly once I realized that enterStateByRoute is called on all parent states in the chain when routing. This means that if state 'c' matches the route, state 'A' will be entered, followed by state 'B' and state 'C' before finally entering state 'c' last. What I didn't realize before was that each of these states is passed the SC.StateRouteHandlerContext object as it is entered allowing you to either check the context in enterState or implement enterStateByRoute in any of the states.

My solution then was to add enterStateByRoute to state 'A' to set the first controller, add enterStateByRoute to state 'B' to set the second controller, etc. For example, in this way, any state past state 'A' is guaranteed to have the first controller selection set and I don't have any duplicated code down the chain.

For example,

// … 
state_A: SC.State.extend({
  initialSubstate: 'state_a',

  enterStateByRoute: function (context) {
    // select object on controller 1 since we are routing
  },

  state_a: SC.State.extend({
    representRoute: 'a',

    enterStateByRoute: function (context) {
      // do setup for state 'a' specific to routing
    }
  }),

  state_B: SC.State.extend({
    initialSubstate: 'state_b',

    enterStateByRoute: function (context) {
      // select object on controller 2 since we are routing
    },

    state_b: SC.State.extend({

      enterStateByRoute: function (context) {
        // do set up for state 'b' specific to routing
      },
// …

The only problem I encountered was that because I had bound all my controllers together, the selection change doesn't propagate immediately and so I would select an object on a controller in the first state, enter the next state and find that the bound controllers' content would not yet have updated.

So I could have waited for bindings to flush by returning an SC.Async object in enterStateByRoute and used this.invokeLast(function () { this.resumeGotoState(); }) to go to the next state at the end of the run loop, but instead I took a declarative approach and simply set/unset each controller's content as I enter/exit the appropriate state.

查看更多
登录 后发表回答