Ember.js сложный в обучении. Хотя даже не так. Концепты Ember.js сложны в освоении и понимании. Мне кажется, что любой курс обучения Ember.js должен начинаться с этих слов.

Я разработчик, работающий с Ruby on Rails (до этого я программировал в .NET и Python). Для меня было довольно проблематично понять магию, заставляющую Ember.js работать. Иногда я общаюсь с другими начинающими разработчиками, вставшими (или пытающимися встать) на путь Ember.js — все их проблемы начинаются из-за того, что они не понимают, как устроен данный фреймворк.

Да, с одной стороны есть официальная документация в которой детально описаны все аспекты данного фреймворка. Но ей не хватает концепции; для начинающего разработчика это просто осколки информации разбросанные случайным способом. Из документации, например, можно узнать что у нас есть в арсенале контроллеры, модели и виды (controller, model, view). Но для того что-бы узнать за что они отвечают и как работают начинающему разработчику предлагают сначала наступить на грабли пару десятков раз. Плюс в нагрузку к контроллерам, моделям и видам Ember.js нам дает целый взвод разношерстых объектов типа компонентов, шаблонов, маршрутизатора и путей (component, template, router, route).

Но обо всем по порядку. Давайте же начнем наше обучение Ember.js с того, что запомним, что это не MVC (model-view-controller) фреймворк; забудьте про это. Да, в Ember.js есть контроллеры, модели и виды–шаблоны, но на этом его схожесть с MVC фреймворками заканчивается. Ember.js это фреймворк для написания приложений работающих в браузере пользователя.

Взгляните на следующую схему, на ней детально описан жизненный цикл запроса в стандартном приложении написанном на Ember.js:



Состояния вашего приложения описываются с помощью адреса который вы задаете в адресную строку вашего браузера. С помощью адреса вы можете сохранять состояние вашего приложения или даже делится состоянием с другими пользователями например послав коллеге ссылку на конкретную задачу в вашем баг-трекере (приложение показывает задачу — это его состояние).

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

НЛО (небольшое лирическое отступление): Ember.js работает по двум принципам. Правила превыше конфигураций (convention over configuration) и если Ember.js не находит ожидаемый объект, то он его создает сам на основе своих внутренних правил. Тут стоит отметить способ работы генератора кода Ember.js. Наверное вы знакомы с генераторами в других фреймворках: они создают файл с кодом и сохраняют его на вашем диске. В отличии от других фреймворков, генератор кода в Ember.js создает объект только тогда когда он ему нужен и держит его в памяти. После того как объект становится не нужным, Ember.js его уничтожает — этот принцип освобождает вас от ненужной поддержки шаблонного кода.

Продолжим. Допустим пользователь запросил путь /posts. Маршрутизатор на основе этого запроса будет пытаться найти следующий объект в цепочке по его названию — путь. Как вы наверное уже догадались, Ember.js будет искать объект типа Route с названием PostsRoute. Путь в Ember.js является ответственным за предоставление модели контроллеру.

Ember.js будет искать модель с названием PostModel. Модель описывает структуру данных сохраненных в вашем хранилище и бизнес логику объектов. Вы можете представить себе модель как класс из которого Ember.js создает объекты содержащие данные. Хранилище данных это всего лишь абстрактный технический слой, который находится между моделью и вашим сервером. В хранилище хранятся данные после того как Ember.js их забирает с сервера.

После того, как путь забирает данные, он отдает их контроллеру. Контроллер в данном случае будет называться PostsController. Стоит заметить, что между контроллером и моделью нет прямой связи; они не знают друг о друге и контроллеру в принципе без разницы какую модель вы ему отдадите. Контроллер всего-лишь декоратор данных — он их обрабатывает перед тем как отдать пользователю.

Затем Ember.js обработанные данные забирает у контроллера и отдает их шаблону. Но есть еще один объект, который находится между контроллером и шаблоном — вид. Именно вид, который в нашем случае будет называться PostsView, говорит Ember.js какой шаблон надо использовать для данного запроса. По правилам Ember.js будет использовать шаблон с названием posts.

Обратная связь


Пользователи могут взаимодействовать с нашим приложением (тыкать по кнопкам, перетаскивать элементы и пакостить разработчику другими доступными пользователю способами). Ember.js различает два вида взаимодействия пользователя с приложением. Первый это т.н. нативные события браузера — click, drag, drop, mouseEnter, mouserLeave и так далее. Второй способ это события которые имеют имя. Второй тип события в основном запускается по щелчку на элемент на котором определенно данное событие.

Так в чем-же разница, спросите вы? Ну или хотели спросить. Первый тип событий обрабатывается исключительно видом; Ember.js без разницы на какую кнопку нажмет пользователь, главный сам факт того что нажатие произошло. Второй тип срабатывает только после нажатия на конкретный элемент и обрабатывается контроллером. Если контроллер не может обработать событие, Ember.js пытается обработать событие в пути (Route Object) и только потом сообщает об ошибке.

Структура шаблонов


Все начинает казаться еще более запутанным после того как новички узнают что в шаблоны Ember.js можно вкладывать другие объекты тем самым вызывая целый цикл заново. На самом деле в этом нет ничего страшного.

На данный момент вы уже знаете, что у шаблона есть контекст — модель, контроллер и вид.

Начнем с частичных шаблонов (partial). Это самый простой объект который вы можете вкладывать в ваши шаблоны (хотя объект наверно слишком громкое имя для частичных шаблонов). Ember.js просто возьмет указанный вами шаблон и представит его в родительском шаблоне; при этом будет использован контекст родителя — т.е. в частичном шаблоне у вас будет доступ к модели, контроллеру и виду родителя. Проще говоря все события будут обрабатываться видом и контроллером родителя.

Вид и компонент довольно похожи друг на друга (по правде сказать вид это родительский класс компонента). Вид и компонент оба имеют свой собственный шаблон. За видом и компонентом находится свой собственный объект. Разница заключается в двух ключевых моментах. Во первых обработка событий — компонент сам обрабатывает именные события а вид просто напросто отдает их родительскому шаблону который в свою очередь отдает их контроллеру. Во вторых окружение — компонент полностью изолирован и может работать только с объектами которые вы ему отдадите; вид в свою очередь имеет доступ ко всем объектам доступным родительскому шаблону. С точки зрения контекста родительского шаблона вид имеет доступ к модели и контроллеру но имеет свой собственный вид. Компонент более изолированный и не имеет доступ ни к чему.

Последний элемент это рендер (render). Он самый сложный из всех объектов доступных в шаблонах. Когда вы вызываете в шаблоне рендер, то вам надо предоставить ему два параметра — название шаблона и модель. Ember.js найдет шаблон и на основании названия шаблона найдет контроллер и вид. Затем он отдаст контроллеру модель и после того как контроллер обработает модель, Ember.js вставит ее в шаблон. Проще говоря с помощью рендера вы можете на одной странице собрать несколько независимых друг от друга шаблонов с абсолютно разным контекстом; рендер не имеет доступ к контексту родительского шаблона (вместо этого рендер имеет свой собственный контекст) и все события будут обрабатываться с помощью вида и контроллера рендера.

Надеюсь что вам понравилось теоретическое знакомство с Ember.js. Если Ember.js вас заинтересовал то вы можете продолжить знакомится с ним прочитав официальный гайд. Также вы можете продолжить знакомство с помощью моей книги, расширенным переводом одной из глав которой является данная статья.