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

Это продолжение маршрута "Понимание Эмбер".

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

Так /users/1/posts должен отобразить список сообщений для пользователя 1. И /users/1/posts/1 должен отображать пост 1 для пользователя 1, но он не должен отображаться внутри user шаблона {{outlet}}, Вместо этого он должен полностью заменить 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 определение ресурса, но если я возьму его, Эмбер не найдет users маршрут. Я хотел бы избавиться от этого последнего остатка.

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, который демонстрирует это.

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

Другие вопросы по тегам