# Разделение документа на части

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

Основные привилегии разделения файла на части: - Избежание повторного копирования компонентов - Быстрое изменение одного компонента вместо десятка повторяющихся - Для автоматического назначения атрибутов у повторяющихся элементов, например для привязки input к label путем добавления уникального идентификатора при каждом включении и тд. - Для отделения данных от HTML разметки, например вынести текст в отдельные файлы

Приведем несколько примеров.

В проекте имеются несколько HTML страниц, и каждая страница состоит из трех основных частей:

  • Head
  • Body
  • Footer

body является динамическим элементом и зависит от страницы, head и footer - глобальные, то есть, они повторяются на каждой странице. Создадим проект исходя из наших требований:

.
└─ source
	└─ html
		├─ index.html
        ├─ contact_us.html
		└─ global
			|─ head.html
			└─ footer.html

head.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>My page</title>
    </head>
    <body>

footer.html

        <footer>
            <p class="copyright">Lorem ipsum dolor sit.</p>
        </footer>
        <script src="/jquery.js"></script>
    </body> 
</html>

index.html, contact_us.html

{{{include "global/head.html"}}}        
<div class="app">
    <!-- ... -->
</div>
{{{include "global/footer.html"}}} 

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

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

# HTML страницы и части

Начнем с того, что HTML плагин видит два типа .html файлов:

  • Страница
  • HTML часть

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

  • Страницы - являются корневым документом, и не могут быть включены в другие страницы!
  • Части - отдельно вынесенная часть html разметки, которая может быть включена в другие части или в страницы!

Распознавание осуществляется автоматически по следующим критериям:

  • Страницы имеют <html></html> элемент

однако, этим поведением можно управлять путем добавление в документ комментария UNGIC:PART или UNGIC:PAGE:

<!-- UNGIC:PART -->
<html lang="en">
    <!-- Преобразовали страницу в часть -->
</html>

или

<!-- UNGIC:PAGE -->
<body>
    <!-- Преобразуем часть в страницу  -->
</body>

TIP

  • HTML страницы не могут быть включены друг в друга!
  • HTML части могут включать в себя другие части и быть включены в HTML страницы!

TIP

Части и страницы могут включать в себя любой поддерживаемый тип файлов HTML плагина с помощью include инструмента.

Стоит отметить, что html части и страницы обрабатываются handlebars шаблонизатором (opens new window) и имеют глобальный объект данных, но не имеют возможность принимать динамические данные!.

# Глобальный объект данных

При включении частей или шаблонов с помощью include инструмента, передаются глобальные данные ungic проекта, а также, информация о текущем корневом документе и включаемом шаблоне:

{
    "ungic": {
        "fs": {
            "dirs": {
                "source": "source",
                "dist": "dist",
                "temp": "temp"
            },
            "dist": {
                "css": "css",
                "fonts": "fonts",
                "img": "img",
                "js": "js"
            },
            "source": {
                "scss": "scss",
                "html": "html",
                "icons": "icons",
                "assets": "assets"
            }
        },
        // Информация о проекте
        "project": {
            "name": "app",
            "version": "1.0.0",
            "author": "unbywyd",
            "address": "http://127.0.0.1:2020"
        },
        // Текущая модель шаблона
        "model": {
            "id": "dGVtcGxhdGVcdGVzdC5oYnM=",
            "path": "template\\test.hbs"
        },
        // Информация о корневой странице
        "page": {
            "id": "dGVzdC5odG1s",
            "path": "test.html"
        },
        "UID": "_drvazpg3t"
    }
}

# Поддерживаемые типы файлов для включения

Простые типы:

  • .html - html части, обрабатываются с помощью handlebars шаблонизатора
  • .text - текстовые файлы включаются в виде текста
  • .md - markdown документы включаются в виде HTML

Шаблоны:

  • .hbs - handlebars шаблоны

типы файлов, которые могут быть использованы в виде данных:

  • .json - файл с данными в JSON формате
  • .yaml - файл с данными в YAML формате

Включение выше перечисленных типов файлов осуществляется непосредственно с помощью include инструмента.

