Search code examples
ember.jsember-router

How can I set a link to route with a dynamic segment


How can I set a link to route with a dynamic segment. According to guide I start with this

window.App = Ember.Application.create()
App.Router.map ->
  @resource 'products'
  @resource 'product', path: '/product/:product_id'

in my template:

{{#linkTo "product.1"}}products{{/linkTo}}

Unfortunately this gives me the follwing error:

Uncaught Error: assertion failed: The attempt to linkTo route 'product.1' failed. 
The router did not find 'product.1' in its possible routes: 'products', 'product', 'index' 

Solution

  • {{linkTo}} expects the route defined in the Router.map, so in according to your mapping it should be simply product.

    As for the dynamic segment, you also have to pass an object which will be serialized in the ProductRoute. The serialization in almost all scenarios occur without the developer having to do anything since Ember relies on conventions. In rare cases, one must implement serialize a little differently, but for most cases you don't have to touch it.

    If you're using {{linkTo}} inside an {{each}} loop you can do it like this:

    {{#each product in controller}}
        {{#linkTo product product}}Details{{/linkTo}}
    {{/each}}
    

    or

    {{#each controller}}
        {{#linkTo product this}}Details{{/linkTo}}
    {{/each}}
    

    Where the first argument is the route name and the second is your model object. In the first code the object has been also named as product, while in the second it's simply being passed as this, which is the product of the iteration.

    If you have an unusual scenario where you have to link to a dynamic route while not using the {{each}} loop, you have to expose the object in the controller (preferred) or view. Then you'd have to do something similar to the following:

    App.SomeController = Em.Controller.extend
      product: null
    
    App.SomeRoute = Em.Route.extend
      ### 
      controller is actually `SomeController` here
      model is not being used, and is null, while the actual model being
      supplied to the controller is `product`, retrieved from store
      ###
      setupController: (controller, model) ->
        product = App.Product.find 1
        controller.set 'product', product
        return
    

    While your template would be similar to this:

    {{#linkTo product controller.product}}Product{{/linkTo}}
    

    How does the route know the id?

    Conventions. The route will serialize the object you pass, and expose an object with a single property, which has the name of the model for that route, followed be "_id", which in this case would be product_id, so when you click that link, the app activates the ProductRoute, runs the serialize method creating that id property, which will subsequently be used as the argument of the model hook. That's where you call find passing params.product_id as argument. Then the model returns a promise of that model which will be used by the setupController, exposing the object to the view layer as controller.content or simply controller.