Skip to content

Instantly share code, notes, and snippets.

@RusAlex
Created June 30, 2012 17:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RusAlex/3024749 to your computer and use it in GitHub Desktop.
Save RusAlex/3024749 to your computer and use it in GitHub Desktop.
Guide.Application.Structure (перевод)
пер. http://emberjs.com/guides/outlets/
Ember Application Structure
На верхнем уровне, вы строите Ember приложение, посредством составления
вложенных маршрутов, для соответсвующих состояний приложения.
Routing Маршрут
Пользователь перемещается по вашему приложению, делая выбор: что смотреть.
Например, у вас блог, ваш пользователь может выбирать между вашими постами и
страницей "Обо мне". В общем вы хотите сделать "по-умолчанию" случай со статьями.
Когда пользователь сделал свой первый выбор, он обычно не заканчивает
просматривать сайт. В контексте Поста, пользователь будет непосредственно его
просматривать и читать комментарии к нему. Внутри каждого отдельного поста, он
может выбрать просматривать комментарии или трэкбэки.
Важно, во всех этих случаях, пользователь принимает решение, что показывать на
странице. Чем глубже он передвигается в ваше приложение, его выбор будет влиять
все на меньшие области страницы.
В следующей части мы рассмотрим, как вы управляете этими областями страницы.
Сейчас давайте рассмотрим как устроены ваши шаблоны.
Когда пользователь впервые открывает приложение, приложение на экране имеет
пустой outlet, который будет контролироваться роутером. В ember outlet это
область шаблона, которая имеет свой собственный шаблон, определяемый во время
загрузки.
[picture]
Шаблон для приложения будет выглядеть примерно так:
<h1>My Application</h1>
{{outlet}}
По-умолчанию, роутер будет изначально направлять на состояние "список постов".
Как это работает, мы рассмотрим позже.
[picture]
Как ожидалось, шаблон лист постов будет отрисовывать список статей. клик мыши
по отдельному посту заменит содержимое outlet шаблоном для этой статьи. Шаблон
будет выглядеть примерно так:
{{#each post in controller}}
<h1><a {{action showPost context="post" href=true}}>{{post.title}}</a></h1>
<div>{{post.intro}}</div>
{{/post}}
По нажатию ссылки отдельного поста, приложение изменит своё состояние на
" состояни отдельный пост" и заменит posts.handlebars на post.handlebars .
[pic]
В этом случае отдельный пост также имеет outlet. И он же дает пользователю
выбор между просмотром комментариев и трекбэков.
Шаблон для отдельного поста выглядит так:
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
{{outlet}}
Опять outlet просто уточняет, что роутер будет принимать решение, что размещать
в шаблоне. Потому что outlet - это особенность всех шаблонов, в то время как вы
идете глубже по лестинце маршрутов, каждый маршрут будет управлять меньшими
частями страницы.
Как это работает
Сейчас вы ознакомились с теоретической частью. Давайте посмотрим, как роутер
управляет outlet.
Шаблоны, контроллеры, вьюшки.
Первое, для каждого высокоуровнего шаблона, будет вьюшка, контроллер с таким же
именем. Например:
* application.handlebars - шаблон для главного приложения.
* App.ApplicationController - контроллер для шаблона. Начальный изменяемый
контекст application.handlebars, это экземпляр этого контроллера.
* App.ApplicationView - объект вьюшка для шаблона.
В ообщем вы будете использовать объекты-вьюшки для управления событиями и
объекты-контроллеры для предоставления данных вашим вьюшкам.
Ember предоставляет два вида контроллеров, ObjectController и ArrayController.
Эти контроллеры служат как прокси для объектов модели и списка объектов модели.
We start with controllers rather than exposing the model objects directly to
your templates so that you have someplace to put view-related computed
properties and don't end up polluting your models with view concerns.
Роутер
Роутер вашего приложения отвечает за передвижение приложения через его состояния,
в ответ на действия пользователя. Вот простой роутер:
App.Router = Ember.Router.extend({
root: Ember.State.extend({
index: Ember.State.extend({
route: '/',
redirectsTo: 'posts'
}),
posts: Ember.State.extend({
route: '/posts'
}),
post: Ember.State.extend({
route: '/posts/:post_id'
})
})
})
Этот роутер определяет три состояния: index состояние, состояние, которое
показывает список постови и состояние для отдельного поста.
В нашем случае мы просто делаем редирект из index в posts состояние. В других
приложениях вы возможно будете захотите иметь отдельную главную страницу.
Пока у нас есть только список состояний и наше приложение покорно переходит в
состояние "posts", но больше ничего не делает. Когда приложение входит в
состояние "posts" , мы хотим соединить outlet к шаблону. Мы достигаем это
посредством "connectOutlets" callback.
App.Router = Ember.Router.extend({
root: Ember.State.extend({
index: Ember.State.extend({
route: '/',
redirectsTo: 'posts'
}),
posts: Ember.State.extend({
route: '/posts',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet(App.PostsView, App.Post.find());
}
}),
post: Ember.State.extend({
route: '/posts/:post_id'
})
})
})
Метод connectOutlets делает несколько вещей для нас.
* создает новый экземпляр App.PostsView, исподьзуюя posts.handlebars шаблон.
* устанавливает свойство "content" для postsController в список всех доступных
постов (App.Post.find()) и делает postsController контроллером для нового
App.PostsView.
* соединяет новую вбюшку к outlet в application.handlebars.
В общем, вы должны думать об этих объектах как действуюищий в тандеме. Вы
всегда будете предоставлять содержимое для вьбюшки, когда будете её создавать.
Перемещения и урлы.
Далее для приложения в состоянии posts, мы хотим предоставить путь перехода в
состояние отдельного поста post.
Мы добьемся этого, уточнив перемещение
posts: Ember.State.extend({
route: '/posts',
showPost: Ember.State.transitionTo('post'),
connectOutlets: function(router) {
router.get('applicationController').connectOutlet(App.PostsView, App.Post.find());
}
})
Вы вызываете это перемещение, используя {{action}} хелпер в текущем шаблоне.
{{#each post in controller}}
<h1><a {{action showPost context="post" href=true}}>{{post.title}}</a></h1>
{{/each}}
Когда пользователь кликает по ссылке с этим хелпером, Ember отсылает событие к
текущему состоянию с соответствующим именем. В этом случае событие - это
перемещение.
Т.к. мы используем transition - перемещение, Ember также может сгенерировать
URL для этой ссылки. Ember использует свойство id, для заполнение :post_id.
Далее мы должны сделать "connectOutlets" в post состоянии. В этот раз
"connectOutlets" метод получит объект post как контекст к хелперу {{action}}.
post: Ember.State.extend({
route: '/posts/:post_id',
connectOutlets: function(router, post) {
router.get('applicationController').connectOutlet(App.PostView, post);
}
})
Резюмируя, вызов "connectOutlets" выполняет следующие шаги:
* создает новый экземпляр App.PostView, используя "post.handlebars" шаблон.
* устаналивает свойство "content" для postController, на post. Который выбрал
пользователь.
* соединяет новый view к outlet в "application.handlebars".
Вам ничего больше не надо делать. Если пользователь возьмет и добавит в
закладки (/post/1) всё будет работать.
Если пользователь введет /posts/1 в адресной строке браузера, то роутер сделает
следующее:
* найдет нужное состояние для этого URL
* вычленит динамический сегмент :post_id из URL и вызовет App.Post.find(post_id).
Это работает используя соглашение, что :post_id это динамический сегмент для
нахождения App.Post
* вызовет "connectOutlets" со значением возвращенным от App.Post.find
Это значит что независимо от того пришел ли пользователь из другой страницы
приложения или через URL роутер вызовет метод connectOutlets с одинаковым
объектом.
Вложенности
Наконец, давайте сделаем комментарии и трэкюэки.
Т.к. post состояние использует тот же шаблон проектирования что и root состояние,
они будут похожи.
post: Ember.State.extend({
route: '/posts/:post_id',
connectOutlets: function(router, post) {
router.get('applicationController').connectOutlet(App.PostView, post);
},
index: Ember.State.extend({
route: '/',
redirectsTo: 'comments'
}),
comments: Ember.State.extend({
route: '/comments',
showTrackbacks: Ember.State.transitionTo('trackbacks'),
connectOutlets: function(router) {
var postController = router.get('postController');
postController.connectOutlet(App.CommentsView, postController.get('comments'));
}
}),
trackbacks: Ember.State.extend({
route: '/trackbacks',
showComments: Ember.State.transitionTo('comments'),
connectOutlets: function(router) {
var postController = router.get('postController');
postController.connectOutlet(App.TrackbacksView, postController.get('trackbacks'));
}
})
})
Здесь только несколько отличий:
* мы уточняем showTrackbacks и showComments перемещения только в состояниях где
они нужны.
* т.к. мы устанваливаем vie для outlet в post.handlebars, мы вызываем
connectOutlet для postController
* в этом случае мы берем content для commentsController и trackbackController
из текущего post. postController это прокси для основного Post так мы можем
получать ассоциативные сущности непосредственно из postController
Ниже когд шаблона для отдельного поста:
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
<p>
<a {{action showComments href=true}}>Comments</a> |
<a {{action showTrackbacks href=true}}>Trackbacks</a>
</p>
{{outlet}}
И наконец, пользователь, возвращаясь к нашему приложению из закладок на
/posts/1/trackbacks, все также попадет на правильную страницу. Давайте
посмотрим что же произойдет в этом случае:
* роутер определит что состояние соответсвющее этому URL (post.trackbacks) и
приложение входит в это состояние.
* для каждого состояния в пути, роутер достает все динамические сегменты и
вызывает connectOutlets. Это полностью отражает, как если бы пользователь
совершил путь до этой страницы двигаясь по ссылкам приложения. По-прежнему
роутер выполнит connectOutlet метод на отдельный пост с App.Post.find(1).
* когда роутер достигает trackback состояния, он вызовет connectOutlets. Потому
что connectOutlets метод для страницы поста устанавливает content postController.
Асинхронность
Наконец, вы можете спросить себя, как эта система может работать, если
приложение еще не загрузило сущность Post в момент вызова App.Post.find(1).
Это работает, потому что ember-data всегда возвращает объект немедленно, даже
если надо инициировать запрос на сервер. Этот олбъект начинается с пустого хэша
с данными data-hash. Когда сервер вовзвращает данные, ember-data обновляет
данные объекта, что также вызывает биндинги на всех атрбутах (свойствах
определенных через DS.attr)
Когда вы запрашиваете оьбъект для его trackbacks, также вернется пустой массив
ManyArray и только когда сервер вернет данные, ember-data автоматически обновит
trackbacks массив.
В вашем trackbacks.handlebars шаблоне будет примерно следующее:
<ul>
{{#each trackback in controller}}
<li><a {{bindAttr href="trackback.url"}}>{{trackback.title}}</a></li>
{{/each}}
</ul>
Когда ember-data обновит trackbacks массив, изменрения распространятся через
trackbacksController и на DOM.
Что если вы также ходите избежать отображения еще не загруженных данных, в этом
случае можно сделать так:
<ul>
{{#if controller.isLoaded}}
{{#each trackback in controller}}
<li><a {{bindAttr href="trackback.url"}}>{{trackback.title}}</a></li>
{{/each}}
{{else}}
<li><img src="/spinner.gif"> Loading trackbacks...</li>
{{/if}}
</ul>
Когда ember-data заполняет ManyArray для trakbacks, полученными данными с
сервера, он также устанавливает свойство isLoaded. Таким образом вы увидите все
trckbacks только когда они будут полностью загружены и готовы к отображению.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment