Skip to content

Latest commit

 

History

History
1063 lines (854 loc) · 59.7 KB

File metadata and controls

1063 lines (854 loc) · 59.7 KB

Представления

Представления организованы подключением шаблонизатора 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 - промежуточный обработчик авторизации. Подробнее о данном промежуточном обработчике смотрите auth
    • req - объект запроса.
    • 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"

Шаблон empty состоит из слоёв head, body и footer. Подробно о слоям описано в разделе Слои, а сам код блока выглядит так:

{{> head }}
  <body>
  {{{ body }}}
  {{> footer}}
  </body>
</html>

Где:

  • {{> head }} - интеграция слоя с названием head.hbs. Подробно о слое head описано в подразделе Слой "head".
  • {{{ body }}} - интеграция слоя со слоями различных страниц.
  • {{> footer}} - интеграция слоя с названием footer.hbs. Подробно о слое footer описано в подразделе Слой "footer".

⬆️ К оглавлению раздела "Представления".


Шаблон "main"

Шаблон main состоит из слоёв head, navbar, body и footer. Подробно о слоях описано в разделе Слои, а сам код блока выглядит так:

{{> head }}
  <body>
    {{> navbar}}
    <div class="container">
      {{{ body }}}
    </div>
  {{> footer}}
  </body>
</html>

