Статьи

Попасть в Ember: часть 4

В моем предыдущем уроке я коснулся того, как использовать Ember.Object для определения ваших моделей и работы с наборами данных. В этом разделе мы более подробно рассмотрим, как Ember использует шаблонную среду Handlebars для определения пользовательского интерфейса вашего приложения.


Большинство разработчиков на стороне сервера используют шаблоны для определения разметки, которая будет динамически заполняться на лету. Если вы когда-либо использовали ASP.NET, ColdFusion, PHP или Rails, то вы наверняка знаете, о чем я говорю.

JavaScript Шаблонирование на стороне клиента в последнее время стало очень популярным, особенно из-за того, что он сосредоточен на создании более настольных приложений. Это означает, что большая часть обработки выполняется на стороне клиента, причем данные в основном извлекаются через запросы API на стороне сервера.

Я помню, как писал о клиентских шаблонах некоторое время назад, когда плагин jQuery Template был впервые выпущен. Спустя почти три года это все еще самая читаемая запись в моем блоге, показывающая, как возрос интерес к шаблонам на стороне клиента. С тех пор был выпущен ряд других платформ, предлагающих богатые возможности и поддерживающие сообщества. Handlebars — один из наиболее популярных вариантов и фреймворк, выбранный проектом Ember для удовлетворения своих шаблонных потребностей. Это имеет смысл, так как Handlerbars был создан соучредителем Ember.js и членом основной команды Иегудой Кацем . Заметьте, однако, что я не планирую делать сравнения между шаблонными структурами, и я сосредоточусь исключительно на Handelbars, так как это то, что Ember.js использует по умолчанию.

В предыдущих статьях я показывал некоторые базовые шаблоны в коде:

1
2
3
<script type=»text/x-handlebars»>
    <h2><strong>{{firstName}} {{lastName}}</strong></h2>
</script>

Две вещи, которые выделяются, это объявление типа для тега script и фигурные скобки, которые действуют как разделители для выражений, с которыми будут работать Handlebars. Это очень типичный синтаксис, который я скоро расскажу более подробно, и вы будете последовательно использовать его при создании шаблонов Ember.


Несмотря на то, что Handlebars использует специальный синтаксис, в конце концов, вы действительно работаете в основном со стандартной разметкой HTML. Handlebars служит для добавления содержимого в эту разметку для визуализации данных пользователю. Это делается путем анализа выражений с разделителями и замены их данными, с которыми вы просили работать Handlebars. В случае Ember Handlebars предоставляет крючки, а Ember использует их. Эти данные обычно поступают с вашего контроллера (помните, что контроллеры служат интерфейсом для ваших моделей).

Первое, что нужно любому шаблону — это определение тега скрипта. Большинство из вас, вероятно, определили теги сценария для загрузки своей библиотеки JavaScript. Фактически, вы уже сделали это, чтобы загрузить Handlebars в ваш проект Ember:

1
2
3
4
<script src=»js/libs/jquery-1.9.1.js»></script>
<script src=»js/libs/handlebars-1.0.0-rc.3.js»></script>
<script src=»js/libs/ember-1.0.0-rc.1.js»></script>
<script src=»js/app.js»></script>

Есть небольшая разница с использованием его для определения шаблона. Сначала мы указываем атрибут типа «text / x-handlebars». Этот type игнорируется браузером, но оставляет текст доступным для проверки и позволяет Ember идентифицировать шаблоны в приложении. Кроме того, Ember использует атрибут данных с именем «data-template-name», который Ember может использовать для связывания определенных частей вашего приложения с шаблоном. Например, следующее объявление определяет шаблон с именем «employee»:

1
2
3
<script type=»text/x-handlebars» data-template-name=»employee»>
</script>

Когда ваше приложение запускается, Ember сканирует DOM на предмет type="text/x-handlebars , компилирует найденные шаблоны и сохраняет их в свойстве объекта Ember, называемом Ember.TEMPLATES который он использует, чтобы выяснить, что для визуализации заданный маршрут. Вот почему так важно следовать соглашениям об именах Ember. В приведенном выше примере этот шаблон будет автоматически связан с маршрутом сотрудника и контроллером, который вы создали в своем приложении. Опять же, я не могу не подчеркнуть, как эти соглашения об именах сделает ваше развитие намного проще.

Ember использует URL-адреса для определения ресурсов, которые необходимо использовать, и шаблонов, которые необходимо отобразить. Давайте представим, что у вас была страница профиля с URL «/ profile». У вас будет ресурс, называемый profile который будет загружать определенные ресурсы для этого URL (например, объект маршрута), и у вас также будет шаблон с тем же именем. Мы рассмотрели определение ресурсов и объектов маршрута во второй части моей серии Ember, поэтому, если вы не уверены в том, что я обсуждаю, обязательно вернитесь туда, чтобы освежиться в этом.

