Представления организованы подключением шаблонизатора Handlebars, который обрабатывает и виззуализирует данные, которые предоставляются в файлах hbs. Данные, которые отрисовывает шаблонизатор управляются контроллерами, а маршруты по которым шаблоны визуализируются управляются маршрутизатором.
⬅️ Подробную информацию обо всех маршрутах смотрите в разделе Маршрутизация.
⬅️ Подробную информацию обо всех контроллерах смотрите в разделе Контроллеры.
↩️ К оглавлению документации.
- Представления
Процедура подключения шаблонизатора:
1. Подключение шаблонизатора в главный файл index.js:
Подключение базовых параметров Handlebars в главный файл index.js:
const exphbs = require('express-handlebars')
const hbs = exphbs.create({
defaultLayout: 'main',
extname: 'hbs',
helpers: require('./utils/hbs-helpers')
})
...
Где:
exphbs.create({})
- метод create объекта exphbs, который принимает список аргументов для инициализации базовых параметров шаблонизатора Handlebars.defaultLayout
- обязательный аргумент. Определяет слой по умолчанию 'main'.extname
- обязательный аргумент. Указывает на расширение файлов.helpers
- необязательный параметр. Может быть как строкой, в случае одного промежуточного обработчика для Handlebars так и объектом, в случае передачи нескольких промежуточних обработчиков.
Инициализация шаблонизатора:
...
app.engine('hbs', hbs.engine)
app.set('view engine', 'hbs')
app.set('views', 'views')
...
Где:
app.engine('hbs', hbs.engine)
- метод engine экземпляра app фреймворка express принимающий объект подключения базовых параметров hbs и метод запуска hbs.engine.app.set('view engine', 'hbs')
- метод set экземпляра app фреймворка express определяющий запуск шаблонизатора hbs.app.set('views', 'views')
- метод set экземпляра app фреймворка express определяющий корневую папку представлений views.
2. Создание шаблонов:
Шаблон - это подоснова или структура. Разработчик, исходя из целей формирует определённую визуализацию - конкретный шаблон или же использует общий шаблон для нескольких визуализаций. Для удобства создаются представления со схожей структурой. Учитывая, что Handlebars позволяет определить шаблон по умолчанию, а это в свою очередь позволяет решить две задачи одним действием. В главной файле index.js определяем шаблон по умолчанию:
...
const hbs = exphbs.create({
defaultLayout: 'main'
...
})
...
Где:
exphbs.create({})
- метод create объекта exphbs, который принимает список аргументов для инициализации базовых параметров шаблонизатора Handlebars.defaultLayout
- ключ принимающий значение шаблона по умолчанию main.
3. Интеграция шаблонов в систему маршрутов:
При определении маршрутизации разных страниц приложения в маршрутах прописывается функция визуализации. Интеграция шаблонизатора на примере маршрутизатора "add":
router.get('/', auth, (req, res) => {
res.render('add', {
title: 'Добавить курс',
isAdd: true
})
})
Где:
router.get('/', auth, (req, res) => {})
- маршрутизатор с типом запросом get, который перенаправляет на страницу '/add'.
auth
- промежуточный обработчик авторизации. Подробнее о данном промежуточном обработчике смотрите authreq
- объект запроса.res
- объект ответа.
res.render('add', {})
- функция визуализации render объекта ответа res, которая принимает три аргумента:
add
- обязательный параметр. Определяет код визуализации. В нашем случае код визуализации - страница 'add' шаблонизатора Handlebars. Код визуализации может быть также строкой.{}
- необязательный параметр. Объект с локальными данными, которые передаються объектом res в шаблон 'add'.function () {}
- необязательный параметр. Callback-функция.
title
- ключ принимающий значение 'Добавить курс'.isAdd
- флаг-переключатель. По умолчанию флаг имеет значение: true.
Следует отметить, что поскольку в передаваемых, локальных данных, мы не передаём ключ layout зарезервированный handlebars, то шаблонизатор для отрисовки страницы 'add' берёт шаблон по умолчанию - main, который был подключён в главном файле index.js выше. В случае, если бы шаблон по умолчанию отсутствовал, движок Handlebars вывел бы ошибку с требованием предоставить шаблон визуализации. Шаблоны могут быть также индивидуальными, как в случае шаблона одного курса:
router.get('/:id', async (req, res) => {
try {
const course = await Course.findById(req.params.id)
res.render('course', {
layout: 'empty',
title: `Курс ${course.title}`,
course
})
} catch (e) {
console.log(e)
}
})
Где:
layout
- ключ принимающий значения шаблона 'empty'.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Представления в своём результате - это страницы отрисовки. Таблица всех представлений:
Блок | Представление | Cтраница |
---|---|---|
- | index | Страница "Главная" |
- | courses | Страница "Курсы" |
- | add | Страница "Добавить курс" |
- | profile | Страница "Профиль" |
- | card | Страница "Корзина" |
- | orders | Страница "Заказы" |
auth | login | Страница входа в аккаунт пользователя |
auth | password | Страница ввода нового пароля пользователя |
auth | reset | Страница сброса пароля пользователя |
- | 404 | Страница 404 |
- | course | Страница одного курса |
- | course-edit | Страница "Редактировать курс" |
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
Шаблоны - это структуры. Различные структуры определяют различную визуализацию разных блоков кода.
Таблица всех шаблонов, которые используются в приложении:
Шаблон | Предназначение слоя |
---|---|
empty | Шаблон разметки страницы вывода одного курса |
main | Шаблон разметки для всех страниц кроме страницы вывода одного курса |
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Шаблон empty состоит из слоёв head, body и footer. Подробно о слоям описано в разделе Слои, а сам код блока выглядит так:
Где:
{{> head }}
- интеграция слоя с названием head.hbs. Подробно о слое head описано в подразделе Слой "head".{{{ body }}}
- интеграция слоя со слоями различных страниц.{{> footer}}
- интеграция слоя с названием footer.hbs. Подробно о слое footer описано в подразделе Слой "footer".
⬆️ К оглавлению раздела "Представления".
Шаблон main состоит из слоёв head, navbar, body и footer. Подробно о слоях описано в разделе Слои, а сам код блока выглядит так:
Где:
{{> head }}
- интеграция слоя с названием head.hbs. Подробно о слое head описано в подразделе Слой "head".<body></body>
- тег контента.{{> navbar}}
- интеграция слоя с названием navbar.hbs. Подробно о слое navbar описано в подразделе Слой "navbar".<div class="container"></div>
- контейнер с классом сontainer, который выделяет код слоя body в отдельный контейнер в структуре DOM-дерева.{{{ body }}}
- интеграция слоя со слоями различных страниц.{{> footer}}
- интеграция слоя с названием footer.hbs. Подробно о слое footer описано в подразделе Слой "footer".
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Шаблонизатор Handlebars поддерживает интеграцию файлов в структуры кода других файлов, тем самым позволяет избежать коппирование кода. Интегрированные файлы называются слоями шаблона или partials. В слои могут выносится как обязательные структуры для правильного формирования DOM-дерева так и необязательные, по структуре отрисовки страниц HTML, но являются одинаковыми в ряде страниц отрисовки.
Таблица всех слоёв, которые применяются в шаблонах:
Слои | Зона участка |
---|---|
head | Отвечает за header разметки |
navbar | Отвечает за navbar разметки |
footer | Отвечает за footer разметки |
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код слоя head:
Где:
{{title}}
- данные заголовка title приходящие вместе с данными шаблона, в функции контроллера.
Передачу данных контроллером в шаблонизатор страницы index.hbs:
router.get('/', (req, res) => {
res.render('index', {
title: 'Главная страница',
isHome: true
})
})
Где:
title
-данные заголовка title о которых говорилось выше.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код слоя navbar:
Где:
<a href="/" class="brand-logo">Магазин курсов</a>
- ссылка ведущая на главную страницу сайта.<ul id="nav-mobile" class="right hide-on-med-and-down">
- список с ID id="nav-mobile" и классом class="right hide-on-med-and-down".{{#if}}
- возможность шаблонизатора Handlebars. Условие, выполнение которого срабатывает на флаг-переключатель активной страницы.
isHome
- возможность шаблонизатора Handlebars. Флаг-переключатель активной страницы Главная в слое navbar. Описание логики ниже.isCourses
- возможность шаблонизатора Handlebars. Флаг-переключатель активной страницы Курсы в слое navbar. Логика аналогична коду блока isHome.
{{else}}
- возможность шаблонизатора Handlebars. Условие срабатывающее если условие if == false.{{/if}}
- возможность шаблонизатора Handlebars. Закрытие тега условия if.
Флаг-переключатель isHome:
Где:
{{#if isHome}}
- возможность шаблонизатора Handlebars. Условие, тело которого срабатывает при активности флага isHome.<li>class="active"</li>
- класс, отвечающий за активность ссылки.<a href="/">Главная</a>
- ссылка на страницу "Главная".
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код слоя footer:
Где:
https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js
- подключение CDN фреймворка materialize./app.js
- подключение скриптов для стилизации контейнеров, классов и т.п.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Структура сайта имеет несколько представлений, которые переключаются посредством вызова кода контроллеров действиями пользователя.
Таблица представлений основных страниц:
Представление | Страница приложения |
---|---|
index | Страница "Главная" |
courses | Страница "Курсы" |
add | Страница "Добавить курс" |
card | Страница "Корзина" |
orders | Страница "Заказы" |
profile | Страница "Профиль" |
login | Страница входа в аккаунт пользователя |
password | Страница ввода нового пароля пользователя |
reset | Страница сброса пароля пользователя |
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона index.hbs:
Где:
<h1>Добро пожаловать</h1>
- заголовок страницы.
Итоговый вид представления "Главная":
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона сourses.hbs:
Где:
<img src="{{img}}" alt="{{title}}">
- данные отправленные контроллером для визуализации в шаблоне курсов courses.hbs.<span class="card-title">{{title}}</span>
- текстовый контейнер с классом "card-title" содержащий формат заголовка title.<div class="card-action actions"></div>>
- контейнер с классом class="card-action actions" содержащий действие, в случае нажатия на ссылку.<a href="/courses/{{id}}" target="_blank">Открыть курс</a>
- ссылка, которая перенаправляет на страницу одного курса.
/courses/{{id}}
- возможность шаблонизатора Handlebars. Маршрут страницы одного курса.
{{#if}} @root.isAuth
- возможность шаблонизатора Handlebars. Условие проверки авторизации пользователя @root.isAuth при которой, isAuth === true выполняется тело условия.{{/if}}
- возможность шаблонизатора Handlebars. Закрытие условия.{{#ifeq userId._id @root.userId}}
- возможность шаблонизатора Handlebars. Промежуточный обработчик ifeq передающийся в базовых аргументах hbs в главном файле index.js. Код промежуточного обработчика указан ниже.
userId._id
- ID пользователя который хранится в базе данных MongoDB.@root.userId
- ID пользователя, который авторизировался на сайте.
<a href="/courses/{{id}}/edit?allow=true">Редактировать</a>
- ссылка на редактирование данных одного курса.
/courses/{{id}}/edit?allow=true
- URL-путь редактирование данных одного курса.
<form action="/card/add" method="POST">
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту 'card/add', который выполняет функции контроллера добавление одного курса в корзину.<input type="hidden" name="_csrf" value="{{@root.csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.<input type="hidden" name="id" value="{{id}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик значения {{id}}.
⬅️ Подробнее о контроллере, который используется в шаблоне "Курсы" смотрите Метод запроса post по маршруту '/card/add'.
Функция ifeq:
module.exports = {
ifeq(a, b, options) {
if (a == b) {
return options.fn(this)
}
return options.inverse(this)
}
}
Где:
ifeq(a, b, options) {}
- функция ifeq принимающая два аргумента:
a
- параметр, принимающий в примере выше userId._id.b
- параметр, принимающий в примере выше @root.userId.options
- передаваемые функция, метод, данные и т.д.
if (a == b) {}
- условие сравнения ID пользователя из базы данных с ID пользователем авторизированным на сайте.
options.fn(this)
- свойство fn объекта options передающий контекст функции-обработчика. В примере выше - это возможность редактирование данных одного курса.options.inverse(this)
- свойство inverse объекта options убирающий контекст функции-обработчика. В примере выше - это возможность редактирование данных одного курса.
Итоговый вид представления "Курсы":
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона add.hbs:
Где:
{{#if error}}
- условие, при котором выполняется текст ошибки.
<p class="alert">{{error}}</p>
- параграф с классом alert с текстом ошибки {{error}}.
{{/if}}
- возможность шаблонизатора Handlebars. Закрытие условия сравнения.<form action="/add" method="POST"></form>
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/add/', который выполняет функции контроллера добавление одного курса в базу данных.<input id="title" name="title" type="text" class="validate" required value="{{data.title}}">
- input содержащий ID id="title" поля ввода, его название name="title", тип type="text", класс class="validate" и обязательный атрибут данных required value="{{data.title}}.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.
⬅️ Подробнее о контроллере, который используется в шаблоне "Добавить курс" смотрите Метод запроса post по маршруту '/add'.
Итоговый вид представления "Добавить курс":
⬆️ К оглавлению раздела "Представления"
Код шаблона profile.hbs:
Где:
<form action="/profile" method="POST" enctype="multipart/form-data">
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/profile', который выполняет функции контроллера добавление данных пользователя в его профиль.enctype="multipart/form-data"
- cвойство которое добавляется в HTML форму для корректной работы модуля multer.
<p>Ваше email: <strong>{{user.email}}</strong></p>
- параграф с текстом почты пользователя.<input id="name" name="name" type="text" class="validate" required value="{{user.name}}">
- input содержащий ID id="name" поля ввода, его название name="name", тип type="text", класс class="validate" и обязательный атрибут данных required value="{{user.name}}.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.
⬅️ Подробнее о контроллере, который используется в шаблоне "Профиль" смотрите Метод запроса post по маршруту '/profile'.
вид представления "Профиль":
⬆️ К оглавлению раздела "Представления".
Код шаблона card.hbs:
Где:
{{#if courses.length}}
- условие, которое выводит все курсы из массива курсов courses.{{#each courses}}
- условие, которое выполняется для каждого курса из массива курсов courses.<button class="btn btm-small js-remove" data-id="{{id}}" data-csrf="{{@root.csrf}}">Удалить</button>
- кнопка удаляющая один курс по его ID data-id="{{id}}" с проверкой авторизации пользователя data-csrf="{{@root.csrf}}.{{/each}}
- возможность шаблонизатора Handlebars. Закрытие условия each.<p><strong>Цена:</strong> <span class="price">{{price}}</span></p>
- текстовый контейнер выводящий итоговую стоимость {{price}}.<form action="/orders" method="POST">
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/orders', который выполняет функции контроллера добавление одного заказа.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.
Итоговый вид представления "Корзина":
⬅️ Подробнее о контроллере, который используется в шаблоне "Корзина" смотрите Метод запроса post по маршруту '/orders'.
⬆️ К оглавлению раздела "Представления"
Код шаблона orders.hbs:
Где:
<h1>Мои заказы</h1>
- заголовок страницы "Заказы".{{#if orders.length}}
- условие, которое выводит все заказы из массива заказаов courses orders.{{#each orders}}
- условие, которое выполняется для каждого заказа из массива заказов orders.<small>{{_id}}</small>
- уникальный ID заказа.<p class="date">{{date}}</p>
- параграф с классом class="date" содержащий дату выдачи заказа.<p><em>{{user.userId.name}}</em> ({{user.userId.email}})</p>
- параграф содержащий имя пользователя и его почту в скобках.{{#each courses}}
- условие, которое выполняется для каждого заказа в массиве курсов courses.{{course.title}} (x<strong>{{count}}</strong>)
- выводит название курса {{course.title}} и в скобках количество единиц этого курса {{count}}.<p>Цену: <span class="price">{{price}}</span></p>
- текстовый параграф, который выводит итоговую стоимость всех курсов {{price}}.
Итоговый вид представления "Заказы":
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Управление логированием пользователя наличивает несколько страниц. Таблица всех представлений логирования:
Представление | Страница приложения |
---|---|
login | Страница логина |
password | Страница сброса пароля |
reset | Страница смены пароля |
Код шаблона auth/login.hbs:
Где:
1. Раздел "Логин"
{{#if loginError}}
- условие, которое выполняется при ошибке ввода логина loginError.<form action="/auth/login" method="POST">
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/auth/login', который выполняет функции контроллера ввода данных пользователя для авторизации на сайте.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.<p><a href="/auth/reset">Забыли пароль?</a></p>
- ccылка введующая на страницу "Сброса пароля".
⬅️ Подробнее о контроллере, который используется в разделе "Логин", шаблона Авторизации смотрите Метод запроса post по маршруту '/auth/login'.
Итоговый вид страницы "Логин":
2. Раздел "Регистрация"
{{#if registerError}}
- условие, которое выполняется при ошибке ввода почты без cимвола "@" - registerError.<form action="/auth/register" method="POST" novalidate>
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/auth/register', который выполняет функции контроллера добавление нового пользователя в базу данных.
⬅️ Подробнее о контроллере, который используется в разделе "Регистрация", шаблона Авторизации смотрите Метод запроса post по маршруту '/auth/register'.
Итоговый вид страницы "Регистрация":
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона auth/password.hbs:
Где:
{{#if error}}
- условие, которое выполняется при ошибке ввода логика error.<form action="/auth/reset" method="POST"
> - возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/auth/login', который выполняет функции контроллера cброса пароля.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.<button class="btn btn-primary" type="submit">Cбросить</button>
- кнопка выполняющая код контроллера запроса post по маршруту '/auth/reset'.
Итоговый вид страницы "Сброс пароля":
⬅️ Подробнее о контроллере, который используется в шаблоне "Сброс пароля" смотрите Метод запроса post по маршруту '/auth/reset'.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона auth/reset.hbs:
Где:
{{#if error}}
- условие, которое выполняется при ошибке ввода логика error.<form action="/auth/password" method="POST"></form>
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/auth/password', который выполняет функции контроллера cмены пароля.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.<input type="hidden" name="userId" value="{{userId}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик проверяющий ID пользователя {{userId}}.<input type="hidden" name="token" value="{{token}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик проверяющий наличие токена пользователя {{token}}.<button class="btn btn-primary" type="submit">Обновить пароль</button>
- кнопка выполняющая код контроллера запроса post по маршруту '/auth/password'.
⬅️ Подробнее о контроллере, который используется в шаблоне "Смена пароля" смотрите Метод запроса post
по маршруту '/auth/password'
.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Таблица представлений дополнительных страниц:
Представление | Страница приложения |
---|---|
course | Страница одного курса |
course-edit | Страница "Редактировать курс" |
404 | Страница 404 |
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона courses.hbs:
Где:
<h1>{{course.title}}</h1>
- заголовок с названием курса {{course.title}}.<img src="{{course.img}}" alt="{{course.title}}">
- картинка курса {{course.img}} с названием заголовка курса {{course.title}}.<p class="price big">{{course.price}}</p>
- параграф с классом class="price big" показывающий цену курса {{course.price}}.
Итоговый вид страницы одного курса:
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.
Код шаблона course-edit.hbs:
Где:
<form action="/courses/edit" method="POST" class="course-form"></form>
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/courses/edit', который выполняет функции контроллера редактирования курса.<input id="title" name="title" type="text" class="validate" required value="{{course.title}}">
- input содержащий ID id="title" поля ввода, его название name="title", тип type="text", классclass="validate"
и обязательным атрибутом required, который должен наличивать значение заголовка курса {{course.title}}.<input type="hidden" name="id" value="{{course.id}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик проверки ID курса {{@root.csrf}}.<input type="hidden" name="_csrf" value="{{csrf}}">
- возможность шаблонизатора Handlebars. Cкрытый обработчик прав доступа авторизации {{@root.csrf}}.<button type="submit" class="btn btn-primary">Редактировать курс</button>
- кнопка выполняющая код контроллера запроса post по маршруту '/courses/edit'.<form action="/courses/remove" method="POST"></form>
- возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/courses/edit', который выполняет функции контроллера удаление курса.
На странице курсов, как было сказано выше, пользотель может редактировать только те курсы, которые сам создал:
Итоговый вид страницы "Редактировать курс":
⬅️ Подробнее о контроллерах, который используется в шаблоне "Редактировать курс" смотрите Метод запроса post по маршруту '/courses/remove' и Метод запроса post по маршруту '/courses/edit'.
⬆️ К оглавлению раздела "Представления"
↩️ К оглавлению документации.
Код шаблона 404.hbs:
Где:
<h1>404 Ошибка</h1>
- заголовок страницы 404.<p>Страница не найдена</p>
- параграф с текстом Страница не найдена.<a href="/">Вернуться на главную страницу</a>
- ccылка ведущая на главную страницу сайта.
Итоговый вид страницы "Страница 404":
⬆️ К оглавлению раздела "Представления"
↩️ К оглавлению документации.