Search code examples
javascriptember.jsember-router

How should I do a redirect to a path with an object in ember?


I'm trying to put together a data-driven menu system in Ember.js. Here's the javascript, js/nodal.js:

window.Form = Ember.Application.create();

var menuOptions = [
{id: "root",
 options: [ "a", "b" ]},
{id: "a",
      name: "Option A",
 options: [ "c", "d" ]},
{id: "b",
  name: "Option B",
 options: [ "e", "f" ]},
{id: "c",
 name: "Option C"},
{id: "d",
 name: "Option D"},
{id: "e",
 name: "Option E"},
{id: "f",
 name: "Option F"}
]

Form.MenuOption = Ember.Object.extend({});

var builtOptions = menuOptions.map( function(attrs) {
return Form.MenuOption.create( attrs );
});

Form.options = {};
for( i = 0; i < builtOptions.length; i++ ) {
var opt = builtOptions[i];
Form.options[opt.id] = opt;
}

Form.MenuRoute = Ember.Route.extend({
model: function(params){
    console.log( params );
    if( !params || !params.index_id || params.index_id == "undefined") {
    params.index_id = "root";
    }
    var keys = Form.options[params.index_id].options;
    var options = new Array();
    keys.map(function(k){ options.push( Form.options[k] ); });
    return options;
  }    
});

Form.IndexRoute = Ember.Route.extend({
redirect: function(){
        /* This here is the problem line... */
    this.transitionTo( "menu", Form.options.root );
}
});

Form.Router.map(function(){
this.resource( "menu", { path: "/menu/:index_id" } );
});

And here's the HTML, such as it is:

  <title>Hello</title>
  <meta name="description" content="My First Ember.js App">
  <meta name="author" content="Your Name Here">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <script data-template-name="application" type="text/x-handlebars">
<h1>Title Goes Here</h1>
{{outlet}}
  </script>


  <script data-template-name="menu" type="text/x-handlebars">
<ul>
{{#each controller}}
  <li>{{#linkTo menu this}}{{name}}{{/linkTo}}</li>
{{/each}}
</ul>
  </script>


  <script src="js/libs/jquery-1.9.1.js"></script>
  <script src="js/libs/handlebars.js"></script>
  <script src="js/libs/ember-1.0.0-rc.1.js"></script>
  <script src="js/nodal.js"></script>
</body>
</html>

What I want is for the user to be redirected to /menu/root as soon as they open the page. So far I haven't been able to find the right incantation to make this work. When I load the above, I get this exception:

Error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. You passed <(generated menu controller):ember172>

If I load index.html#/menu/root directly, it works - it's only the redirect that's not working. Replacing the this.transitionTo line in the IndexRoute definition with this:

this.transitionTo( "menu", [Form.options.root] );

does redirect, but it redirects to /menu/undefined, not /menu/root. That's ugly, and obviously broken.

What have I missed?

EDIT

As suggested, I tried adding an ArrayController:

Form.MenuController = Ember.ArrayController.extend({});

This gives me a new error:

arrangedContent.addArrayObserver is not a function

Solution

  • The problem is that your URL assumes you are working with one model, but in transitioTo and your model hook your are passing/returning an array. Your MenuRoute is always about one MenuOption, right?

    Therefore i would suggest:

    Form.MenuRoute = Ember.Route.extend({
    model: function(params){
        console.log( params );
        if( !params || !params.index_id || params.index_id == "undefined") {
        params.index_id = "root";
        }
        var menuOption = Form.options[params.index_id];
        return menuOption;
      }    
    });
    

    Your corresponding template should look like this in this case:

    <script data-template-name="menu" type="text/x-handlebars">
    <ul>
    {{#each option in controller.content.options}}
      <li>{{#linkTo menu option}}{{option.name}}{{/linkTo}}</li>
    {{/each}}
    </ul>
    </script>