Как реализовать компонентный подход к организации кода на чистом JavaScript?
Здравствуйте уважаемые посетители stackoverflow!
Имеется ряд вопросом, но о них чуть ниже. Заранее извиняюсь за большой текст, патался максимально подробно расписать, чтобы было понятно.
У меня есть страничка(учебная), на которой Список, Корзина и Карточки с продуктами.

Пытаюсь сделать все это дело с помощью компонентов. Создал классы, которые отвечают за определенную сущность на странице. Класс для списка.
class ProductTypes {
constructor(parentDOM, productsArray, activeType, ulClassName) {
this.parentDOM = parentDOM;
this.productsArray = productsArray;
this.activeType = activeType;
this.ulClassName = ulClassName;
}
// создание ul
createList() {
let ul = document.createElement("ul");
ul.classList.add(this.ulClassName);
this.parentDOM.appendChild(ul);
}
// создание li и добавление в ul
createListItems() {
let parent = document.querySelector("." + this.ulClassName);
this.productsArray.map(item => {
const { attribute, name } = item;
let li = document.createElement("li");
if (attribute === this.activeType) {
li.setAttribute("data-name", attribute);
li.setAttribute("data-default", this.activeType);
li.textContent = name;
li.classList.add("active-product");
parent.appendChild(li);
} else {
li.textContent = name;
li.setAttribute("data-name", attribute);
parent.appendChild(li);
}
});
}
changeProductType() {
let parent = document.querySelector("." + this.ulClassName);
parent.addEventListener("click", async event => {
if (event.target.tagName === "LI") {
const type = event.target.getAttribute("data-name");
this.switchActiveClass(event.target, "active-product", parent);
app.getProductsItem(type); // функция из APP ?
}
});
}
switchActiveClass(element, className, parent) {
parent.childNodes.forEach(el => {
el.classList.remove(className);
el.removeAttribute("class");
if (el.getAttribute("data-default")) {
el.removeAttribute("data-default");
}
});
element.classList.add(className);
}
render() {
this.createList(); // ?
this.createListItems(); // ?
this.changeProductType(); // ?
}
}
const productTypesList = new ProductTypes(
ROOT_PRODUCT_TYPES,
productsType,
"sandwiches",
"menu-list"
);
productTypesList.render();
Класс для карточек с товарами:
class ProductItem {
constructor(name, description, category, image, price, type) {
this.name = name;
this.description = description;
this.category = category;
this.image = image;
this.price = price;
this.type = type;
}
render() {
const html = `
<div class="item-wrapper" data-name="${this.name}">
<div class="item-wrapper__img"><img src="data${this.image}" alt="${this.category}"></div>
<div class="item-wrapper__name">${this.name}</div>
<div class="item-wrapper__description" data-atr="${this.type}">${this.description}</div>
<div class="item-wrapper__price">Цена: ${this.price} руб.</div>
<div class="item-wrapper__count">
<span>Количество</span>
<div class="item-count__button">
<button class="btn-count" data-atr="decrease">
<i class='fas fa-minus' data-atr='decrease'></i>
</button>
<span class="item-count__field">1</span>
<button class="btn-count" data-atr="increase">
<i class='fas fa-plus' data-atr='increase'></i>
</button>
</div>
<div class="item-wrapper__inbasket">
<button class="btn-style basket-btn-add" data-name="${this.name}" data-atr="inBasket">
В корзину
</button>
</div>
</div>
</div>`;
return html;
}
}
Для api запроса я создал так же отдельный класс:
class FetchApi {
constructor(url) {
this.url = url;
}
async fetchData(typeProduct) {
try {
let response = await fetch(this.url);
if (response.ok) {
let data = await response.json();
const products = data.menu.filter(product => {
if (typeProduct === product.category) return product;
});
return products;
}
} catch (error) {
console.log(error);
}
}
}
const request = new FetchApi(URL);
Далее чтобы компоненты 'ничего не знали друг о друге наприямую', я создал отдельный класс APP в котором у меня будет происходить вызов функция для рендера данных в корзине и данных с карточками товара(в зависимости выбранного типа из списка).
class App {
constructor() {}
// вызвать рендер продуктов
// вызвать рендер корзины, когда будет в нее что-то добавлено
async getProductsItem(productType) {
ROOT_RIGHT_SIDE.innerHTML = "";
const products = await request.fetchData(productType); // массив с продуктами
// console.log(products);
products.map(product => {
const { name, description, category, image, price, type } = product;
ROOT_RIGHT_SIDE.innerHTML += new ProductItem(
name,
description,
category,
image,
price,
type
).render();
});
}
}
const app = new App();
Вопросы такие:
- Я не знаю как правильно организовать вызов методов из APP, чтобы происходил ререндер карточек. Как это я сделал сейчас, в классе ProductTypes у меня есть метод changeProductType(), в котором вызывается метод класса app.getProductsItem(type). Там у меня идет вызов метода из класса FetchApi, который возврвщает массив с продуктами определенных типов.
- В классе App в методе getProductsItem(), по полученному массиву я прохожусь и добавляю на страницу экземплары класса ProductItem (получается - одна итерация - один экземплар). - Я так же не знаю, правильно ли так делать )
- Не знаю где и как объявить методы обработчика клика для кнопок +, -, в корзину. Добавлял, как отдельный метод соответственно в классе через addeventlistener, но так как я создаю контент через шаблон, ошибка что карточки с нужным враппером еще нет.
- Не нравится(но я и не знаю, как правильно), как я вызвал функции создания ul и li в классе ProductTypes, вызвал я их в методе рендер.
Благодарю за внимание и еще раз извиняюсь за такой большой текст :)