Когда вы посещаете этот URL, Ember знает, что ему нужно загрузить эти ресурсы и проанализировать определенный вами шаблон. Он делает это через свои соглашения об именах, зная, что, поскольку вы перешли в «/ profile», ему нужно загрузить ресурсы, определенные в profile , и отобразить шаблон с именем data-template-name="profile" .

  • Маршрут: ProfileRoute
  • Контроллер: ProfileController
  • Шаблон: профиль (обратите внимание, что это строчные буквы)

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

Также важно отметить, что если вы объявляете шаблон без атрибута data-template-name , Ember будет предполагать, что это шаблон уровня приложения — тот, который обычно используется в качестве шаблона всего сайта для создания элементов пользовательского интерфейса, например в качестве заголовков, колонтитулов и навигации. И если вы явно не определяете шаблон для приложения или даже ресурса (например, для URL), Ember сделает это автоматически для вас, чтобы обеспечить стабильность и согласованность в вашем приложении.


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

1
2
3
<script type=»text/x-handlebars»>
    <h2><strong>{{firstName}} {{lastName}}</strong></h2>
</script>

В этом случае выражения {{firstName}} и {{lastName}} будут проанализированы Ember и заменены фактическими данными. Кроме того, Ember настраивает наблюдателей так, чтобы при изменении ваших данных ваш шаблон автоматически обновлялся, и обновления отражались для пользователя вашего приложения.

До сих пор я показывал вам очень простой пример, но вывод таков:

  • Ember использует специальный атрибут type для определения шаблонов.
  • Шаблоны используют стандартную разметку вместе с выражениями с разделителями, которые анализируются на стороне клиента.
  • Эти шаблоны имеют полный набор возможностей Handlebars.
  • Ember настраивает наблюдателей для динамического обновления данных пользовательского интерфейса по мере их изменения.

Это дает большую гибкость в том, как вы структурируете свой пользовательский интерфейс. Давайте продолжим смотреть на функции, которые доступны.


Помните, что Ember использует Handlebars, поэтому у вас есть доступ ко всем его выражениям здесь. Условные выражения необходимы для того, чтобы сделать почти что-нибудь полезное; Руль предлагает довольно много вариантов.

