使用OOP在Typescript中创建UI



我的目标是创建一个纯类型脚本的Inventory应用程序。";应用程序";class是入门级。具体来说,我创建了两个实例来初始化ProductView和CategoryView。我这样做是因为我需要根据点击的菜单生成表(通过CSS样式显示/隐藏(。例如,如果用户单击"产品"菜单,我应该显示表和模态的产品。当我点击每个菜单时,我都会出现这样的错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'initUI')

Uncaught TypeError: Cannot read properties of undefined (reading 'initCategoryUI')

Github存储库

import { selectedMenu, productMenu, categoryMenu } from "./dom";
import { ProductView } from "./productview";
import { CategoryView } from "./categoryview";
import { Types } from "./entity";
import "./assets/scss/style.scss";
import Entity from "./entity";
class App {
private _productView: ProductView;
private _categoryView: CategoryView;
constructor() {
this._productView = new ProductView(new Entity<IProduct>(Types.IProduct));
this._categoryView = new CategoryView(
new Entity<ICategory>(Types.ICategory)
);
console.log("check ::", this._categoryView);
selectedMenu.forEach((el: HTMLAnchorElement) =>
el.addEventListener("click", this._selectedMenuHandler)
);
this._init();
}
private _selectedMenuHandler(): void {
if (this instanceof Element) {
if (this.classList.contains("menu__products")) {
this.classList.add("menu__selected");
categoryMenu?.classList.remove("menu__selected");
console.log("what happend in ::", this._productView); //undefined
this._productView.initUI();
} else if (this.classList.contains("menu__categories")) {
this.classList.add("menu__selected");
productMenu?.classList.remove("menu__selected");
console.log("what happend in ::", this._categoryView); //undefined
this._categoryView.initCategoryUI();
}
}
}
private _init() {
this._productView.initUI();
}
}
const app = new App();

import { modalPopup } from "./dom";
export class View {
private _span: HTMLElement;
constructor() {
this._span = document.querySelector<HTMLElement>(".close")!;
this._span.addEventListener("click", this._closeModal);
document.addEventListener("click", (e: Event) =>
this._closeModalWindowClicked(e)
);
}
protected _closeModalWindowClicked(e: Event) {
if (e.target === modalPopup) modalPopup.style.display = "none";
}
protected _openModal() {
modalPopup.style.display = "block";
}
protected _closeModal() {
modalPopup.style.display = "none";
}
}

import {
btn,
tableThead,
tableBody,
btnSubmit,
inputTitle,
categoryElement,
inputQuantity,
modalContentCategory,
modalContentProduct,
modalHeader,
} from "./dom";
import { Product } from "./product";
import Entity from "./entity";
import { View } from "./view";
btn?.classList.add("btn-product");
export class ProductView extends View {
private _categoryValue: string = "";
private _productInventory: Entity<IProduct>;
constructor(inventory: Entity<IProduct>) {
super();
console.log("create Product::", this);
this._productInventory = inventory;
btn?.addEventListener("click", this._openModal);
btnSubmit?.addEventListener("click", this._addButtonHandler.bind(this));
categoryElement?.addEventListener("change", this._selectCategoryHandler);
}
public initUI() {
this._createModal();
this._createHeaderTable();
this._renderTable();
}
private _createModal() {
modalContentCategory?.classList.add("hidden");
modalContentProduct?.classList.remove("hidden");
modalHeader!.innerHTML = "Add Product";
}
private _createHeaderTable(): void {
if (tableThead) {
tableThead.innerHTML = `<th>#</th>
<th>Title</th>
<th>Quantity</th>
<th>Category</th>
<th></th>`;
}
}
private _tableUIBody(item: Product, id: number) {
return `<tr class="table__row ${++id % 2 != 0 ? "odd" : ""}">
<td>${id}</td>
<td>${item.title}</td>
<td>${item.quantity}</td>
<td>${item.category}</td>
<td><button data-id="${item.id}" class="btn-delete" >
<i class="ph-trash-bold "></i>
<span>Delete</span>
</button> <button data-id="${item.id}" class="btn-edit">
<i class="ph-pencil-simple-bold"></i>
<span>Edit</span>
</button></td>
</tr>`;
}
private _selectCategoryHandler(e: Event) {
const select = document.querySelector<HTMLSelectElement>(
".form__select-category"
);
this._categoryValue = select?.options[select?.selectedIndex].value!;
}
private _addButtonHandler() {
const newProduct = new Product(
inputTitle?.value!,
this._categoryValue,
Number.parseInt(inputQuantity?.value!)
);
this._productInventory?.add(newProduct);
this._renderTable();
this._closeModal();
}
private _renderTable(): void {
tableBody!.innerText = "";
const categories = this._productInventory.storage;
const allCategory = categories.map((category: ICategory, index) => {
return this._tableUIBody(<Product>category, index);
});
tableBody!.innerHTML += allCategory.join("");
}
}

import {
tableBody,
tableThead,
inputTitle,
btn,
btnSubmit,
modalContentCategory,
modalContentProduct,
modalHeader,
} from "./dom";
import Entity, { Types } from "./entity";
import { Category } from "./category";
import { View } from "./view";
export class CategoryView extends View {
private _categoryInventory: Entity<ICategory>;
constructor(categoryInventory: Entity<ICategory>) {
super();
console.log("create Category::", this);
this._categoryInventory = categoryInventory;
btn?.addEventListener("click", this._openModal);
btnSubmit?.addEventListener("click", this._addButtonHandler);
}
initCategoryUI() {
this._createModal();
this._createHeaderTable();
this._renderTable();
}
private _createModal() {
modalContentProduct?.classList.add("hidden");
modalContentCategory?.classList.remove("hidden");
modalHeader!.innerHTML = "Add Category";
}
private _createHeaderTable(): void {
if (tableThead) {
tableThead.innerHTML = `<th>#</th>
<th>Title</th>
<th></th>`;
}
}
_addButtonHandler() {
const newCategory = new Category(inputTitle?.value!);
this._categoryInventory.add(newCategory);
this._renderTable();
this._closeModal();
}
private _renderTable(): void {
tableBody!.innerText = "";
const categories = this._categoryInventory.storage;
const allCategory = categories.map((category: ICategory, index) => {
return this._tableUIBody(<Category>category, index);
});
tableBody!.innerHTML += allCategory.join("");
}
private _tableUIBody(item: Category, id: number) {
return `<tr class="table__row ${++id % 2 != 0 ? "odd" : ""}">
<td>${id}</td>
<td>${item.title}</td>
<td><button data-id="${item.id}" class="btn-delete" >
<i class="ph-trash-bold"></i>
</button> <button data-id="${item.id}" class="btn-edit">
<i class="ph-pencil-simple-bold"></i>
</button></td>
</tr>`;
}
}
el.addEventListener("click", () => this._selectedMenuHandler())

尝试使用箭头函数,该函数可以保留封闭词汇上下文的this

最新更新