Статьи

Одностраничное приложение с Laravel и EmberJS

В этой части мы увидим, как работает Ember, как использовать Ember Data и как с ним создать что-то простое. Маршрутизатор, Маршрут, Модель, Шаблон и Магазин — вот некоторые из концепций Ember. Я не собираюсь объяснять каждый из них, поэтому, если вы чувствуете себя застрявшим, используйте документацию . Как обычно, вы можете скачать код этой части здесь .

Давайте код

Обратите внимание, что при разработке с помощью Ember рекомендуется загрузить Ember Inspector. Они выпустили Ember с расширением Chrome, и теперь это расширение также на Firefox.

В этом примере мы собираемся поместить каждую строку JS в /public/static/app.js . В реальном проекте это не очень хорошая идея. Это упрощает наш пример, но спросите себя: вы когда-нибудь делали серьезную работу с архитектурой MVC всего в одном большом файле? Мы увидели, как работает Laravel: контроллеры находятся в одной папке, каждый из них в одном файле, конфигурация в отдельной папке, модели тоже. Я предлагаю вам сделать то же самое с Эмбер, когда вы погрузитесь в правильный проект.

Первое, что вы делаете при запуске Ember, — это создаете приложение. Это глобальное пространство имен для всего, что вы кодируете с помощью Ember. Приложение может быть создано следующим образом:

 App = Ember.Application.create(); 

Я предлагаю активировать немного отладки, просто добавив строку кода при создании приложения.

 App = Ember.Application.create({ LOG_TRANSITIONS: true }); 

Это не намного больше, чем вывод вашего движения через URL и шаблоны в консоли. Кроме того, мы собираемся использовать Ember Data, который является отдельным модулем Ember и обеспечивает приятную интеграцию с REST, переводя все из Store Object в запрос на сервере. По умолчанию Ember Data использует адаптер покоя. Вы также можете использовать Fixture Adapter для тестирования и локальной разработки. По сути, Ember Data — это мост между серверами (Rest API) и локальным хранилищем с объектом Store.

Как мы видели ранее, наш API использует пространство имен. Данные Ember поставляются с адаптером отдыха, который принимает пространство имен, префикс, который мы видели в группах маршрутов Laravel. Давайте передадим в наше пространство имен в качестве аргумента.

 App.ApplicationAdapter = DS.RESTAdapter.extend({ namespace: 'api/v1' }); 

Адаптер теперь запрашивает все данные через example.com/api/v1/ .

Свяжите App Store с адаптером, и вы готовы начать разработку.

 App.Store = DS.Store.extend({ adapter: 'App.ApplicationAdapter' }); 

Одним из основных понятий Ember является URL. Все построено вокруг этой идеи. Маршрутизатор поддерживает синхронизацию URL-адресов и шаблонов. Внутри маршрутизатора вы можете определить ресурс и сопоставить его с определенным URL. В этом примере мы будем работать только с фоторесурсом и пользовательским ресурсом. Не стесняйтесь добавлять ресурс категории и устанавливать отношения с Ember. Не забывайте, что ранее мы создали несколько отношений (один ко многим и принадлежат) с Laravel, но мы их не использовали. Использовать отношения «один ко многим» в Laravel достаточно просто, но я не хочу вас перегружать. Если интерес к сгенерированным комментариям будет достаточным, мы добавим это в наше приложение в следующем посте вместе с нумерацией страниц.

Маршрутизатор — это место, где должны быть определены все маршруты. Здесь мы определили два ресурса с их URL. URL не является обязательным здесь. :photo_id является аргументом. Допустим, мы переходим на example.com/photo/2 . Что случилось бы? У нас есть ресурс, который передает наш запрос модели или контроллеру, и там мы получаем некоторые данные из Магазина. Если Магазин не находит его, он смотрит на сервер. :photo_id может быть использован для получения этих данных. В этом случае это выглядит на example.com/api/v1/photos/2 . Вы видите, что фотография множественного числа. Ember сам по себе ищет множественное число ресурса.

 App.Router.map(function() { this.resource('photo', {path: "/photo/:photo_id"}); this.resource('user', {path: "/user/:user_id"}); }); 

Маршрут начинается с первой буквы ресурса с заглавной буквы и должен находиться в пространстве имен приложения. Также добавьте слово «Маршрут» после названия ресурса. Так что для фото ресурса маршрут должен быть таким: App.PhotoRoute