Кроме того, HTML плагин позволяет расширять список поддерживаемых файлов, путем добавления пользовательский обработчиков!

TIP

HTML плагин позволяет зарегистрировать любой пользовательский тип файлов и создать для них обработчики!

# Шаблоны

В примере выше, мы использовали статические файлы для включения head.html и footer.html часть в нашу страницу, улучшим наш пример с использованием шаблонов:

Для того, чтобы наша "шапка" сайта принимала динамический заголовок, переименуем наш head.html файл в head.hbs, теперь наш файл обрабатывается handlebars шаблонизатором и готов принимать данные!

Изменим включение head.html файла на head.hbs и передадим данные в виде queryString, а точнее заголовок страницы:

index.html

{{{include "global/head.hbs" data="title=Home page"}}}        
<div class="app">
    <!-- ... -->
</div>
{{{include "global/footer.html"}}} 

включим наши данные в сам шаблон:

head.hbs

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{{title}}</title>
    </head>
    <body>

теперь, наша "шапка" страницы может получать динамический заголовок в зависимости от страницы проекта:

contact_us.html

{{{include "global/head.hbs" data="title=Contact us"}}}        
<div class="app">
    <!-- ... -->
</div>
{{{include "global/footer.html"}}} 

TIP

Шаблоны могут включаться в любые сущности которые обрабатываются handlebars шаблонизатором! По умолчанию, это .html страницы, .html части и сами .hbs шаблоны!

TIP

Для включения шаблонов используется include инструмент, а для передачи данных используется аргумент data.

# Данные для шаблонов

Данные которые можно передать для генерации шаблонов:

  • ссылку на .json файл с данными
  • ссылку на .yaml файл с данными
  • инлайн в queryString формате
  • идентификаторы экспортируемых данных из SASS фреймворка
  • icons идентификатор для включения экспортируемых данных из Icons плагина (Устарело, следует использовать параметр icons)
// Data from .json file
{{{include "global/head.hbs" data="./data/person.json"}}}    

// Data from .yaml file
{{{include "global/head.hbs" data="./data/person.yaml"}}}  

// Inline as queryString
{{{include "global/head.hbs" data="first_name=Artem&last_name=Pupkin"}}}  

// Icons from icons plugin (deprecated)
{{{include "global/head.hbs" data="icons"}}} 
// use instead
{{{include "global/head.hbs" icons=true}}}  


// Get data from sass framework
{{{include "global/head.hbs" sass="project"}}}  

# data

С помощью data аргумента осуществляется передача данных к шаблону! Значением data аргумента может быть:

  • Относительный путь к .JSON / .YAML файлам

    {{includes "template.hbs" data="data/data.json"}}
    
  • queryString значение

    {{includes "template.hbs" data="name=Artem&nickname=Unbywyd"}}
    
  • объект данных (можно передавать данные от шаблона к шаблону)

    {{includes "template2.hbs" icons=true}}
    
    // template2
    {{includes "template3.hbs" data=icons.fonts}}
    
  • icons значение для включения данных об иконках

    // (устарело)
    {{includes "icons.hbs" data="icons"}} 
    
    // используйте для этого параметр icons
    {{includes "icons.hbs" icons=true}}
    

# icons данные

Для того, чтобы передать включаемому шаблону данные об иконках проекта из Icons плагина, следует воспользоваться параметром icons

{{{include "./templates/head.hbs" icons=true}}}  

./templates/head.hbs

{{{debug "icons"}}}

# Данные из SASS framework

SASS framework экспортирует данные о текущем sass проекте, кроме того, все компоненты sass фреймворка могут экспортировать данные в JSON формате. Все экспортируемые данные попадают в dist/exports/sass-options.json файл текущего проекта, кроме этого, все экспортируемые данные доступны HTML плагину и могут быть использованы при генерации шаблонов.

Для того, чтобы включить данные из SASS фреймворка, следует воспользоваться sass аргументом include инструмента:

// Передать все экспортируемые данные sass фреймворка
{{{include "templates/test.hbs" sass=""}}}  

