Skip to content

Instantly share code, notes, and snippets.

@thehydroimpulse
Forked from wycats/ember-routes.md
Created October 17, 2012 06:31

Revisions

  1. @wycats wycats revised this gist May 14, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ember-routes.md
    Original file line number Diff line number Diff line change
    @@ -32,7 +32,7 @@ App.stateManager = Ember.StateManager.create({
    // the application's main view is a ContainerView whose
    // view is bound to the manager's `currentView`
    manager.set('currentView', App.PostsView.create({
    controller: postsController
    controller: manager.get('postsController')
    }));
    },

  2. @wycats wycats revised this gist May 14, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ember-routes.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ at a simple example:
    App.stateManager = Ember.StateManager.create({
    start: Ember.State.extend({
    index: Ember.State.extend({
    route "/",
    route: "/",

    setupContext: function(manager) {
    manager.transitionTo('posts.index')
  3. @wycats wycats revised this gist May 13, 2012. 1 changed file with 61 additions and 2 deletions.
    63 changes: 61 additions & 2 deletions ember-routes.md
    Original file line number Diff line number Diff line change
    @@ -151,15 +151,14 @@ show: Ember.State.extend({
    The state manager will create the methods for you behind the scenes. You
    will get a state that looks like this:


    ```javascript
    show: Ember.State.extend({
    route: "/:post_id",
    modelType: 'App.Post',

    setupContext: function(manager, context) {
    var postsController = manager.get('postsController');
    postsController.set('selected', post);
    postsController.set('selected', context.post);

    manager.set('currentView', App.PostView.create({
    controller: postController
    @@ -174,4 +173,64 @@ show: Ember.State.extend({
    return { "post": App.Post.find(params['post_id']) }
    }
    })
    ```

    That makes the full example from above look like:

    ```javascript
    App.stateManager = Ember.StateManager.create({
    start: Ember.State.extend({
    index: Ember.State.extend({
    route "/",

    setupContext: function(manager) {
    manager.transitionTo('posts.index')
    }
    }),

    posts: Ember.State.extend({
    route: "/posts",

    setupContext: function(manager) {
    // the postsController is shared between the index and
    // show views, so set it up here
    var postsController = manager.get('postsController');

    postsController.set('content', Post.findAll());
    },

    index: Ember.State.extend({
    route: "/",

    setupContext: function(manager) {
    // the application's main view is a ContainerView whose
    // view is bound to the manager's `currentView`
    manager.set('currentView', App.PostsView.create({
    controller: postsController
    }));
    },

    showPost: function(manager, event) {
    // this event was triggered from an {{action}} inside of
    // an {{#each}} loop in a template
    manager.transitionTo('show', event.context);
    }
    }),

    show: Ember.State.extend({
    route: "/:post_id",
    modelType: 'App.Post',

    setupContext: function(manager, context) {
    var postsController = manager.get('postsController');
    postsController.set('selected', context.post);

    manager.set('currentView', App.PostView.create({
    controller: postController
    }))
    }
    })
    })
    })
    })
    ```
  4. @wycats wycats created this gist May 13, 2012.
    177 changes: 177 additions & 0 deletions ember-routes.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,177 @@
    # Routing in Ember

    In Ember, the application's state manager handles routing. Let's take a look
    at a simple example:

    ```javascript
    App.stateManager = Ember.StateManager.create({
    start: Ember.State.extend({
    index: Ember.State.extend({
    route "/",

    setupContext: function(manager) {
    manager.transitionTo('posts.index')
    }
    }),

    posts: Ember.State.extend({
    route: "/posts",

    setupContext: function(manager) {
    // the postsController is shared between the index and
    // show views, so set it up here
    var postsController = manager.get('postsController');

    postsController.set('content', Post.findAll());
    },

    index: Ember.State.extend({
    route: "/",

    setupContext: function(manager) {
    // the application's main view is a ContainerView whose
    // view is bound to the manager's `currentView`
    manager.set('currentView', App.PostsView.create({
    controller: postsController
    }));
    },

    showPost: function(manager, event) {
    // this event was triggered from an {{action}} inside of
    // an {{#each}} loop in a template
    manager.transitionTo('show', event.context);
    }
    }),

    show: Ember.State.extend({
    route: "/:post_id",

    setupContext: function(manager, post) {
    var postsController = manager.get('postsController');
    postsController.set('selected', post);

    manager.set('currentView', App.PostView.create({
    controller: postController
    }))
    },

    serialize: function(manager, post) {
    return { "post_id": post.get('id') }
    },

    deserialize: function(manager, params) {
    return Post.find(params["post_id"]);
    }
    })
    })
    })
    })
    ```

    The primary goal of Ember routing is ensuring that the code that runs when you enter a state
    programmatically is the same as the code that runs when you enter a state through the URL.

    In general, once you have set up your router, navigating through your application should
    automatically update the URL. Sharing that URL across sessions or through social media
    should result in exactly the same path through the application hierarchy.

    Let's take a look at what happens in both situations with this simple example:

    ## Scenario 1: Entering at /

    When the user enters at `/`, the state manager will start out in the `start` state. It
    will immediately move into the `start.index` state. Because the route for the `index`
    state has no dynamic segments, the state manager passes no context to the setupContext
    event.

    In this case, the index state immediately moves the state manager into the default view
    for this application, the `posts.index` state.

    When the state manager transitions into the `posts.index` state, it generates a URL for
    the current state, `/posts`, and sets the current URL to `/posts` (either using hash
    changes or pushState as appropriate).

    Entering the `posts.index` state will also render a list of all of the posts as the
    current view. Clicking on a post will trigger the state's `showPost` event with that
    post object as its context.

    The `showPost` event will transition the state manager into the `posts.show` state,
    passing the post as its context.

    In this case, the `posts.show` state has a dynamic segment (`:post_id`), so the
    state manager invokes the `serialize` method with the context. Assuming the post
    has an id of 45, the `serialize` method returns `{ "post_id": 45 }`, which the
    state manager uses to generate the state's portion of the URL.

    ## Scenario 2: Entering at /posts/45

    Now that the user has navigated to a state corresponding to a URL of `/posts/45`,
    he may want to bookmark it or share it with a friend. Let's see what happens when
    a user enters the app at `/posts/45`.

    First, the app will descend into the `posts` state, whose route matches the first
    part of the URL, `/posts`. Since the state has no dynamic segment, it invokes the
    `setupContext` method with no context, and the state sets up the `postsController`,
    exactly as before when the app programmatically entered the state.

    Next, it descends into `posts.show`. Since `posts.show` has a dynamic segment,
    the state manager will invoke the state's `deserialize` method to retrieve its
    context.

    The `deserialize` method returns a `Post` object for id `45`. To finish up, the
    state manager invokes `setupContext` with the deserialized `Post` object.

    # Integration with ember-data (or other models)

    NOTE: Implementation of this section is still TODO

    While the above system is cool, writing a serialize and deserialize method for
    every state containing a dynamic segment can get somewhat repetitive.

    To avoid this, you can specify a `modelType` on a state, and you will get
    default methods. For example, if you specify a `modelType` of `App.Post`,
    like this:

    ```javascript
    show: Ember.State.extend({
    route: "/:post_id",
    modelType: 'App.Post',

    setupContext: function(manager, context) {
    var postsController = manager.get('postsController');
    postsController.set('selected', context.post);

    manager.set('currentView', App.PostView.create({
    controller: postController
    }))
    }
    })
    ```

    The state manager will create the methods for you behind the scenes. You
    will get a state that looks like this:


    ```javascript
    show: Ember.State.extend({
    route: "/:post_id",
    modelType: 'App.Post',

    setupContext: function(manager, context) {
    var postsController = manager.get('postsController');
    postsController.set('selected', post);

    manager.set('currentView', App.PostView.create({
    controller: postController
    }))
    },

    serialize: function(manager, context) {
    return { "post_id": context.post.get('id') };
    },

    deserialize: function(manager, params) {
    return { "post": App.Post.find(params['post_id']) }
    }
    })
    ```