Также следует расширить объект Route.

 App.PhotoRoute = Ember.Route.extend({}); 

Объект Route может иметь разные хуки для разных вещей. Два из этих хуков используются для определения имени контроллера для этого ресурса и определения модели. Давайте придерживаться модели.

 App.PhotoRoute = Ember.Route.extend({ model: function(params){ return this.store.find('photo', params.photo_id); } }); 

Внутри мы указали модель hook и передали параметр. Куда идет этот параметр? /photo/:photo_id имеет URL с параметром: /photo/:photo_id . photo_id хранится в params и может использоваться внутри функции. Не забывайте, что каждый ресурс и каждый маршрут имеет доступ к магазину. Объект Store сохраняет всю информацию внутри него и использует Local Storage для повышения производительности. Таким образом, это сокращает количество запросов на сервере. Вот почему разработка с Ember ускоряет ваше приложение — в конце концов, пользователи становятся счастливее.

Используя store.find('resource') вы можете получить все данные для этого ресурса из объекта store. Вы также можете получить только одну строку. Например, если вы хотите получить только фотографию с заданным идентификатором, используйте объект магазина и найдите ресурс фотографии с указанным идентификатором в качестве второго параметра.

 return this.store.find('photo', params.photo_id); 

Ember ищет данные в example.com/api/v1/photo_id . По умолчанию Ember работает с данными, ища идентификаторы. Если вы вставили некоторые отношения для этого ресурса, вы также можете получить данные, связанные с ним. Вот и весь код для маршрутов, очень похожий для каждого случая и простой:

 App.IndexRoute = Ember.Route.extend({ model: function(){ return this.store.find('photo'); } }); App.PhotoRoute = Ember.Route.extend({ model: function(params){ return this.store.find('photo', params.photo_id); } }); App.UserRoute = Ember.Route.extend({ model: function(params){ return this.store.find('user', params.user_id); } }); 

Небольшое примечание: IndexRoute — это маршрут по умолчанию, связанный с корневым URL. И под корнем я имею в виду example.com/ URL. Существуют и другие маршруты по умолчанию, например ApplicationRoute, который выполняется при запуске приложения.

Модельный объект

Внутри Ember’s Model Object вы указываете данные и их тип ресурса. Приятной особенностью Ember является то, что когда значение ресурса изменяется, а другое значение зависит от измененного значения, оно автоматически обновляется с помощью некоторой магии наблюдателя. Модель должна начинаться с заглавной буквы и расширять объект модели.

 App.Photo = DS.Model.extend({}); 

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

Фотомодель должна выглядеть примерно так:

 var attr = DS.attr; // This cuts my writting. Inside the model i use attr instead of DS.attr App.Photo = DS.Model.extend({ user_id: attr("number"), // The expected value is a number url: attr("string"), // The expected value is a string title: attr("string"), description: attr("string"), category: attr("number"), fullUrl: function(){ // Another value that depends on core values. return "/files/" + this.get("url"); }.property('url'), backgroundImage: function(){// This depends on another value but not on core ones return 'background: url("' + this.get("fullUrl") + '") no-repeat; '; }.property('fullUrl') }); 

С помощью attr ( DS.attr ) вы указываете, как вы хотите DS.attr эти данные. Например, мы хотим, user_id значение user_id было числом. Таким образом, мы защищены от внешних данных.

Модель пользователя похожа. Помните, Ember Data будет искать его в /api/v1/users . Соглашение об именах немного сложнее. Например, если вы запрашиваете ресурс с именем user , Ember Data будет искать example.com/prefix/users , а если вы запрашиваете определенный ресурс, то запрашивает example.com/prefix/users/user_id . Знание того, как Laravel раскрывает данные и как Ember хочет, чтобы их данные спасли вас от головной боли

 App.User = DS.Model.extend({ name: attr("string"), lastname: attr("string"), username: attr("string"), fullname: function(){ return this.get('name') + " " + this.get('lastname'); }.property("name", "lastname") }); 

Взгляды

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

Вы помните первый шаблон, который мы написали в третьей части? Это шаблон приложения. Этот шаблон будет отображаться при доступе к example.com в браузере.

Вы не можете разрабатывать приложение дальше, если не вносите изменения в этот шаблон. Заменить <!-- The content will be here --> комментарий с: {{outlet}} .

Почему? Все наши ресурсы вложены в маршрут приложения. Но если я смотрю на свой код, я не вижу индекса на маршрутизаторе. Это почему?