Допустим, у меня был набор данных JSON, который выглядел так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
«items»: [{
    «title»: «Tearable Cloth Simulation in JavaScript»,
    «url»: «http://codepen.io/stuffit/pen/KrAwx»,
    «id»: 5592679,
    «commentCount»: 20,
    «points»: 127,
    «postedAgo»: «1 hour ago»,
    «postedBy»: «NathanKP»
}, {
    «title»: «Netflix now bigger than HBO»,
    «url»: «http://qz.com/77067/netflix-now-bigger-than-hbo/»,
    «id»: 5592403,
    «commentCount»: 68,
    «points»: 96,
    «postedAgo»: «2 hours ago»,
    «postedBy»: «edouard1234567»
}

Если бы я хотел убедиться, что данные title доступны, я мог бы добавить условный оператор if, используя выражение #if :

1
2
3
{{#if item.title}}
    <li>{{item.title}} — {{item.postedAgo}} by {{item.postedBy}}</li>
{{/if}}

Он проверяет, не является ли item.title неопределенным, и продолжает обработку последующих выражений для выражений данных title , postedAgo и postedBy .

Поскольку этот набор данных содержит более одной «записи», можно предположить, что мы, вероятно, захотим перебрать каждый элемент item . Вот где в {{#each}} вступает выражение {{#each}} . Это позволяет вам перечислять список объектов. Итак, опять же, учитывая, что шаблоны представляют собой комбинацию выражений разметки и Handlebars, мы можем использовать выражение #each для циклического прохождения всех элементов, доступных в нашем объекте модели Ember. Помните, что модель Ember получена из контроллера, который связан с шаблоном, через соглашения об именовании Ember.

1
2
3
4
5
6
7
<ul>
    {{#each item in model}}
    {{#if item.title}}
        <li>{{item.title}} — {{item.postedAgo}} by {{item.postedBy}}</li>
    {{/if}}
    {{/each}}
</ul>

Это сделало бы нечто похожее на

1
2
3
4
5
6
<ul>
<li>Tearable Cloth Simulation in JavaScript — 1 hour ago by NathanKP</li>
<li>Netflix now bigger than HBO — 2 hours ago by edouard1234567</li>
<li>Fast Database Emerges from MIT Class, GPUs and Student&#39;s Invention — 33 minutes ago by signa11</li>
<li> Connecting an iPad retina LCD to a PC — 6 hours ago by noonespecial</li>
</ul>

Несомненным преимуществом является неявная спецификация Ember для наблюдателя, которая обновит ваши данные после обновления.

Если ваше условное выражение должно быть более сложным, вам нужно создать вычисляемое свойство . Это позволяет вам создать свойство на основе метода, который может применять сложные условия кода к вашим данным. Допустим, я просто хотел отобразить данные с заголовком «Имитация ткани в JavaScript». Есть пара вещей, которые мне нужно настроить:

  • Мне нужно вычисленное свойство, чтобы отсканировать каждый элемент и сказать, соответствует ли заголовок
  • Мне нужно создать контроллер, который может использоваться каждым перечисляемым элементом в шаблоне
  • Мне нужно обновить шаблон, чтобы он использовал этот контроллер для каждого элемента
    Первое, что мне нужно сделать, это создать новый контроллер, который обернет каждый зацикленный элемент и создаст в нем вычисляемое свойство:
1
2
3
4
5
App.TitleController = Ember.ObjectController.extend({
    titleMatch: function() {
      return this.get(&#39;title&#39;) === &quot;Tearable Cloth Simulation in JavaScript&quot;;
    }.property()
});

Глядя на код, мы создаем подкласс Ember.ObjectController для создания контроллера. Это контроллер, который будет оборачивать каждый элемент в нашем шаблоне. Затем мы создаем метод с именем titleMatch который использует метод get() чтобы titleMatch текущий заголовок, сравнить его с определенным мной текстом и вернуть логическое значение. Наконец, вызывается метод Ember property (), чтобы определить метод titleMatch как вычисляемое свойство.

Как только мы это установим, мы обновляем выражение {{#each}} шаблона, чтобы представить каждый элемент с новым контроллером, который мы создали. Это делается с помощью директивы itemController . itemController что нужно понять, это то, что itemController — это ключевая фраза в Ember, предназначенная для itemController контроллера к элементам шаблона. Не путайте это с реальным именем контроллера (как я делал изначально). Имя контроллера присваивается itemController , например:

1
2
3
4
5
6
7
<ul>
   {{#each item in model itemController=»title»}}
      {{#if titleMatch}}
        <li>{{foo.title}} — {{foo.postedAgo}} by {{foo.postedBy}}</li>
      {{/if}}
    {{/each}}
</ul>

Опять же, соглашения об именах диктуют, что при назначении имен в шаблонах мы используем строчные буквы. В этом случае мы назначаем TitleController для itemController .

Теперь, когда каждый элемент titleMatch , вычисленное свойство titleMatch используется для оценки заголовка и отображения данных, если они совпадают.


Создание динамических шаблонов — это не просто выплевывание текста. Бывают моменты, когда обрабатываемые данные должны влиять на внешний вид пользовательского интерфейса. Отображение изображения или создание ссылки являются отличными примерами этого.

Привязка данных к элементу требует использования специальных помощников Ember, которые помогают определить контекст атрибута, а также обеспечить правильное обновление атрибутов при изменении данных. Для атрибутов элемента вспомогательный {{bindAttr}} используется для заполнения значений атрибута. Если бы нам нужно было динамически указать URL-адрес изображения, мы бы использовали следующий синтаксис:

1
<img {{bindAttr src=»logoUrl»}} alt=»Logo»>

То же самое можно сделать для атрибутов, которые не получают значения, таких как disabled :

1
<input type=»checkbox» {{bindAttr disabled=»isAdministrator»}}>

В этом случае isAdminstrator может быть вычисляемым свойством, основанным на методе контроллера, или просто обычным свойством объекта, что дает вам большую гибкость в определении условий для отключения флажка. Эта гибкость распространяется и на определение имен классов. Если бы я хотел использовать условный оператор, чтобы определить, должен ли класс применяться к моему элементу, я мог бы использовать следующий код:

1
2
3
<div {{bindAttr class=»isUrgent»}}>
  Warning!
</div>

В зависимости от логического состояния моя разметка будет такой:

1
2
3
<div {{bindAttr class=»is-urgent»}}>
  Warning!
</div>

для true состояния, или:

1
2
3
<div>
  Warning!
</div>

за false состояние. Обратите внимание, что, когда я указал isUrgent для класса, Ember разбил имя и представил класс как is-urgent . Если вы предпочитаете указывать свой собственный класс на основе результатов, вы можете использовать условное выражение, похожее на троичное выражение:

1
<div {{bindAttr class=»isUrgent:urgent:normal»}}>

Это вернет urgent или normal для класса на основе условного значения isUrgent .


Шаблоны станут основой вашего пользовательского интерфейса, поэтому очень важно потратить время на чтение документации на сайтах Ember и Handlebars, чтобы лучше понять их общую мощь. Даже если вы не используете Ember, Handlebars — это отличная среда для повседневного использования, которая стоит вложений в изучение того, как ее использовать.

Габриэль Манрикс написал отличное руководство по рулю здесь, на Nettuts +, которое вы можете использовать, чтобы быстро освоить фреймворк.