JavaScript Loading a single model with Ember.js

I'm trying to write a pretty simple app: I have some games (say chess, tictactoe... whatever), and there are several boards of each game. I want to show a list of games, and then show a list of boards when you click on a game.

But I'm facing a lot of problems. I'll first describe them, and I'll paste the code after that:

  1. The list of games is correctly shown. No problem here.
  2. When I click a game, I get this error:

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

    BUT if I press F5 or write the URL manually, everything works fine. And I have no idea why.

    UPDATE: I've seen that if I change the games template, changing the {{#linkTo}} with a hand-written link, everything works OK:

    • This is the non-working linkTo: {{#linkTo 'game' game}}{{game.name}}{{/linkTo}}. It builds the URL correctly, but it fails when I click on it.
    • This is a hand-written <a> tag: <a target='_blank' href="#/games/{{unbound game.id}}">{{game.name}}</a>. It works perfectly.
  3. The Url of each board should follow this format:

    /games/1/boards/5

    But when I write the {{#linkTo 'board' board}} what I get is:

    /games/undefined/boards/5

Here is the code (You can see a "working" copy here in JBin. But it's not functional, because it relays on a local REST app):

The Router:

MGames.Router.map(function () {
    this.resource('games', function () {
        this.resource ('game', {path: '/:game_id'}, function () {
            this.resource('board', {path: '/boards/:board_id'});
        });
    });
});

MGames.IndexRoute = Ember.Route.extend({
    redirect: function () {
        this.transitionTo('games');
    }
});

MGames.GamesRoute = Ember.Route.extend ({
    model: function() {
        return MGames.Game.findAll();
    }
});

MGames.GameRoute = Ember.Route.extend ({
    model: function(params) {
        return MGames.Board.findAllByGame(params.game_id);
    }
});

MGames.BoardsRoute = Ember.Route.extend ({
    model: function(params) {
        return this.modelFor('game').then(
            function (game) {
                return MGames.Board.find(game.get('id'), params.board_id);
            }
        );
    }
});

The models:

MGames.Game = Ember.Object.extend({
    id: null,
    name: null,
    icon: null
});

MGames.Game.reopenClass({
    findAll: function() {
        var url = [MGames.GAMES_API_URL];
        url.push ('games');
        url = url.join('/');

        var result = Ember.ArrayProxy.create({ content: [] });

        $.getJSON(url).then (
            function (response) {
                response.forEach(function (child) {
                    result.pushObject (MGames.Game.create(child));
                });
            }
        );

        return result;
    },

    find: function (id) {
        var url = [MGames.GAMES_API_URL];
        url.push ('games');
        url.push (id);
        url = url.join('/');

        var game = MGames.Game.create({ isLoaded: false });

        $.getJSON(url).then (
            function(response) {
                game.setProperties(response);
                game.set('isLoaded', true);
            }
        );

        return game;
    }
});

MGames.Board = Ember.Object.extend({
    id: null,
    name: null,
    owner: null,
    game: null,
    is_public: null,
    created_at: null
});

MGames.Board.reopenClass({
    findAllByGame: function (game) {
        var url = [MGames.GAMES_API_URL];
        url.push ('games');
        url.push (game);
        url.push ('boards');
        url = url.join('/');

        var result = Ember.ArrayProxy.create({ content: [] });

        $.getJSON(url).then (
            function (response) {
                console.log (response);
                response.forEach(function (child) {
                    result.pushObject (MGames.Board.create(child));
                });
            }
        );

        return result;
    },

    find: function (game, board) {
        url = [MGames.GAMES_API_URL];
        url.push ('games');
        url.push (game);
        url.push ('boards');
        url.push (board);
        url = url.join('/');

        var result = MGames.Board.create();

        $.getJSON(url).then (
            function(response) {
                result.setProperties(response);
            }
        );

        return result;
    }
});

And the template:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>MGames</title>
  <link rel="stylesheet" target='_blank' href="css/bootstrap.css">
  <link rel="stylesheet" target='_blank' href="css/main.css">
</head>
<body>

  <script type="text/x-handlebars">
    <div class="navbar navbar-fixed-top">
      <div class="navbar-inner">
        <div class="container">
          <div class="nav-collapse collapse">
            <ul class="nav">
              <li class="active">{{#linkTo 'index' class="brand"}}MGames{{/linkTo}}</li>
            </ul>
          </div>
        </div>
      </div>
    </div>

    <div class="container">
      {{outlet}}
    </div>
  </script>

  <script type="text/x-handlebars" id="games">
    <div class="row">
      <header id="header">
        <h1>Games</h1>
      </header>
      <ul>
      {{#each game in controller}}
      <li>
        {{#linkTo 'game' game}}{{game.name}}{{/linkTo}}
      </li>
      {{/each}}
      </ul>

      {{outlet}}
    </div>
  </script>

  <script type="text/x-handlebars" id="game">
    <div class="row">
      <div class="span3" id="boards">
        <header id="header">
          <h1>Boards</h1>
        </header>

        <ul id="board-list">
          {{#each board in controller}}
          <li>
            {{#linkTo 'board' board}}{{board.name}}{{/linkTo}}
          </li>
          {{/each}}
        </ul>
      </div>
      <div class="span9">
        {{outlet}}
      </div>
    </div>
  </script>

  <script type="text/x-handlebars" id="board">
    <header id="header">
      <h1>{{game.name}} - {{name}} <small>{{owner.nickname}}</small></h1>
    </header>
  </script>

  <script src="js/libs/jquery.js"></script>
  <script src="js/libs/bootstrap.js"></script>
  <script src="js/libs/handlebars.js"></script>
  <script src="js/libs/ember.js"></script>

  <script src="js/application.js"></script>
  <script src="js/router.js"></script>
  <script src="js/models/game.js"></script>
  <script src="js/models/user.js"></script>
</body>
</html>
Answer:1

Ok, I finally figured out the problem. In my gameRoute I wrote this:

MGames.GameRoute = Ember.Route.extend ({
    model: function(params) {
        return MGames.Board.findAllByGame(params.game_id);
    }
});

It worked when I write the URL directly in the browser bar, because Ember calls the model function, but when following a {{#linkTo}} the model is the one passed as parameter, so the modelfunction isn't called.

So the working code is this one (a little bit simplified):

MGames.GameRoute = Ember.Route.extend ({
    model: function (params) {
        // This is only called when loading the URL directly,
        // not following a link. We load the game, and in the
        // setupController we'll load the boards.
        return MGames.Game.find(params.game_id);
    },
    setupController: function(controller, game) {
        // This is *always* called, so we load the boards
        model = MGames.Board.findAllByGame(game.id);
        controller.set('model', model);
    }
});
Answer:2

function f(message) { console.log(message,arguments); } f("H", "e", "l", "l", "o"); It shows: H + ["H", "e", "l", "l", "o"] Question: why message only shows H, not ["H", "e", "l", "l", "o"]?

function f(message) { console.log(message,arguments); } f("H", "e", "l", "l", "o"); It shows: H + ["H", "e", "l", "l", "o"] Question: why message only shows H, not ["H", "e", "l", "l", "o"]?

Suppose I want to call some JavaScript function. I should keep the required resource file (the JavaScript file) in the application folder and link. But what are the built-in functions like eval(), ...

Suppose I want to call some JavaScript function. I should keep the required resource file (the JavaScript file) in the application folder and link. But what are the built-in functions like eval(), ...

  1. javascript function loaded
  2. javascript function not loaded
  3. javascript function page loaded
  4. javascript check if function loaded
  5. javascript document loaded function
  6. javascript function after page loaded
  7. javascript function after dom loaded
  8. javascript run function when loaded
  9. javascript wait function loaded

So I have 5 grunt-contrib-watch tasks: sass to compile sass files testConcat to concat tests implementationConcat to concat implementation files for testing templates to precompile handlebars ...

So I have 5 grunt-contrib-watch tasks: sass to compile sass files testConcat to concat tests implementationConcat to concat implementation files for testing templates to precompile handlebars ...

I have these two simple functions that change the body background color <script type="text/javascript"> function lightson(){ document.body.bgColor="#EBEBEB"; } function lightsoff(){ ...

I have these two simple functions that change the body background color <script type="text/javascript"> function lightson(){ document.body.bgColor="#EBEBEB"; } function lightsoff(){ ...