По умолчанию example.com/ url назначается IndexRoute если вы не назначили этот URL другому маршруту. Ember переводит приложение на верхний уровень по умолчанию, и все вложено в него. Если вы запрашиваете URL внутри этого маршрута приложения, то, используя {{outlet}} в качестве заполнителя, Ember берет шаблон этого маршрута и помещает его в этот заполнитель.

Давайте сделаем еще один шаблон и используем его для IndexRoute . Это будет первая страница. Первый шаблон — это шаблон приложения. Индексный шаблон будет отображаться внутри {{outlet}} .

data-template-name — это имя шаблона. Весь код внутри этого тега скрипта будет помещен внутри {{outlet}} .

 <script type="text/x-handlebars" data-template-name="index"> <ul class="small-block-grid-1 medium-block-grid-2 large-block-grid-3 custom-grid-ul"> {{#each}} <li {{bind-attr style="backgroundImage"}}> <div class="custom-grid"> {{#link-to 'photo' this}}<h5 class="custom-header">{{title}}</h5>{{/link-to}} <span>Author: {{user_id}}</span> </div> </li> {{/each}} </ul> </script> 

{{#each}} это что-то вроде цикла. Если модель шаблона имеет массив, и мы хотим запросить все данные, тогда мы используем этот специальный тег. Этот цикл начинается с {{#each}} и заканчивается {{/each}} . Внутри этого цикла мы используем все значения, которые возвращаются из цикла. Помните, что внутри модели мы вернули photo ресурса. Модель извлекает данные из хранилища и возвращает их в шаблон. Посмотрите на фото модель. Мы указали некоторые поля там, и эти поля используются внутри шаблона, внутри цикла {{#each}} .

Другим специальным тегом является тег {{#link-to}} . Этот тег генерирует ссылку на маршрут фотографии и передает параметр. Параметр this является id этого объекта. В этом случае удостоверение личности с фотографией. И снова тег {{#link-to}} заканчивается на {{/link-to}} . {{title}} не является специальным тегом, он просто получает значение заголовка для этого объекта.

Добавим фото шаблон. Этот шаблон является шаблоном для Photo Route. Опять же, я предлагаю ознакомиться с соглашениями по присвоению имен для того, как это отображается и как осуществляется присвоение имен.

 <script type="text/x-handlebars" data-template-name="photo"> <div style="text-align: center;"> <h4>{{title}}</h4><br> <img {{bind-attr src="fullUrl" alt="title"}}><br> <span>Author: {{#link-to 'user' user_id}}{{author.name}}{{/link-to}}</span> </div> </script> 

Используя тег {{attribute-here}} , выбранные атрибуты будут созданы внутри этого тега. Мы использовали его внутри <img> . Использование {{title}} внутри тега в качестве атрибута вызывает проблемы. Handlebars и Ember генерируют некоторые дополнительные объекты внутри DOM. Чтобы решить эту проблему, мы используем {{bind-attr}} . Когда мы делаем ссылку на маршрут пользователя, мы передаем параметр: user_id . При нажатии на ссылку URL-адрес будет обновлен с помощью example.com/user/the_id . Но у нас пока нет пользовательского шаблона. Давайте создадим один.

 <script type="text/x-handlebars" data-template-name="user"> <h2>Hello: {{fullname}} </h2> </script> 

Это отображает только полное имя. fullname — это свойство нашего App.User которое расширяет DS.Model .

Прежде чем закончить все это, я сделал гифку того, как это выглядит:

enter image description here

Завершение

Как видите, это еще не завершенный проект. Много работы все еще необходимо; идти вперед и экспериментировать с ним, учиться у него и изменить его. Полный проект будет размещен на моей учетной записи Github и будет часто обновляться. Любой вклад приветствуется, я хотел бы работать вместе.

В этой серии мы многому научились — я тоже многому научился. Мы увидели, как работать с облаком, узнали о его положительных и отрицательных сторонах. Мы увидели, как мы можем разработать приложение в обеих средах и как настроить Laravel для разных сред. Мы увидели, как создать REST API с помощью Laravel, оставаясь на одной странице приложения с Ember. Надеюсь, вам всем было так же весело, как и мне.

Как вы думаете? Хотите узнать больше о Heroku, Laravel или Ember? Оставьте комментарий ниже, всегда приятно услышать отзывы читателей!