В этой статье хотел бы рассказать немного про библиотеку, первую версию которой я создал еще в конце прошлого года. Суть очень простая — расширить возможности языка HTML, чтобы можно было без JavaScript'а писать простые и рутинные вещи: отправка формы в json формате, загрузка HTML тимплейтов на определенную страницу(по сути модульная система для HTML через http/s запросы), турболинки(привет пользователям RoR), простая шаблонизация на основе ответов ajax запросов и немного еще.
Библиотека называется EHTML или Extended HTML. Основана она на небезызвестной идее веб компонентов. Она доступна на гитхабе, там довольно таки структурированная документация с примерами. В этой статье я просто опишу основные идеи, возможно кому-то это зайдет.
Подключение библиотеки
Начнем с того, что для использования либы не нужен ни npm, ни cli тула для создания проекта, все что требуется — это HTML странички, где вы включаете скрипт библиотеки:
<head>
<script src="/../js/ehtml.bundle.min.js" type="text/javascript"></script>
</head>
Модульная система
Начнем с простого — составление HTML странички из множества других:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<script src="/../js/ehtml.bundle.min.js" type="text/javascript"></script>
</head>
<body class="main">
<div class="articles">
<e-html data-src="/../html/first.html"></e-html>
<e-html data-src="/../html/second.html"></e-html>
<e-html data-src="/../html/third.html"></e-html>
</div>
</body>
</html>
Для этого мы используем кастомный элемент e-html
, который имеет единственный атрибут data-src
, где мы указываем где раздается HTML часть страницы. То есть если например first.html
выглядит как-то так:
<div class="article">
<!-- some content of the first article -->
</div>
то в конечном счете, наша страница будет зарендерена следующим образом:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<script src="/../js/ehtml.bundle.min.js" type="text/javascript"></script>
</head>
<body class="main">
<div class="articles">
<div class="article">
<!-- content of the first article -->
</div>
<div class="article">
<!-- content of the second article -->
</div>
<div class="article">
<!-- content of the third article -->
</div>
</div>
</body>
</html>
То есть теги e-html
будут заменены контентом из HTML файла, который мы указывали в data-src
атрибуте.
В библиотеке широко применяются элементы с тегом <template/>
. Тут вы можете найти информацию про этот элемент. Одним из примеров использования этого элемента является e-wrapper
, который появился в последней версии либы. По сути это <template/>
элемент с атрибутом is="e-wrapper"
и он позволяет заврапить динамический контент каким-нибудь статическим шаблоном. То есть скажем у вас есть статический шаблон следующего вида:
<!-- /../html/wrapper.html -->
<div class="base">
<p>
Header content
</p>
<p id="dynamic-content">
<span>Default content</span>
</p>
<p>
Footer content
</p>
</div>
И скажем у вас есть динамический контент, который вы хотите заврапить им:
<body class="main">
<template
is="e-wrapper"
data-src="/../html/wrapper.html"
data-where-to-place="#dynamic-content"
data-how-to-place="instead">
<p>
Variation of content
</p>
</template>
</body>
Когда вы откроете страничку с кодом выше, она будет сформирована следующим образом:
<div class="base">
<p>
Header content
</p>
<p>
Variation of content
</p>
<p>
Footer content
</p>
</div>
С помощью атрибута data-src
вы указываете HTML файл, откуда вы хотите взять статический шаблон. В атрибуте data-where-to-place
вы указываете селектор элемента в статическом шаблоне куда вы хотите поместить динамический контент, который как раз находится внутри e-wrapper
тимплейта. Ну и с помощью data-how-to-place
вы описываете как именно вы хотите заврапить. Есть три возможные опции: instead
(контент просто заменит элемент с селектором, который указан в data-where-to-place
), before
(контент будет помещен до указанного элемента) и соответсвенно after
(контент будет помещен после элемента). Таким образом, элементы e-html
и e-wrapper
тимплейт позволяют комбинировать html шаблоны для составления страниц. Конечно при рендеринге делаются дополнительные http запросы, чтобы загрузить страницы, однако если использовать кэш запросов — то это проблема полностью нивелируется.
Шаблонизация ajax запрсов
Скажем у вас есть эндпоинт, который отдает информацию о музыкальном альбоме в json формте:
/../album/{title}
title = 'Humbug'
{
"title": "Humbug",
"artist": "Arctic Monkeys",
"type": "studio album",
"releaseDate": "19 August 2009",
"genre": "psychedelic rock, hard rock, stoner rock, desert rock",
"length": "39:20",
"label": "Domino",
"producer": "James Ford, Joshua Homme"
}
Тогда используя тимплейтный элемент с атрибутом is="e-json"
, вы можете загрузить и замапить ответ на HTML:
<template is="e-json" data-src="/../album/Humbug" data-object-name="albumResponse">
<div data-text="Title: ${albumResponse.body.title}"></div>
<div data-text="Artist: ${albumResponse.body.artist}"></div>
<div data-text="Type: ${albumResponse.body.type}"></div>
<div data-text="Release date: ${albumResponse.body.releaseDate}"></div>
<div data-text="Genre: ${albumResponse.body.genre}"></div>
<div data-text="Length: ${albumResponse.body.length}"></div>
<div data-text="Label: ${albumResponse.body.label}"></div>
<div data-text="Producer: ${albumResponse.body.producer}"></div>
</template>
Атрибут data-src
указывает, от куда вы загружаете информацию в json формате, data-object-name
— это имя объекта ответа, который содержит основные компоненты http ответа: body
, headers
и statusCode
. Атрибут data-text
позволяет вам использовать значения объекта в качестве текста, который будет помещен в шаблон внутри e-json
тимплейта.
Также есть возможность использовать циклы и условия при маппинге. Допустим, у нас есть следующий эндпоинт с информацией о музыкальном альбоме со списком песен:
title = 'Humbug'
{
"title": "Humbug",
"artist": "Arctic Monkeys",
"songs": [
{ "title": "My Propeller", "length": "3:27" },
{ "title": "Crying Lightning", "length": "3:43" },
{ "title": "Dangerous Animals", "length": "3:30" },
{ "title": "Secret Door", "length": "3:43" },
{ "title": "Potion Approaching", "length": "3:32" },
{ "title": "Fire and the Thud", "length": "3:57" },
{ "title": "Cornerstone", "length": "3:18" },
{ "title": "Dance Little Liar", "length": "4:43" },
{ "title": "Pretty Visitors", "length": "3:40" },
{ "title": "The Jeweller's Hands", "length": "5:42" }
]
}
Тогда с помощью тимплейта e-for-each
можно показать весь список песен:
<template is="e-json" data-src="/../album/Humbug" data-object-name="albumResponse">
<div data-text="Title: ${albumResponse.body.title}"></div>
<div data-text="Artist: ${albumResponse.body.artist}"></div>
<div><b data-text="${albumResponse.body.songs.length} songs:"></b></div>
<template
is="e-for-each"
data-list-to-iterate="${albumResponse.body.songs}"
data-item-name="song">
<div class="song-box">
<div data-text="No. ${song.index}/${album.songs.length}"></div>
<div data-text="Title: ${song.title}"></div>
<div data-text="Length: ${song.length}"></div>
</div>
</template>
</template>
В атрибуте data-list-to-iterate
мы указываем список из ответа, который мы хотим итерировать. А в атрибуте data-item-name
мы декларируем имя переменной для каждого айтема, которое мы будем использовать внутри e-for-each
тимплейта.
Также можно например использовать в комбинации тимплейты e-for-each
и e-if
для отображения лишь определенных песен:
<template is="e-json" data-src="/../album/Humbug" data-object-name="albumResponse">
<div data-text="Title: ${albumResponse.body.title}"></div>
<div data-text="Artist: ${albumResponse.body.artist}"></div>
<div><b data-text="${albumResponse.body.songs.length} songs:"></b></div>
<template is="e-for-each"
data-list-to-iterate="${albumResponse.body.songs}"
data-item-name="song">
<template
is="e-if"
data-condition-to-display="${(song.length.split(':')[0] * 60 + song.length.split(':')[1] * 1) <= 210}"
>
<div class="song-box">
<div data-text="No. ${song.index}/${album.songs.length}"></div>
<div data-text="Title: ${song.title}"></div>
<div data-text="Length: ${song.length}"></div>
</div>
</template>
</template>
</template>
В атрибуте data-condition-to-display
тимплейта e-if
мы указываем условие, при котором тот или иной айтем отображается.
Отправка формы
Последнее что хочется показать в этой статье — это отправка форм. Очень часто хочется отправлять данные из формы в json формате, поэтому был создан элемент e-form
, который предоставляет такую возможность.
Допустим у нас есть следующий эндпоинт, который позволяет создавать/сохранять новые альбомы:
/artist/{name}/albums/add
name = 'Arctic Monkeys'
POST
Example of expected request body: {
"title": "Humbug",
"type": "studio album",
"releaseDate": "19 August 2009",
"genre": ["psychedelic rock", "hard rock", "stoner rock", "desert rock"],
"length": "39:20",
"label": "Domino",
"producer": "James Ford, Joshua Homme"
}
Тогда форма, которая может посылать такие запросы будет выглядеть примерно так:
<e-form>
Title:
<input type="text" name="title">
Type:
<input type="radio" name="type" value="studio album" checked>
<label for="one">One</label>
<input type="radio" name="type" value="live album" checked>
<label for="one">One</label>
Release date:
<input type="date" name="releaseDate">
Genre:
<input type="checkbox" name="genre" value="psychedelic rock">
<input type="checkbox" name="genre" value="hard rock">
<input type="checkbox" name="genre" value="stoner rock">
<input type="checkbox" name="genre" value="desert rock">
Total length:
<input type="time" name="totalLength">
Producer:
<input type="text" name="producer">
<button
id="send"
data-request-url="/artist/Arctic_Monkeys/albums/add"
data-request-method="POST"
data-request-headers="{}"
data-ajax-icon="#ajax-icon"
data-response-name="savedAlbum"
onclick="this.form.submit(this)"
data-actions-on-response="
logToConsole('response: ', '${savedAlbum}');
"
/>
<img id="ajax-icon" src="/../images/ajax-loader.gif"/>
</e-form>
Ключевое отличие от обычной формы в том, что мы декларируем детали эндпоинта в элементе, на который действует пользователь, в данном случае это кнопка. С помощью соответствующих атрибутов мы указываем метод, хедеры, путь для эндопинта. Также можно даже указать ajax иконку, которая будет активирована во время запроса. Также мы должны естественно указать действие, на которое будет отправляться форма — в данном случае это клик. И также мы указываем действие, которое мы делаем когда ответ получен. Полный список поддерживаемых таких действий вы можете найти здесь.
Больше информации
Если вы заинтересовались идеей и даже хотите попробовать библиотеку в деле или просто что-то узнать побольше, то в README библиотеки есть исчерпывающая информация о поддерживаемых элементах. Также можете посмотреть видео, где я пишу простое блог приложение исключительно используя EHTML.
Буду рад ответить на вопросы, если такие возникнут. И спасибо за внимание!