Пользователь
0,0
рейтинг
13 февраля 2014 в 12:26

Разработка → Анатомия Ember.js (часть первая, теоретическая) из песочницы

image 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:

image

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

Итак представьте себе что приложение написанное на 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 вас заинтересовал то вы можете продолжить знакомится с ним прочитав официальный гайд. Также вы можете продолжить знакомство с помощью моей книги, расширенным переводом одной из глав которой является данная статья.
Руслан Яхъяев @iiShrimp
карма
11,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (20)

  • 0
    Вот вопрос, который меня мучал изначально, в гайде не смог найти толковый ответ. Все примеры, которые есть по ember'у, очень прямолинейны и далеки от более реальных приложений, как должно выглядеть следующая структура:

    /dashboard

    этот route содержит в себе несколько компонентов страницы, есть левое меню, которое приходит с сервера, есть данные статистики, есть dashboard статистика, и есть один график. Все это приходит от сервера в одном запросе, разделенными приблизительно так:

    {
    dashboard: {
    data: [{… data1… }, {… data2… }, {… data3… }]
    menu: [{… item1… }, {… item2… }, {… item3… }],
    indicators: [{… indicator1… }, {… indicator2… }, {… indicator3… }],
    statistics: [{… statistics1… }, {… statistics2… }, {… statistics3… }],
    }
    }

    data, menu, indicators, statistics (collection'ы) — разные блоки, темплейты. data1, data2, data3 etc. — это все моделию

    и вопрос такой, как в идеологии ember'а, такое реализовать?

    Спасибо!
    • 0
      Ember.js все равно какой ответ придет от сервера — главное что-бы он содержал всю нужную ему информацию именно в том формате к отором ее будет ожидать приложение.

      В данном случае вам надо начать копать со стороны клиента и приспособить ответ сервера клиенту. Не зная проблематики вопроса в деталях я бы вам посоветовал создать класс dashboard в котором вам надо будет назначить связи с другими объектами:

      App.Dashboard = DS.Store.extend({
        data: DS.hasMany('data'),
        menu: DS.hasMany('menu'),
        indicators: DS.hasMany('indicators'),
        statistics: DS.hasMany('statistics)
      });
      


      Можно реализовать двумя способами. Вызывайте в шаблоне dashboard хелпер render и как модель отдайте ему связь. Или можете создать компонент и отдать ему нужный объект.

      Ну и на последок, ответ от сервера долже выглядеть примерно вот так:

      {
        data: {
          data1: {...},
          data2: {...}
        },
      
        dashboard: {
          ....
          data: [data.id1, data2.id]
        }
      }
      
      • +1
        Я решал эту проблему так. В роутере:
        model: function() {
            return Ember.RSVP.hash({
                // use Ember.Data or whatever you whant
               data1: this.store.find('data', /* find query */),
               data2: Ember.$.getJSON(url).then(function(data) {
                     return data.summary;
               })
            });
        }
        


        Потом можно в setupController растащить этот объект на модели:
             setupController: function(controller, models) {
          var dashboard = models.data1,
              summary = models.data2;
        
          controller.set('dashboard', dashboard);
          controller.set('content', summary);
        },
        
        • 0
          Крутое решение с Ember.RSVP.hash, спасибо!

          А второй кусок можно сделать чуть изящнее, если назвать свойства dashboard и content:
          setupController: function(controller, models) {
            controller.setProperties(models);
          }
          
          • 0
            я вначале написал кусок с router. А потом решил дополнить пример, но 3 минут не хватило, поэтому видите, что видите.
  • +1
    Спасибо за статью!
    Действительно, на первый взгляд все понятно и логично, но как дела заходят дальше документации — приходится ломать голову и перерывать интернет.
    Может подскажете достойные ресурсы для понимания Ember, Ember-data?
    • –1
      К сожалению, все что есть по Ember-data на данный момент это куски информации разбросанные по офицальным докам.

      Из достойных могу вам посоветовать курс Codeschool или мою книгу. Остальные материалы по Ember'у, с которыми я сталкивался, устройство фреймворка и его особенности опичывают только поверхностно.
      • –1
        Codeschool платный. Обидно, когда начинаешь заниматься и тут плати. Надо об этом сразу предупреждать.
        • –1
          Ну вы словно не из снг, ей богу
    • 0
      Есть еще 'Ember.js in action' на manning.com, но насколько она хороша пока не могу сказать. В codeschool лично я разочаровался. Все-таки вставка кусков кода в нужные места не слишком помогает пониманию кухни. Если они ничего не поменяли, конечно.
  • 0
    Как же всё сложно, наверно я слишком туп для ember.
    Точно не буду юзать, 4ый год пишу на чистом js, ни о чём не жалею.
  • 0
    Хотите сказать, что если я создам PostModel, мне в PostsRoute model не придется переопределять?
    • 0
      Да. Если вы будете придерживаться дефолтных настроек Ember'а то он вам сам в PostsRoute вызовет все доступные модели Post.
  • 0
    Разве модель не должна называться просто Post (без приставки Model)?

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

    Далее Вы зачем-то поставили в один ряд partial, render и компоненты. partial и render всего лишь хелперы в шаблонах, которые указывают, как именно нужно вставить шаблон/вид в текущий шаблон. Вы также не упомянули другой важный хелпер — view, который используется намного чаще, чем partial.
    Компоненты — это специальные виды, которые сами себе являются контроллером.

    И позвольте не согласиться с первым тезисом Вашей статьи. Ember.js просто другой. А сложность — это понятие сугубо субъективное, как мне кажется.
    • 0
      Присоединюсь к замечаниям. Смущает что на схеме (как я поял) controller имеет один view. Это не так, контроллер понятия не имеет о view, он используется как контекст для view (и для template), таким образом мы можем использовать один экземпляр controller для многих view.

      Тем не менее спасибо за статью.
    • 0
      Похоже, что вы работали с Yii Framework, и сразу назвали все своими именами. По сто раз перечитывал статью автора, пока не наткнулся на ваш комментарий. Спасибо за хелперы и компоненты!
  • 0
    Купил я книгу, разбираюсь, могу сказать, что книга устарела немного, если следовать 1 в 1 тому, как там написано, встречаются не совсем понятные ошибки, которые не так просто понять и исправить, и почти все касаются ember-data. Я бы посоветовал исправить начальные главы книги, чтобы так сказать, не разочаровывать читателей ). В целом книга неплоха.
  • 0
    А у нас кстати вакансия есть на ember в Москве, интересно кому?
    • 0
      а у нас в питере есть вакансия =)
    • 0
      Чем обусловлена приверженность этому фреймворку в вашей компании? Интересно, как исторически сложилось?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.