Идиоматические Emberjs для вложенных маршрутов, но не вложенных шаблонов

Это продолжение статьи Понимание маршрутов Ember.

Основные/детальные представления великолепны, но я пытаюсь создать иерархический URL-маршрут без вложения их шаблонов. Однако мне по-прежнему нужен доступ к родительской модели для таких вещей, как навигационные ссылки и другие ссылки.

Таким образом, /users/1/posts должен отображать список сообщений для пользователя 1. А /users/1/posts/1 должен отображать сообщение 1 для пользователя 1, но оно не должно отображаться внутри {{outlet}} шаблона user. Вместо этого он должен полностью заменить шаблон user. Однако мне по-прежнему нужен доступ к текущему пользователю в шаблоне post, чтобы я мог вернуться к пользователю, показать имя пользователя и т. д.

Сначала я попробовал что-то вроде этого (метод № 1):

App.Router.map(function() {
  this.resource('user', { path: '/users/:user_id' }, function() {
    this.resource('posts',  function() {
      this.resource('post', { path: '/:post_id' });
    });
  });
});

...

App.PostRoute = Ember.Route.extend({
  model: function(params) {
    return App.Post.find(params.post_id);
  },
  renderTemplate: function() {
    this.render('post', {
      into: 'application'
    });
  }
});

Это заменило шаблон user на post, как и ожидалось. Но когда я нажимаю кнопку «Назад» в браузере, шаблон user больше не отображается. По-видимому, представление сообщения уничтожается, но родительское представление не вставляется повторно. Здесь есть несколько вопросов, в которых упоминается это.

Затем я заставил его работать с чем-то вроде этого (метод № 2):

