Вступление
Это вторая часть учебника Instaelm, где мы создаем клон Instagram в языке программирования ELM, чтобы продемонстрировать, как реализуется реальный мир проекта в ELM. Мы сосредоточиваемся на взаимодействии с другим кодом JavaScript и возможность взаимодействовать С сервером API, написанным в Node.js и используя Hapi.js библиотека.
В предыдущем уроке мы охватываем основы архитектуры ELM и как создавать вложенные компоненты.
Что вы узнаете
Ты выучишь:
- Как настроить сервер Hapi.js, который обслуживает фальшивые данные API
- Как получить данные от JavaScript в модуль ELM с портами
- Как отправить сообщение из модуля ELM для JavaScript для получения дополнительных данных API
Настраивать
В предыдущей части учебника мы подробно описали, как создать ELM, но для этой части учебника нам нужно только установить Hapi.js и Инертный Плагин, который используется для обслуживания статических файлов и каталогов.
Для этого мы запустим эту команду:
npm install --save hapi inert
Создание сервера API с HAPI: Server.js
Прежде чем мы сделаем все в ELM, давайте создадим наш сервер API. Сервер API будет служить данным о фотографиях, которые будут отображаться в фото сетке.
Импорт hapi.js.
В файле server.js
Мы начинаем, импортируя модуль HAPI:
var Hapi = require('hapi');
Создание сервера
Затем мы создаем сервер, который загружает на порт 3000:
var server = new Hapi.Server(); server.connection({ port: 3000 });
Получить фотографии маршрут данных
Первый маршрут будет подавать данные о фотографиях на Получить
просьба доступа к /фотографии/
Ур Данные должны будут сопоставить тип записи, который мы определили в первой части учебника (в файле PhotoView.elm
). Мы собираемся вернуть данные для двух фотографий, Первая фотография не будет иметь комментариев, пока второе фото будет иметь одно фото.
server.route({ method: 'GET', path: '/photos/', handler: function(request, reply) { var photos = { 'photos': [ { user: 'User', location: 'Some City', likesCount: 123, commentsCount: 0, comments: [], url: 'webinar-ad.png' }, { user: 'Another User', location: 'Another City', likesCount: 987, commentsCount: 11, comments: [{ user: 'User', message: 'Awesome photo!' }], url: 'webinar-ad.png' } ] }; reply(photos); } });
Понравить фото с почтовым запросом
Второй маршрут будет принят почтовый запрос на URL /как/
Это увеличит количество любит на фото. У него есть один Параметр: URL фото.
server.route({ method: 'POST', path: '/like/', handler: function(request, reply) { reply({ result: 'added like to photo with url: ' + request.payload.photo }); } });
Параметры, передаваемые запросу, находятся в request.payload
объект. Мы собираемся ответить с сообщением, которое говорит все в порядке.
Служение index.html и других статических файлов
Чтобы упростить вещи, мы собираемся использовать сервер HAPI, чтобы также служить статическими файлами в нашем проекте. Эти статические файлы:
index.html
: Файл Bootstrap, который загружает наш JavaScript, CSS и встраивает фото сетку на страницу.instaelm.js
: Компилированный проект Instaelm (в настоящее время составлен в настоящее время только с Main.elm, но позже в этом руководстве мы будем составлять другие модули в него).still.css
: Общие стили CSS для всех устройств.Style-Desktop.css
: Стиль CSS для настольных экранов.Style-Laptop.css
: Стиль CSS для экранов ноутбука.webinar-ad.png
: Пример фото.
Вот как мы обслуживаем эти файлы с HAPI:
server.register(require('inert'), function(err) { if (err) { throw err; } server.route({ method: 'GET', path: '/{param*}', handler: { directory: { path: '.' } } }); });
Мы просто обслуживаем все файлы из каталога проекта, используя Hapi каталог обработчик Отказ Маршрут создан, когда инертный
Плагин зарегистрирован и загружен в HAPI. Это не рекомендуется для настройки производства, но в нашем случае мы можем жить с ним, чтобы обеспечить Все работает должным образом.
Запуск сервера
Мы начинаем такой сервер:
server.start(function(err) { if (err) { throw err; } console.log('Server running at: ' + server.info.uri); });
Он выводит URL-адрес и номер порта сервера API, когда он начинает работать, в противном случае он бросает ошибку.
Запуск сервера API
Мы можем запустить файл Server.js, запустив:
node server.js
Теперь мы готовы сделать вызовы API из нашего веб-приложения.
Посетить http://localhost: 3000/ И вы увидите, что index.html загрузил вместе с JavaScript и stylesheets.
Использование портов для передачи данных между ELM и JavaScript
Теперь давайте настроим методы, которые сделают запросы API. После этого мы можем настроить модуль ELM для фотосетки с портом, чтобы мы могли загрузить данные в него.
Загрузка jquery.
Во-первых, нам нужно загрузить jQuery. Мы собираемся добавить следующую строку прямо перед тегом сценария «instaelm.js»:
Это jQuery загружена из Официальный сайт jQuery CDN Отказ
Создание запросов с jQuery ajax: index.html
Мы собираемся заменить текущий код JavaScript в index.html
С некоторым новым кодом, который сделает запрос на фотографии с API Сервер, и это позволит нам “нравится” фото.
Делать запрос на пост, как фотография
Вот код для любить фото:
function likePhoto(photoUrl) { var payload = { photo: photoUrl }; $.post('/like/', payload, function(data) { alert(data.result); }); }
Нам придется реализовать это в ELM на компоненте просмотра фотографий, чтобы, когда нам нравится фотография, он будет запускать этот запрос API.
Мы можем проверить, что это работает, позвонив Allshoto
Функция с Некоторые примеры данных:
likePhoto('test url', 'test user');
Он отобразит диалоговое окно оповещения в браузере, который показывает ответ с сервера API.
Создание запроса на получение фотографий
Теперь вот код для получения списка фотографий с сервера API для отображения на фото Grid:
function getPhotos(onSuccessCallback) { $.getJSON('/photos/', {}, function(data) { onSuccessCallback(data.photos); }); }
Передайте функцию обратного вызова, которая будет называться, когда запрос на получение было успешным. Мы передаем результат к функции обратного вызова. Это позволяет нам проверить этот запрос API и подключить его в ELM.
Вот как мы проверим эту функцию API:
getPhotos(function(photos) { alert('There are ' + photos.length + ' photos to render'); });
Загрузка данных в ELM с портами: Main.elm
Чтобы загрузить данные в ELM, мы собираемся создать порт Отказ Порты могут быть Используется для доступа к данным ELM из JavaScript и может использоваться для отправки данных в эльмах.
Отправка значений на JS – это команда. Прослушивание значений, входящих в от JS, является подпиской.
Объявление порта модуля
В Main.elm
Мы собираемся изменить объявление модуля, чтобы заявить, что этот модуль имеет порты:
port module Main exposing (..)
Замена начинающейпрограммы с программой
Тогда мы заменяем Главная
Функция со следующим кодом:
main = program { init = init , view = view , update = update , subscriptions = subscriptions }
Мы удаляем Главная
Определение функции, потому что мы больше не собираюсь использовать BeginnerProgram
функция. Мы меняем BeginnerProgram
к Программа
При импорте HTML.App.
Определение функции init
Теперь мы должны определить init
функция. Это будет возвращено Модель, которую мы уже определили для использования в качестве исходной модели для отображения.
init : (Model, Cmd Msg) init = (model, Cmd.none)
init
Функция – кортеж двух элементов; модель, а a сообщение. Это позволяет нам инициализировать компонент таким образом, когда триггеры мероприятие. Очень полезно, когда мы загружаем данные, как только Компонент загружен в домо.
В этом случае мы не хотим отправлять какое-либо сообщение и просто хотите использовать примерные данные в модели, которую мы уже определили.
Определение подписок
подписки
Функция также должна быть определена. Это устанавливает порт Фотографии
с сообщением о том, что он отправит всякий раз, когда это Обновлено:
subscriptions : Model -> Sub Msg subscriptions model = photos UpdatePhotos
Мы собираемся позвонить наше сообщение UpdatePhotos
И у нас будет добавить его в Msg
Тип рядом с OpenPhoto
и ClockPhoto
Сообщения.
Обновление типа MSG
Давайте добавим UpdatePhotos
сообщение для Msg
тип:
type Msg = OpenPhoto Photo | ClosePhoto | UpdatePhotos (List Photo)
Обновление функции обновления
Теперь, когда мы добавили новый тип MSG, нам придется обновить наши Обновить
Функция для обработки UpdatePhotos
сообщение.
Нам также нужно понадобиться обновить функцию, потому что подпись типа изменилась, когда мы переключились с Html.app.beginnerprogram
к Html.app.program
Отказ Подпись тока типа Обновить
принимает Модель
аргумент, а затем возвращает Модель
объект. Нам нужно изменить тип подписи, чтобы принять Модель
Аргумент, а затем верните кортеж, содержащий обновленную модель и сообщение для передачи (это полезно, когда мы хотим обновить модель и запускать другие сообщения). Нам также необходимо изменить значения возврата для включения Cmd.none
:
update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of OpenPhoto photo -> ({ model | photoOpened = Just photo }, Cmd.none) ClosePhoto -> ({ model | photoOpened = Nothing }, Cmd.none) UpdatePhotos newPhotos -> ({ model | photos = newPhotos }, Cmd.none)
Когда Обновить
Функция получает сообщение UpdatePhotos
это изменит поле Фотографии
существующей модели, чтобы показать новый набор фотографий.
Повторно компилируйте и вызовите GetPhotos в index.html
Теперь мы можем перекомпилировать код: Elm Make Main.elm --output instaelm.js
А потом, в index.html
Мы должны хранить результат встраивания Elm.main
а затем инициируйте звонок для GetPhotos с обратным вызовом, что Обновите фотосетку компонента в ELM:
// index.html var node = document.getElementById("main"); var main = Elm.Main.embed(node); setTimeout(function() { getPhotos(function(photos) { main.ports.photos.send(photos); }); , 300);
Чтобы получить доступ к порту, мы храним ссылку на встроенный главный составная часть. Затем после завершения запроса API мы используем Отправить
Функция Фотографии
Порт для отправки сообщения от JavaScript в ELM с данными.
Мы используем Сетримс
загрузить фотографии через несколько секунд.
Использование флагов для инициализации модели данных
Альтернативой этому использует Html.app.programhwithflags который позволяет нам пройти в «флагах», которые используются для создания исходной модели данных. Это позволило бы нам загрузить фотографии с сервера, а затем, как только она закончится загрузка, мы могли бы вставить основную фотосетку сетки в DOM с фотографиями.
Давайте просто пойдем вперед и замените предыдущий код в index.html
со следующим:
var node = document.getElementById("main"); var main; getPhotos(function(photos) { main = Elm.Main.embed(node, { name:'hello', photos: photos }); });
В приведенном выше фрагменте кода в функции обратного вызова прошли GetPhotos
Мы будем вставлять фото сетку ( elm.main
) в Дом и снабжение его флагами (начальные параметры компонент).
Так как мы используем ПрограммаWithflags
Мы должны изменить наше Главная
Функция в Main.elm
использовать ПрограммаWithflags
вместо Программа
И мы должны изменить init
функция принять Флаги
Параметр:
import Html.App exposing (programWithFlags) main = programWithFlags { init = init , view = view , update = update , subscriptions = subscriptions } type alias Flags = { photos : List Photo , name : String } init : Flags -> (Model, Cmd Msg) init flags = ({ model | photos = flags.photos , name = flags.name }, Cmd.none)
Чтобы сделать наш код более четкой, мы также определили псевдоним типа Флаги
Что позволяет легко видеть, какие флаги можно использовать при инициализации компонент. В этом случае мы хотим начальный список фотографий для Нагрузка и имя профиля, которое мы загружаем (это сохранить вещи простыми, и вы можете добавить больше флагов программы, если хотите).
Теперь давайте перенесем код. Возможно, что мы должны видеть, это то, что нагрузка страницы, запрос API сделан и, как только он закончен, компонент Photo Grid вставляется в DOM.
Использование ELM для отправки сообщений на другой код JavaScript: Main.elm, index.html
Теперь, когда мы можем передавать данные в ALM-компонент, мы также хотим отправлять данные из ELM обратно в другой код JavaScript. Мы хотим иметь возможность щелкнуть кнопку «Мне нравится» после открытия фото в фотовисе и имейте ли, что вызывает запрос jQuery ajax на наш сервер API.
Сначала мы обновляем BallyPhoto Message, чтобы принять аргумент и обновлять ссылки на это сообщение в компоненте вложенного фотовиев. Затем мы настроили Обработка сообщений из фотовив в основной компонент Обновить
функция. Тогда мы создаем и используем дополнительные порт отправлять сообщения обратно на JavaScript и запустите событие. Наконец, мы отвечаем на мероприятие с некоторым кодом jQuery для отправки данных понравившейся фотографии на сервер API.
Обновление сообщения AllsePhoto, чтобы принять аргумент
В компоненте фотовиев нам нужно обновить Msg
Типы, которые мы определили, в частности, мы должны повторно определить Allshoto
Тип, чтобы принять Фото
Тип записи как аргумент:
type Msg = LikePhoto Photo | SubmitComment String | CloseModal
Затем мы обновляем функцию, которая отображает кнопку «например,»:
likeButton : Photo -> Html Msg likeButton photo = div [ class "like-button" , onClick (LikePhoto photo) ] [ text "Like This Photo" ]
Мы также должны обновить Sidebartop
и боковая панель
рендеринг Функции.
Sidebartop
Функция должна принять фотографию, а не просто имя пользователя и местоположение фото:
-- PhotoView.elm sidebarTop : Photo -> Html Msg sidebarTop photo = div [ class "sidebar-top" ] [ div [ class "photo-info" ] [ div [ class "user" ] [ text photo.user ] , div [ class "location" ] [ text photo.location ] ] , div [ class "photo-actions" ] [ followButton , likeButton photo ] ]
Функция звонит на Sidebartop
будет обновляться для поддержки:
-- PhotoView.elm sidebar : Photo -> Html Msg sidebar photo = div [ class "sidebar" ] [ sidebarTop photo , sidebarCount photo.likesCount photo.commentsCount , sidebarComments photo.commentsCount photo.comments ]
Оглядываясь назад, было бы лучше пройти во всей фото Запись на Sidebarcount
и Боковой коммуникацию
функционирует, а не конкретные поля из записи. Когда вы изучаете ELM, вы найдете разные способы определить функции, которые соответствуют вашим целям и Рефакторинг прост и безопаснее, чем обычно, благодаря проверке типа компилятора ELM.
Картирование фотовидных сообщений, которые будут обрабатываться в фото GRID: Main.elm
Сообщения, которые отправляют фотовизте, являются исключительными для модуля PhotoView, они имеют тип Photoview.msg
Отказ То же самое верно в фото сетке в Main.elm, Msg
Тип в этом файле является исключительным для этого модуля.
Типы два разных типа, поэтому мы должны сопоставить Photoview.msg
Типы в Main.msg
Чтобы иметь возможность справиться с ними с нашими Обновить
функция.
Мы должны добавить Photoviewmsg
как новый тип, который принимает Photoview.msg
сообщение:
-- Main.elm type Msg = OpenPhoto Photo | ClosePhoto | UpdatePhotos (List Photo) | PhotoViewMsg PhotoView.Msg
Чтобы сделать фотовид, теперь мы должны обновить Photoview
Функция так, чтобы сообщения фотовив отображаются на типе фотовевми:
-- Main.elm photoView : String -> Photo -> Html Msg photoView newComment photoOpened = let model = { photo = photoOpened , newComment = newComment , showCloseButton = True } in model |> PhotoView.view |> Html.App.map PhotoViewMsg
Это делается с использованием Html.app.map . функция. Без этого ELM не будет скомпилировать наш файл, потому что типы не будут совпадать (photoview.msg не совпадает с MSG).
Отправка послания AltePhoto из ELM через порт
В нашем Обновить
Функция, которую мы должны обрабатывать случай фотовив Сообщения, такие как CloseModal и Allphoto получают:
-- Main.elm update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of OpenPhoto photo -> ({ model | photoOpened = Just photo }, Cmd.none) ClosePhoto -> ({ model | photoOpened = Nothing }, Cmd.none) UpdatePhotos newPhotos -> ({ model | photos = newPhotos }, Cmd.none) PhotoViewMsg msg -> case msg of PhotoView.SubmitComment comment -> (model, Cmd.none) PhotoView.CloseModal -> update ClosePhoto model PhotoView.LikePhoto { url } -> (model, likePhoto url)
Все сообщения, которые будут отправлены и получены через фото Grid Компонент и компонент представления вложенной фотографии необходимо обрабатывать Обновить
Функция, независимо от того, могут ли они вносить какие-либо изменения в модели.
Давайте посмотрим, что происходит, когда главная фотография Grid Компонент получает Allshoto
Сообщение от фотовид составная часть:
update msg model = case msg of PhotoViewMsg msg -> case msg of PhotoView.LikePhoto { url } -> (model, likePhoto url)
Полученное сообщение имеет тип Photoviewmsg
и имеет один Параметр, Msg
который имеет тип Photoview.msg
Отказ Дело, которое соответствует, когда мы отправляем AltPhoto, это Photoview.ikephoto
случай, который имеет один аргумент, Фото
записывать. Мы используем разрушительность в аргументе, потому что мы беспокоили только одно поле в записи, URL
поле.
Результатом является то, что модель не меняется, однако мы будем Отправка . URL
к Allshoto
порт.
Добавление нового порта для отправки сообщений через
Давайте добавим Allshoto
порт:
-- Main.elm port likePhoto : String -> Cmd msg
Это было прямо вперед, потому что нам нужно только объявить тип порта. Теперь мы можем получить URL фото в JavaScript из ELM.
Отвечая на сообщение с jQuery
Порт вяза, Allshoto
, установлен и при нажатии на «например» Кнопка после открытия модального фотовивного сообщения, сообщение будет передано через ELM, пока он не отправится через порт.
Вернуться к нашему index.html
файл. В JavaScript нам нужно подписаться на Allshoto
порт и всякий раз, когда он получает фото URL, мы Запустит запрос AJAX на сервер API.
Мы собираемся определить Подписаться
Функция, которая будет называться В ближайшее время загружено главная компонент фотосетки и подписыватся на функцию обратного вызова AllsePhoto, которую мы определили ранее Allshoto
порт:
// index.html function subscribe(mainApp) { mainApp.ports.likePhoto.subscribe(function(photoUrl) { likePhoto(photoUrl); }); }
Чтобы вызвать эту функцию, мы должны сначала звонить в GetPhotos
:
var node = document.getElementById("main"); var main; getPhotos(function(photos) { main = Elm.Main.embed(node, { name:'hello', photos: photos }); subscribe(main); });
Составьте его ( elm make main.elm --otput instaelm.js
) а затем запустить API Server ( Node Server.js
) и проверить http://localhost: 3000 Отказ Теперь вы должны получать начальную партию фотографий для отображения в сетке фото и сможете любить фотографии через модуль просмотра фотографий.
Заключение
Вот график вызова нашего Instagram Code. Вы можете увидеть, что существует много функций просмотра, и что способ к компонентам гнезда и обработки сообщений/событий очень четко. Взаимодействие между JavaScript и Elm компактно и легко видеть из небольшой коллекции функций.
Щелчок здесь Чтобы просмотреть изображение в полном объеме
В целом, вы обнаружите, что разработка на фронт-конце в ELM будет немного быстрее, чем на других языках, но что более важно, что всякий раз, когда вы должны отладить какой-то код, вы будете намного быстрее при обнаружении источника любых проблем.
Для получения дополнительной информации о взаимодействии JavaScript с ELM, не забудьте прочитать через Введение в ELM написано языком Создатель.
Спасибо за чтение этой второй части учебника. Я делаю больше Работайте здесь и там на instaelm, Instagram-Clone в Elm, а вы Проверьте код здесь: https://gitlab.com/rudolfo/instaelm/tree/master Часть два кода находится под тегом Часть-два
Отказ Я определенно поощряю всех, кто использует Реагировать или угловать чтобы дать вяза попробовать.