Где:

  • {{> 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".

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.

Cлои


Шаблонизатор Handlebars поддерживает интеграцию файлов в структуры кода других файлов, тем самым позволяет избежать коппирование кода. Интегрированные файлы называются слоями шаблона или partials. В слои могут выносится как обязательные структуры для правильного формирования DOM-дерева так и необязательные, по структуре отрисовки страниц HTML, но являются одинаковыми в ряде страниц отрисовки.

Таблица всех слоёв, которые применяются в шаблонах:

Слои Зона участка
head Отвечает за header разметки
navbar Отвечает за navbar разметки
footer Отвечает за footer разметки

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Слой "head"

Код слоя head:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
  <link rel="stylesheet" href="/index.css">
  <title>{{title}}</title>
</head>

Где:

  • {{title}} - данные заголовка title приходящие вместе с данными шаблона, в функции контроллера.

Передачу данных контроллером в шаблонизатор страницы index.hbs:

router.get('/', (req, res) => {
  res.render('index', {
    title: 'Главная страница',
    isHome: true
  })
})

Где:

  • title -данные заголовка title о которых говорилось выше.

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Слой "navbar"

Код слоя navbar:

<nav>
  <div class="nav-wrapper">
    <a href="/" class="brand-logo">Магазин курсов</a>
    <ul id="nav-mobile" class="right hide-on-med-and-down">

      {{#if isHome}}
        <li class="active"><a href="/">Главная</a></li>
      {{else}}
        <li><a href="/">Главная</a></li>
      {{/if}}

      {{#if isCourses}}
        <li class="active"><a href="/courses">Курсы</a></li>
      {{else}}
        <li><a href="/courses">Курсы</a></li>
      {{/if}}

      {{#if isAuth}}
          {{#if isAdd}}
              <li class="active"><a href="/add">Добавить курс</a></li>
          {{else}}
              <li><a href="/add">Добавить курс</a></li>
          {{/if}}


          {{#if isProfile}}
              <li class="active"><a href="/profile">Профиль</a></li>
          {{else}}
              <li><a href="/profile">Профиль</a></li>
          {{/if}}
          {{#if isCard}}
              <li class="active"><a href="/card">Корзина</a></li>
          {{else}}
              <li><a href="/card">Корзина</a></li>
          {{/if}}

          {{#if isOrder}} 

              <li class="active"><a href="/orders">Заказы</a></li>
          {{else}}
              <li><a href="/orders">Заказы</a></li>
          {{/if}}

            <li><a href="/auth/logout">Выйти</a></li>
        {{else}}
          {{#if isLogin}}
              <li class="active"><a href="/auth/login#login">Войти</a></li>
          {{else}}
              <li><a href="/auth/login#login">Войти</a></li>
          {{/if}}
        {{/if}}
    </ul>
  </div>
</nav>

Где:

  • <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}}
      <li class="active"><a href="/">Главная</a></li>
    {{else}}
      <li><a href="/">Главная</a></li>
    {{/if}}

Где:

  • {{#if isHome}} - возможность шаблонизатора Handlebars. Условие, тело которого срабатывает при активности флага isHome.
  • <li>class="active"</li> - класс, отвечающий за активность ссылки.
  • <a href="/">Главная</a> - ссылка на страницу "Главная".

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Слой "footer"

Код слоя footer:

<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="/app.js"></script>

Где:

  • 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>

<p>Здесь только лучшие курсы</p>

Где:

  • <h1>Добро пожаловать</h1> - заголовок страницы.

Итоговый вид представления "Главная":

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Курсы

Код шаблона сourses.hbs:

<h1>Курсы</h1>

{{#if courses.length}}
  {{#each courses}}
  <div class="row">
    <div class="col s6 offset-s3">
      <div class="card">
        <div class="card-image">
          <img src="{{img}}" alt="{{title}}">
        </div>
        <div class="card-content">
          <span class="card-title">{{title}}</span>
          <p class="price">{{price}}</p>
        </div>
        <div class="card-action actions">
          <a href="/courses/{{id}}" target="_blank">Открыть курс</a>
          {{#if @root.isAuth}}

          {{#ifeq userId._id @root.userId}}
            <a href="/courses/{{id}}/edit?allow=true">Редактировать</a>
          {{/ifeq}}

          <form action="/card/add" method="POST">
            <input type="hidden" name="_csrf" value="{{@root.csrf}}">
            <input type="hidden" name="id" value="{{id}}">
            <button type="submit" class="btn btn-primary">Купить</button>
          </form>
          {{/if}}
        </div>
      </div>
    </div>
  </div>
  {{/each}}
{{else}}
<p>Курсов пока нет</p>
{{/if}}

Где:

  • <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 убирающий контекст функции-обработчика. В примере выше - это возможность редактирование данных одного курса.

Итоговый вид представления "Курсы":

2.png

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Добавить курс

Код шаблона add.hbs:

<h1>Добавить новый курс</h1>

{{#if error}}
<p class="alert">{{error}}</p>
{{/if}}

<form action="/add" method="POST">
  <div class="input-field">
    <input id="title" name="title" type="text" class="validate" required value="{{data.title}}">
    <label for="title">Название курса</label>
    <span class="helper-text" data-error="Введите название"></span>
  </div>

  <div class="input-field">
    <input id="price" name="price" type="number" class="validate" required min="1" value="{{data.price}}">
    <label for="price">Цена курса</label>
    <span class="helper-text" data-error="Введите цену"></span>
  </div>

  <div class="input-field">
    <input id="img" name="img" type="text" class="validate" required value="{{data.img}}">
    <label for="img">URL картинки</label>
    <span class="helper-text" data-error="Введите url картинки"></span>
  </div>

  <input type="hidden" name="_csrf" value="{{csrf}}">

  <button class="btn btn-primary">Добавить курс</button>
</form>

Где:

  • {{#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'.

Итоговый вид представления "Добавить курс":

3.png

⬆️ К оглавлению раздела "Представления"


Профиль

Код шаблона profile.hbs:

<h1>Профиль</h1>

<div class="row">
  <div class="col s6">
    {{#if user.avatarUrl}}
      <img 
        class="avatar"
        src="{{user.avatarUrl}}" 
        alt="avatar-{{user.name}}"
      >
    {{else}}
      <p>Аватара нет</p>
    {{/if}}
  </div>

  <div class="col s6">
    <form action="/profile" method="POST" enctype="multipart/form-data">
      <p>Ваше email: <strong>{{user.email}}</strong></p>

      <div class="input-field">
        <input id="name" name="name" type="text" class="validate" required value="{{user.name}}">
        <label for="name">Ваше имя</label>
        <span class="helper-text" data-error="Имя не может быть пустым"></span>
      </div>

      <div class="file-field input-field">
        <div class="btn">
          <span>Аватар</span>
          <input type="file" name="avatar">
        </div>
        <div class="file-path-wrapper">
          <input class="file-path validate" type="text">
        </div>
      </div>

      <input type="hidden" name="_csrf" value="{{csrf}}">

      <button type="submit" class="btn">Изменить</button>
    </form>
  </div>
</div>

Где:

  • <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'.

вид представления "Профиль":

4.png

⬆️ К оглавлению раздела "Представления".


Корзина

Код шаблона card.hbs:

<h1>Корзина</h1>

<div id="card">
{{#if courses.length}}
  <table>
    <thead>
      <tr>
        <th>Название</th>
        <th>Количество</th>
        <th>Действия</th>
      </tr>
    </thead>
    <tbody>
      
      {{#each courses}}
      <tr>
        <td>{{title}}</td>
        <td>{{count}}</td>
        <td>
          <button 
          class="btn btm-small js-remove" 
          data-id="{{id}}"
          data-csrf="{{@root.csrf}}"
          
          >Удалить</button>
        </td>
      </tr>
      {{/each}}
    </tbody>
  </table>

  <p><strong>Цена:</strong> <span class="price">{{price}}</span></p>

  <form action="/orders" method="POST">
    <input type="hidden" name="_csrf" value="{{csrf}}">
    <button type="submit" class="btn">Сделать заказ</button>
  </form>
{{else}}
  <p>Корзина пуста</p>
{{/if}}
</div>

Где:

  • {{#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}}
  {{#each orders}}
    <div class="row">
      <div class="col s6 offset-s3">
        <div class="card">
          <div class="card-content">
            <span class="card-title">
              Заказ <small>{{_id}}</small>
            </span>
            <p class="date">{{date}}</p>
            <p><em>{{user.userId.name}}</em> ({{user.userId.email}})</p>

            <ol>
              {{#each courses}}
                <li>
                  {{course.title}} (x<strong>{{count}}</strong>)
                </li>
              {{/each}}
            </ol>

            <hr>

            <p>Цену: <span class="price">{{price}}</span></p>
          </div>
        </div>
      </div>
    </div>
  {{/each}}
{{else}}
  <p>Заказов пока нет</p>
{{/if}}

Где:

  • <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}}.

Итоговый вид представления "Заказы":

6.png

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Вход / Выход

Управление логированием пользователя наличивает несколько страниц. Таблица всех представлений логирования:

Представление Страница приложения
login Страница логина
password Страница сброса пароля
reset Страница смены пароля

Вход / Регистрация


Код шаблона auth/login.hbs:

<div class="auth">
    <div class="row">
        <div class="col s12">
            <ul class="tabs">
                <li class="tab col s6"><a class="active" href="#login">Войти</a></li>
                <li class="tab col s6"><a class="active" href="#register">Регистрация</a></li>
            </ul>
        </div>
        <div id="login" class="col s6 offset-s3">

            {{#if loginError}}
                <p class="alert">{{loginError}}</p>
            {{/if}}

            <h2>Войти в магазин</h2>
            <form action="/auth/login" method="POST">
                <div class="input-field">
                    <input id="email" name="email" type="email" class="validate" required>
                    <label for="email">Email</label>
                    <span class="helper-text" data-error="Введите email"></span>
                </div>

                <div class="input-field">
                    <input id="password" name="password" type="password" class="validate" required>
                    <label for="password">Пароль</label>
                    <span class="helper-text" data-error="Введите пароль"></span>
                </div>

                <input type="hidden" name="_csrf" value="{{csrf}}">

                
                <button class="btn btn-primary" type="submit">Войти</button>
                <p><a href="/auth/reset">Забыли пароль?</a></p>
            </form>
        </div>
        <div id="register" class="col s6 offset-s3">

            {{#if registerError}}
                <p class="alert">{{registerError}}</p>
            {{/if}}

            <h2>Создать аккаунт</h2>
            <form action="/auth/register" method="POST" novalidate>
                <div class="input-field">
                    <input id="remail" name="email" type="email" class="validate" required>
                    <label for="remail">Email</label>
                    <span class="helper-text" data-error="Введите email"></span>
                </div>

                <div class="input-field">
                    <input id="name" name="name" type="text" class="validate" required>
                    <label for="name">Ваше имя</label>
                    <span class="helper-text" data-error="Введите имя"></span>
                </div>

                <div class="input-field">
                    <input id="rpassword" name="password" type="password" class="validate" required>
                    <label for="rpassword">Пароль</label>
                    <span class="helper-text" data-error="Введите пароль"></span>
                </div>

                <div class="input-field">
                    <input id="confirm" name="confirm" type="password" class="validate" required>
                    <label for="confirm">Пароль ещё раз</label>
                    <span class="helper-text" data-error="Введите пароль"></span>
                </div>

                <input type="hidden" name="_csrf" value="{{csrf}}">

                <button class="btn btn-primary" type="submit">Зарегистрироваться</button>
            </form>
        </div>
    </div>
</div>

Где:
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'.

Итоговый вид страницы "Логин":

3.png

2. Раздел "Регистрация"

  • {{#if registerError}} - условие, которое выполняется при ошибке ввода почты без cимвола "@" - registerError.
  • <form action="/auth/register" method="POST" novalidate> - возможность шаблонизатора Handlebars. Метод запроса post по маршруту '/auth/register', который выполняет функции контроллера добавление нового пользователя в базу данных.

⬅️ Подробнее о контроллере, который используется в разделе "Регистрация", шаблона Авторизации смотрите Метод запроса post по маршруту '/auth/register'.

Итоговый вид страницы "Регистрация":

4.png

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Сброс пароля

Код шаблона auth/password.hbs:

<div class="row">
    <div class="col s6 offset-s3">
        {{#if error}}
            <p class="alert">{{error}}</p>
        {{/if}}
        <h2>Забыли пароль?</h2>
        <form action="/auth/reset" method="POST">
            <div class="input-field">
                <input id="email" name="email" type="email" class="validate" required>
                <label for="email">Email</label>
                <span class="helper-text" data-error="Введите email"></span>
            </div>

            <input type="hidden" name="_csrf" value="{{csrf}}">
            <button class="btn btn-primary" type="submit">Cбросить</button>
        </form>
    </div>
</div>

Где:

  • {{#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'.

Итоговый вид страницы "Сброс пароля":

5.png

⬅️ Подробнее о контроллере, который используется в шаблоне "Сброс пароля" смотрите Метод запроса post по маршруту '/auth/reset'.
⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Смена пароля

Код шаблона auth/reset.hbs:

<div class="row">
    <div class="col s6 offset-s3">
        {{#if error}}
            <p class="alert">{{error}}</p>
        {{/if}}
        <h2>Задать новый пароль</h2>
        <form action="/auth/password" method="POST">
            <div class="input-field">
                <input id="password" name="password" type="password" class="validate" required>
                <label for="password">Новый пароль</label>
                <span class="helper-text" data-error="Введите пароль"></span>
            </div>

            <input type="hidden" name="_csrf" value="{{csrf}}">
            <input type="hidden" name="userId" value="{{userId}}">
            <input type="hidden" name="token" value="{{token}}">
            <button class="btn btn-primary" type="submit">Обновить пароль</button>
        </form>
    </div>
</div>

Где:

  • {{#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:

<div class="course">
  <h1>{{course.title}}</h1>

  <img src="{{course.img}}" alt="{{course.title}}">

  <p class="price big">{{course.price}}</p>
</div>

Где:

  • <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}}.

Итоговый вид страницы одного курса:

2.png

⬆️ К оглавлению раздела "Представления".
↩️ К оглавлению документации.


Редактировать курс

Код шаблона course-edit.hbs:

<h1>Редактировать {{course.title}}</h1>

<form action="/courses/edit" method="POST" class="course-form">
  <div class="input-field">
    <input id="title" name="title" type="text" class="validate" required value="{{course.title}}">
    <label for="title">Название курса</label>
    <span class="helper-text" data-error="Введите название"></span>
  </div>

  <div class="input-field">
    <input id="price" name="price" type="number" class="validate" required min="1"  value="{{course.price}}">
    <label for="price">Цена курса</label>
    <span class="helper-text" data-error="Введите цену"></span>
  </div>

  <div class="input-field">
    <input id="img" name="img" type="text" class="validate" required  value="{{course.img}}">
    <label for="img">URL картинки</label>
    <span class="helper-text" data-error="Введите url картинки"></span>
  </div>

  <input type="hidden" name="id" value="{{course.id}}">
  <input type="hidden" name="_csrf" value="{{csrf}}">

  <button type="submit" class="btn btn-primary">Редактировать курс</button>
</form>

<form action="/courses/remove" method="POST">
  <input type="hidden" name="_csrf" value="{{csrf}}">
  <input type="hidden" name="id" value="{{ course.id }}">
  <button class="btn red">Удалить курс</button>
</form>

Где:

  • <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', который выполняет функции контроллера удаление курса.

На странице курсов, как было сказано выше, пользотель может редактировать только те курсы, которые сам создал:

2.png

Итоговый вид страницы "Редактировать курс":

3.png

⬅️ Подробнее о контроллерах, который используется в шаблоне "Редактировать курс" смотрите Метод запроса post по маршруту '/courses/remove' и Метод запроса post по маршруту '/courses/edit'.
⬆️ К оглавлению раздела "Представления"
↩️ К оглавлению документации.


Страница 404

Код шаблона 404.hbs:

<h1>404 Ошибка</h1>
<p>Страница не найдена</p>
<hr>

<a href="/">Вернуться на главную страницу</a>

Где:

  • <h1>404 Ошибка</h1> - заголовок страницы 404.
  • <p>Страница не найдена</p> - параграф с текстом Страница не найдена.
  • <a href="/">Вернуться на главную страницу</a> - ccылка ведущая на главную страницу сайта.

Итоговый вид страницы "Страница 404":

1.png

⬆️ К оглавлению раздела "Представления"
↩️ К оглавлению документации.