App.Router.map(function() {
  this.resource('user', { path: '/users/:user_id' },  function() {
    this.resource('posts');
  this.resource('post', { path: '/users/:user_id/posts' },  function() {
    this.resource('post.index', { path: '/:post_id' });
  });
});

...

App.PostRoute = Ember.Route.extend({
  model: function(params) {
    return App.User.find(params.user_id);
  },
  setupController: function(controller, model) {
    controller.set('user', model);
  }
});

App.VideoIndexRoute = Ember.Route.extend({
  model: function(params) {
    return App.Post.find(params.post_id);
  }
});

App.PostIndexController = Ember.ObjectController.extend({
  needs: 'post'
});

Но мне это кажется немного хакерским и не очень СУХИМ.

Во-первых, мне нужно снова получить User в PostRoute и добавить его как специальную переменную в PostController (в этом не было бы необходимости, если бы маршруты были правильно вложены, и я мог бы просто установить свойство needs: 'user' в PostController). Кроме того, это может повлиять или не повлиять на серверную часть в зависимости от реализации адаптера ember-data или какой-либо технологии, используемой для извлечения данных с сервера (т. е. это может привести к ненужному второму вызову для загрузки User) .

Мне также нужно дополнительное объявление `PostIndexController' только для того, чтобы добавить эту новую зависимость, что не имеет большого значения.

Еще одна вещь, которая кажется неправильной, это то, что /users/:user_id/posts появляется дважды в конфигурации маршрутизатора (один вложенный, один нет).

Я могу справиться с этими проблемами, и это работает, но я думаю, что в целом это кажется вынужденным и не таким изящным. Мне интересно, не хватает ли мне какой-то очевидной конфигурации, которая позволит мне делать это с обычными вложенными маршрутами, или если у кого-то есть рекомендация для более "способа Ember.js" сделать это.

Я должен упомянуть, что, несмотря на технические достоинства метода № 2, мне потребовалось довольно много времени, чтобы понять, как заставить его работать. Потребовалось много поисков, чтения, экспериментов, отладки и т. д., чтобы найти правильную комбинацию определений маршрутов. Я полагаю, что это не очень уникальный вариант использования, и пользователю должно быть очень просто настроить что-то подобное, не тратя часы на пробы и ошибки. Я буду рад написать несколько советов по этому поводу в документации Ember.js, если это окажется правильным подходом.

Обновлять:

Спасибо @spullen за разъяснение этого. Мой случай был не таким простым, как в примере, потому что для некоторых подмаршрутов нужны вложенные шаблоны, а для некоторых нет, но ответ помог мне разобраться. Моя окончательная реализация выглядит примерно так:

App.Router.map(function() {
  this.resource('users', { path: '/users/:user_id' }, function() {
    this.resource('users.index', { path: '' }, function() {
      this.resource('posts')
    });
    this.resource('post', { path: '/posts/:post_id' }, function() {
      this.resource('comments', function() {
        this.resource('comment', { path: '/:comment_id' });
      });
    });
  });
});

Итак, теперь posts отображается под шаблоном users, но post заменяет все. Затем comments отображается под post, а comment, в свою очередь, под comments.

Все они являются подмаршрутами users, поэтому пользовательская модель доступна для всех из них без акробатики, выполняя this.modelFor('users') в каждом маршруте, где это необходимо.

Итак, шаблоны выглядят так:

users
|- posts
post
|- comments
   |-comment

Я не знаю, зачем { path: '' } нужен для определения ресурса users.index, но если я уберу его, Ember не найдет маршрут users. Я хотел бы избавиться от этого последнего пережитка.


person Ike    schedule 09.06.2013    source источник


Ответы (1)


Вы можете определить родительский шаблон, чтобы просто отображать выход и иметь индексный маршрут, который будет отображаться внутри него. Затем для вложенного ресурса вы можете сделать то же самое.

<script type="text/x-handlebars" data-template-name="user">
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="user/index">
  <h2>user/index</h2>
</script>

<script type="text/x-handlebars" data-template-name="posts">
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="posts/index">
  <h2>posts/index</h2>
</script>

Таким образом, это не будет мастер/деталь.

Маршрутизатор будет:

App.Router.map(function() {
  this.resource('user', function() {
    this.resource('posts', function() { });
  });
});

Затем, если вам нужно получить информацию о родителе, вы можете использовать modelFor. Итак, если бы вы были в сообщениях, вы могли бы сделать this.modelFor('user');

Вот jsbin, демонстрирующий это.

Надеюсь, это полезно.

person spullen    schedule 21.06.2013
comment
Действительно, это работает и кажется нативным. Я все еще чувствую, что не так просто разобраться в использовании пустых (только {{outlet}}) шаблонов, а также в правильном именовании и вложении определений ресурсов/маршрутов в маршрутизаторе. Спасибо за разъяснения. В моем конкретном приложении мне действительно нужно, чтобы это было частично вложено. Я отредактирую исходный пост, чтобы добавить свою окончательную версию. - person Ike; 25.06.2013
comment
Как вы видите выше, в конце моего обновления мне все еще нужен, казалось бы, ненужный { path: '' } для маршрута .index, иначе ничего не работает (Ember жалуется, что маршрут пользователей не может быть найден). Немного странно, но в целом намного лучше, чем раньше. Спасибо еще раз! - person Ike; 26.06.2013
comment
Ваша реализация немного неверна. Я сделал новый пример, чтобы лучше продемонстрировать, что вы пытаетесь сделать. jsbin.com/uzahuy/2/edit - person spullen; 27.06.2013
comment
Кроме того, посмотрите на вывод консоли, чтобы увидеть, какие маршруты отображаются на экране. - person spullen; 27.06.2013
comment
Ну, не совсем так. В вашем примере все шаблоны заменяют друг друга. В моем случае мне нужно, чтобы первые два были вложены друг в друга, а затем третий должен заменить предыдущие 2. Затем вложение должно произойти снова. Как на маленькой диаграмме иерархии ASCII. Вот почему мне нужна эта причудливая организация маршрутов. Хотя, возможно, есть лучший способ сделать это. - person Ike; 27.06.2013
comment
Почти. Одна вещь, которая немного сбивает с толку, это то, что у вас есть ‹h2›users/index‹/h2› в двух шаблонах. Но если вы переименуете второй в user/index (пользователь в единственном числе), то вы заметите, что первые три уровня заменяют друг друга. См. здесь: jsbin.com/unoduy/1/edit. Встраивается только последний шаблон. Я пытался сделать это в два этапа: первые два вложены, вторые два вложены, но заменяют первые два. Я предполагаю, что это угловой случай, но он работает с настройкой, которую я описал выше. Спасибо за ваше время и примеры. Они очень помогли. - person Ike; 27.06.2013