// templates/test.hbs

{{{debug "sass"}}}
// Фильтровать опции, оставить только те, что связанные с проектом
{{{include "templates/test.hbs" sass="project"}}}  

// получить конкретную опцию
{{{include "templates/test.hbs" sass="project__theme" single=true}}}  

Экспортируем данные из app компонента

// components/app/once.scss

$export: this.export('author', (nickname: "Unbywyd")); 

Использование:


{{{include "templates/person.hbs" sass="app__author" single=true}}}  

// templates/person.hbs

{{{sass.nickname}}}

# cwd

С помощью cwd аргумента, можно назначать базовый путь к файлам относительно source/html директории:

{{{include "memu.hbs" data="menu.json" cwd="./profile/"}}}    

// тоже самое, но без cwd
{{{include "./profile/memu.hbs" data="./profile/menu.json"}}}    

# move

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

// page.html
// Передаем данные первому шаблону
{{{include "template.hbs" data="./data/data.json"}}}

// Первый шаблон имеет доступ к переданным данным
// template.hbs

{{{debug "icons"}}}

// Для того, чтобы передать все данные второму шаблону, 
// необходимо задать параметр move

// Передать все текущие входные данные к следующему шаблону
{{{include "template2.hbs" move=true}}}

// Можно передать конкретный объект
{{{include "template2.hbs" move=person.info}}}

// Совместное использование data + move
{{{include "template2.hbs" data=icons move=person.info}}}

# Отслеживание входных данных

Для того, чтобы узнать какие данные пришли к шаблону, следует воспользоваться debug инструментом:

head.hbs

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{{title}}</title>
    </head>
<body>
{{{debug}}}

Можно передать путь вложения объекта для отслеживания:

{{{debug "icons.fonts.0.name"}}}

Обратите внимание! Данный инструмент выводит на экран иерархическое дерево с входными данными в JSON формате, данный элемент является частью HTML, именно поэтому debug нужно использовать в валидном HTML месте, а точнее, должен находиться в видимом элементе (не скрытом посредством css), который располагается непосредственно в body элементе.

WARNING

Место использования debug инструмента влияет на его отображение в браузере! Используйте данный инструмент в валидном html месте документа.

# Пользовательский тип файлов

В первых версиях ungic, кроме handelbars была поддержка pug, underscore и mustache шаблонизаторов, начиная с 3.+ версии, базовым и единственным шаблонизатором остался handebars. Однако, был разработал API для добавления пользовательских типов файлов! Для того, чтобы добавить пользовательский тип файлов, необходимо зарегистрировать его в конфигурации проекта:

"html": {
    "beautify": {},
    "minifier": {},
    ...
    "customTypeHandlers": {
        "mustache": {
            "transformer": "./mustache/transformer",
            "includeHandler": "./mustache/includeHandler",
            "dev": false
        }                 
    },
    ...

Для регистрации пользовательского типа файла необходимо:

  • использовать customTypeHandlers параметр в конфигурации проекта в секции html плагина
  • в роли ключа данного объекта указать новый тип расширения файла
  • зарегистрировать асинхронный обработчик (transformer) для трансформации содержимого файла при каждом его изменении
  • зарегистрировать обработчик (includeHandler), который используется при включении include инструментом данного типа файла

# transformer

  • Arguments
    • content - содержимое файла которое нужно обработать и вернуть!
    • attrs - параметры файла

Функция трансформер, может быть асинхронной, должна возвращать обработанный входной content параметр. Данная функция будет вызываться каждый раз, когда содержимое файла будет изменено.

module.exports = async function(content, attrs) {
    return content
}

# includeHandler

  • Arguments
    • content - содержимое файла которое нужно обработать и вернуть!
    • attrs - все возможные данные включая входные параметры
    • compile - handlebars.compile метод

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

module.exports = function(content, attrs, compile) {
    return compile(content)(attrs);
} 

Пример с Mustache:

// ./mustache/includeHandler
const Mustache = require('mustache');

module.exports = function(content, attrs, compile) {
    return Mustache.render(content, attrs);
}