ember.js中的多个动态段

时间:2013-05-23 00:33:49

标签: ember.js

我目前的路线定义如下:

App.Router.map(function() {
    this.resource('players', { path: ':page_id' }, function() {
        this.resource('player', { path: ':player_id' });
    });
});

我的想法是左边有一个玩家名单。显示的播放器名称取决于page_id。在右侧,我根据player_id显示单个玩家及其所有信息。问题是,两者都是独立的,这意味着我可以在第三个玩家页面上,同时显示列表中的第一个玩家,或根本没有玩家。

我一直在尝试做的是这样的事情,这是PlayersController中的一个方法,当我点击进入下一个玩家页面时会被调用:

doTransition: function() {
    var players = App.Player.findAllForPage(this.playersPerPage, this.currentOffset);
    players.reopen({
        id: this.currentOffset
    });
    var playerController = this.get('controllers.player');
    var currentPlayer = playerController.getWithDefault('content');
    if (currentPlayer) {
        this.transitionToRoute('player', players, currentPlayer);
    } else {
        this.transitionToRoute('players', players);
    }    
}

我正在尝试做什么:当我点击进入下一个播放器页面时,如果当前没有播放器显示,则转换到PlayersRoute,否则转换到PlayerRoute,以便转换完成后仍会显示播放器。

问题:有时currentPlayer变量并不总是为null,即使当前没有显示任何播放器。有没有办法解决这个问题,也许是从某个地方获取当前路线?

2 个答案:

答案 0 :(得分:5)

鉴于你说这两个部分(基于page_id的玩家列表和基于player_id的玩家信息)是完全独立的,在我看来你似乎不会嵌套路线,相反,有两个命名的出口(称为leftright,或pageplayer等),您有选择地进行渲染。

路由器:

App.Router.map(function() {
    this.resource('page', { path: ':page_id' });
    this.resource('player', { path: ':player_id' });
 });

application.hbs:

{{outlet page}}
{{outlet player}}

然后,您可以覆盖renderTemplatepage路由的player以呈现到相应的模板中。为了澄清,page路线将显示你当前拥有的玩家路线(它取决于page_id,但页面有很多玩家,所以路线根据页面显示玩家),{{1} } route将根据player显示玩家信息。

(作为旁注,我不认为你可以按照player_id resource player放置resource players,以现在的方式嵌套资源 - 我认为只有路由可以嵌套。)

编辑:使用包含多个动态细分的单一路线

我认为您的建议可行。从linked example看起来你需要创建“父”资源(不是嵌套路由,但是有更多通用路径,比如/ page /和/ page /:page_id / player /:player_id)。然后,您可以通过相应路线中的model单独设置模型,并为双动态细分路径提供serialize挂钩:

serialize: function(model) {
    return {
        page_id : this.modelFor('page').get('id') 
        player_id : this.modelFor('player').get('id')
    };
}

请注意,我们不依赖于传入的model对象,因为您已经说过页面和播放器面板可以完全独立,因此我们使用modelFor代替。

我认为你也可以处理关于默认页面的逻辑,如果没有通过redirect钩子在这里建议渲染/默认播放器。

最后,您将覆盖PagePlayer路由中的renderTemplate以实际执行渲染:

renderTemplate: function(model, controller) {
  this.render("page", { into: "page" });
  this.render("player", { into: "player"});
}

我认为你必须小心不要在更一般的路线中渲染模板,因为如果你从/ page / 1 / player / 2移动到/ page / 1 / player / 3,页面路线不是重新进入。

答案 1 :(得分:4)

虽然Sherwin的回答让我对我的目标有了一个很好的了解,但我只想提出一个完整的例子并对我最终实现的内容有一个大概的了解。这可能有助于将来参考。

我将通过使模型成为一个简单的int来简化它,这样我们就可以直接从url转换为model,反之亦然。

模板:

<script type="text/x-handlebars">
  {{outlet a}}
  {{outlet b}}
</script>

<script type="text/x-handlebars" id="a">
  {{model}}
</script>

<script type="text/x-handlebars" id="b">
  {{model}}
</script>

应用:

App = Ember.Application.create();

App.Router.map(function() {
  // This route has 2 dynamic segments
  this.resource("ab", { path: "/:a/:b" });
});

App.IndexRoute = Ember.Route.extend({
  redirect: function() {
    // At the entry point, encapsulate the 2 models in the context object,
    // and transition to the route with dual dynamic segments
    this.transitionTo('ab', {a: 3, b:4});
  }
});

App.AbRoute = Ember.Route.extend({
  model: function(params) {
    // The model is {a, b} directly
    return params;
  },

  renderTemplate: function(){
    // Render in the named outlet using the right controller
    this.render('a', {into: 'application', outlet: 'a', controller: 'a'});
    this.render('b', {into: 'application', outlet: 'b', controller: 'b'});
  },

  serialize: function(model) {
    return {
      a: model.a,
      b: model.b
    };
  },

  setupController: function(controller, model) {
    // Setup each controller with its own model
    this.controllerFor('a').set('model', model.a);
    this.controllerFor('b').set('model', model.b);
  }
});

附加说明:

从AbController中可以使用单个'ab'模板呈现{{model.a}}{{model.b}},但我认为单独的控制器和模板更清晰,并且它启用了可重用性。此外,其中一个控制器可能是一个ArrayController,它可以完美地工作。

JS Bin of the example