Стек: HTML, SCSS, TS, Webpack
Структура проекта:
- src/ — исходные файлы проекта
- src/components/ — папка с JS компонентами
- src/components/base/ — папка с базовым кодом
Важные файлы:
- src/pages/index.html — HTML-файл главной страницы
- src/types/index.ts — файл с типами
- src/index.ts — точка входа приложения
- src/scss/styles.scss — корневой файл стилей
- src/utils/constants.ts — файл с константами
- src/utils/utils.ts — файл с утилитами
Для установки и запуска проекта необходимо выполнить команды
npm install
npm run start
или
yarn
yarn start
npm run build
или
yarn build
Архитектурный паттерн: Model-View-Presenter. Код приложения разделён на:
- слой данных (model), отвечает за хранение и изменение данных;
- слой отображения (view), отвечает за отображение данных на странице;
- презентер, отвечает за связь между отображением и слоем данных.
Модель для работы с массивом товаров:
interface IAppModel {
products: ICard[];
preview: string | null;
isBlocked: boolean;
setItems(items: ICard[]): void;
setPreview(id: string): void;
getItem(cardId: string): ICard;
getItems(): ICard[];
}
Товар:
interface ICard {
id: string;
description: string;
image: string;
title: string;
category: string;
price: number| null;
}
Корзина:
interface IBasketModel {
products: ICard[];
counter: number;
addItem(cardId: string): number;
removeItem(cardId: string): void;
getTotalPrice(): number;
isBasketProduct(id: string): boolean;
getCounter(): number;
}
Данные покупателя:
interface IUserData {
payment: string;
email: string;
phone: string;
address: string;
}
Данные заказа:
interface IOrder extends IUserData {
total: number;
items: string[]; // массив с идентификаторами товаров
}
Типы ответа сервера после отправки заказа:
type TSuccessOrderResponse = {
id: string;
total: number;
}
type TFailOrderResponse = {
error: string;
}
Данные для обработчика события выбора метода оплаты:
export interface IPaymentPickEvent {
event: Event;
allButtons: Record<string, HTMLButtonElement>;
}
Данные для обработчика события добавления товара в корзину:
export type AddToBasketEvent = {
records: ICard;
card: Card;
};
Абстрактный класс, наследуемый классами слоя отображения. Реализует методы создания элемента с помощью шаблона,
его обновления и перерисовки, а также удаления из разметки. Конструктор класса принимает объект типа T,
для определения и последующего использования его данных при заполнении и рендеринге элемента.
Конструктор:
constructor(template: HTMLTemplateElement, events: IEvents) {
this.template = template;
this.events = events;
}
Поля:
protected template: HTMLTemplateElement
— ссылка на шаблон элемента;protected element: HTMLElement | HTMLButtonElement
— ссылка на разметку элемента;protected events: IEvents
— экземпляр EventEmitter;
Методы:
abstract fillElement(data: T): void
— заполняет элемент данными, переданными в параметре;render(): HTMLElement
- возвращает разметку элемента;remove(): void
— удаляет элемент из разметки;
Содержит базовую логику отправки запросов. В конструктор передаётся адрес сервера и опционально — объект с заголовками запроса.
Методы:
get
— выполнит GET-запрос на переданный в параметрах эндпоинт и вернёт Promise с ответом сервера;post
— принимает объект с данными, которые будут переданы в формате JSON в теле запроса, и отправляет эти данные на эндпоинт — второй параметр метода. Метод POST-запроса может быть переопределён путём задания третьего параметра, по-умолчанию выполняется POST.
Брокер событий позволяет отправлять события и подписываться на события, происходящие в системе. Класс используется в презентере для обработки событий и в слоях приложения для генерации событий. Основные методы, реализуемые классом описаны интерфейсом. Класс имеет следующие методы:
on
— устанавливает обработчик на событие;off
— снимает обработчик с события;emit
— воспроизводит событие;trigger
— возвращает функцию, которая при вызове инициализирует событие, переданное в параметрах метода. Таким образом, помимо обработчиков события может содержать дополнительную обработку.onAll
— вызвать все события;offAll
— удалить все события.
Класс необходим для работы с массивом карточек. Конструктор класса принимает экземпляр класса EventEmitter.\
Поля:
products: ICard[]
— массив карточек;isBlocked: boolean
— заблокирована ли страница, если сервер вернул ошибку;events: IEvents
— экземпляр классаEventEmitter
для инициализации событий при изменении данных;
Методы:
setItems(items: ICard[]): void
— записывает полученный от сервера массив данных;getItem(cardId: string): ICard
— возвращает отдельно взятую карточку;getItems(): ICard[]
— возвращает массив карточек;blockThePage(): void
— управляет свойством isBlocked и инициирует событие «page:block»;
Класс необходим для работы с данными пользователя.
Конструктор класса принимает инстант брокера событий.\
Поля:
protected data: IUserData
— объект с данными пользователя;protected events: IEvents
— экземпляр классаEventEmitter
для инициализации событий при изменении данных;public errors: Record<string, string>
— объект, хранящий сообщения ошибок, привязаных полям, где ключ — имя поля, а значение — текст ошибки;
Методы: геттеры и сеттеры для сохранения/получения данных пользователя, а также:
setError(data: {fieldName: string, errorMessage: string}): void
— добавляет ошибку в поле errors;validate(value: string): boolean
— валидация данных пользователя;clear(): void
— очистит данные пользователя;
Предназначен для работы с данными корзины. Реализует методы добавления товара, подсчёта количества и общей стоиомости
всех товаров в корзине.Конструктор класса принимает инстант брокера событий.\
Поля:
_products: ICard[]
— массив карточек;events: IEvents
— экземпляр классаEventEmitter
для инициализации событий при изменении данных;
Методы:
addItem(product: ICard): void
— добавит в корзину товар;removeItem(cardId: string): void
— удалит товар из корзины и вызовет событие изменения корзины;getTotalPrice(): number
— вернёт сумму всех товаров в корзине;isBasketProduct(id: string): boolean
— есть ли в корзине товар с таким id;isEmpty(): boolean
— возвращает true если корзина пуста;getCounter(): number
— вернёт количество товаров в корзине;get products(): ICard[]
— вернёт массив товаров, добавленных в корзину;clear(): void
— очищает корзину от добавленных товаров;
Отвечают за отображение и взаимодействие с пользователем.
Предназначен для работы с контейнером карточек на главной странице. Конструктор принимает экземпляр брокера событий.
Поля:
protected main: HTMLElement
- контейнер с карточками;protected basket: HTMLButtonElement
— кнопка корзины;protected basketCounter: HTMLElement
- элемент счётчика товаров;protected events: IEvents
— экземпляр EventEmitter;
Методы:
-
showProducts(items: HTMLElement[]): void {}
— вставит массив карточек на страницу; -
showBasketAmount(items: number): void
— отобразит в иконке корзины количество добавленных товаров;
Необходим для реализации модального окна.
Содержит методы для открытия и закрытия модального окна, вставки в него контента, устанавливает слушатели на клавишу ESC (закрытие мод.окна при нажатии), на оверлей и кнопку-крестик (закрытие при клике).\
Поля:
protected container: HTMLElement
— контейнер модального окна;content: HTMLElement;
— содержимое модального окна (контент);closeButton: HTMLButtonElement
— кнопка закрытия;submitButton?: HTMLButtonElement
— кнопка подтверждения действия;isOpened: boolean
— значение указывает, открыто ли модальное окно;events: IEvents
— экземпляр классаEventEmitter
для инициализации событий при изменении данных;
Методы:
open(element: HTMLElement): void
- вставляет контент в модальное окно и открывает его;close(): void
— закрывает модальное окно, очищая контент;setContent(content: HTMLElement): void
— вставляет элемент в контейнер модального окна;clear(): void
— очищает модальное окно от контента;
Предназначен для реализации отображения формы. При сабмите инициирует событие, в которое передаёт объект с данными из полей ввода. При изменении данных в поле ввода также инициирует событие изменения данных. Управляет активностью кнопки подтверждения.
Поля:
element: HTMLFormElement
— элемент формы;formName: string
— название формы;submitButton: HTMLButtonElement
— кнопка подтверждения;events: IEvents
— экземпляр брокера событий;inputs: NodeListOf<HTMLInputElement>
— инпуты формы;inputsErrors: NodeListOf<HTMLElement>
- элементы ошибок инпутов;paymentButtons?: Record<'card' | 'cash', HTMLButtonElement>
— радио-кнопки в форме с методом оплаты;
Методы:
toggleButtonState(): void
— включает/отключает кнопку подтверждения;clear(): void
— очищает поля ввода и деактивирует кнопку сохранения;showInputError(fieldName: string, errorMessage: string): void
— отображает текст ошибки;hideInputError(fieldName: string)
— скрывает текст ошибки под указанным полем ввода;get form(): HTMLFormElement
— геттер для получения элемента формы;
Наследует класс Component<T>
. Отвечает за отображение компонента карточки и его взаимодействие с пользователем.
Конструктор принимает объект данных товара ICard
, шаблон разметки и экземпляр брокера событий. Далее по шаблону производится поиск всех\ возможных элементов компонента. При наличии таковых в шаблоне, они заполняются данными из объекта ICard
, после чего на интерактивные элементы добавляются слушатели событий.
Поля:
_id: string
— идентификатор товара;public element: HTMLElement
— элемент карточки;description?: HTMLElement
— описание товара;image?: HTMLImageElement
— изображение товара;title?: HTMLElement
— название товара;category?: HTMLElement
— категория товара;price?: HTMLElement
— цена товара;addToBasketButton?: HTMLButtonElement
— кнопка «добавить в корзину»;removeFromBasket?: HTMLButtonElement
— кнопка удаления из корзины;public basketItemIndex?: HTMLElement
— элемент-индекс товара в корзине;
Методы:
fillElement(data: ICard): void
— заполняет данными элементы, имеющиеся у компонента товара;toggleButtonState(cardId): void
— отключает/включает кнопку «В корзину», если товар уже был добавлен в корзину;get id(): string
— возвращает _id карточки;
Предназначен для отображения модального окна корзины. Конструктор класса принимает инстант брокера событий и элемент разметки корзины.
Поля:
element: HTMLElement
— элемент открытой корзины;list: HTMLElement
— контейнер списка товаров;totalAmount: HTMLElement
— элемент разметки, отображающий общую стоимость товаров;orderButton: HTMLButtonElement
— кнопка «Оформить»;events: IEvents
— инстант брокера событий;
Методы:
displayProducts(products: Card[]): void
— отобразит товары корзины;displayTotalAmount(value: number): void
— отобразит сумму всех товаров корзины;buttonState(value: boolean): void
— управляет активностью кнопки «Оформить»;
Предназначен для отображения модального окна успешной покупки. Конструктор класса принимает инстант брокера событий и элемент-контейнер с разметкой содержимого для модального окна.
Поля:
_element: HTMLElement
— содержимое модального окна;orderDescription: HTMLElement
— элемент, отображающий сумму заказа;submitButton: HTMLButtonElement
— кнопка сабмита;events: IEvents
— инстант брокера событий;
Методы:
get element(): HTMLElement
— геттер поля_element
;showOrderAmount(amount: number): void
— устанавливает сумму заказа в полеorderDescription
;
Предназначен для реализации модального окна неудачной покупки. Расширяет конструктор родительского класса.
Класс предоставляет методы взаимодействия с бэкендом. Конструктор принимает инстант класса API.
Роль презентера будет выполнять код, распологающийся в файле ./src/index.ts
. Он описывает взаимодействие отображения и данных.
Сначала создаются все необходимые экземпляры классов, а затем настраивается работа событий.
События, генерируемые брокером событий вызывают обработчики, за счёт которых и осуществляется взаимодействие.\
Создаются классами моделей.
page:block
— содержит обработку ошибки в случае, если данные с сервера не были получены;basketModel:changed
- изменение количества товаров корзины;userData:changed
- изменение данных пользователя;
Создаются классами отображения.
-
modal:close
- закрытие модального окна; -
basket:open
- открытие корзины; -
basket:submit
- нажатие кнопки «оформить» в корзине; -
item:open
- нажатие на карточку; -
item:addToBasket
- нажатие на кнопку «в корзину» в модальном окне карточки; -
item:removeFromBasket
- удаление товара из корзины; -
payment-method:changed
- выбор метода оплаты; -
address:changed
- изменение данных в поле с адресом; -
modal-address:submit
- нажатие на кнопку «далее» в модальном окне с адресом доставки; -
email:changed
- изменение данных в поле с почтой; -
phone-number:changed
- изменение данных в поле с номером телефона; -
modal-contacts:submit
- нажатие на кнопку «далее» в модальном окне с почтой и телефоном; -
modal-success:submit
— нажатие на кнопку подтверждения в модальном окне успешной покупки.
Взаимодействия классов на примере добавления товара в корзину:
- Слой отображения. В классе Card навешивается слушатель события клика по кнопке «Добавить в корзину».
- Слой презентера. Клик вызывает обработчик события «item:selected» из слоя презентера. В обработчике события вызывается метод модели BasketModel.addItem(cardId).
- Слой данных. Модель записывает новые данные, после чего происходит событие «basket:changed».
- Слой презентера. В обработчике «basket:changed» вызывается функция слоя отображения Basket.render(products).
- Слой отображения. Метод render() отобразит карточки из массива BasketModel.products.