我已经实现了从类别或子类别添加子类别的功能。但是视图没有更新,它需要重新加载来更新视图。 在这里,我以嵌套方式显示我的类别数据,并通过递归组件完成。 我的类别组件:
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { CategoryService } from 'src/app/services/category.service';
import { concatMap } from 'rxjs/operators';
@Component({
selector: 'app-categories',
templateUrl: './categories.component.html',
styleUrls: ['./categories.component.css'],
})
export class CategoriesComponent implements OnInit {
categories: any = [];
categoryTree: any = [];
categoryMap: any = [];
categoryIdToDelete: any;
selectedParent: any = null;
addCategoryForm!: FormGroup;
saveButtonText: string = 'Save';
// toggle title
addingSubcategory: boolean = false;
toggleAddingSubcategory(parentCategory: any) {
console.log(parentCategory);
this.addingSubcategory = true;
this.selectedParent = parentCategory;
this.addCategoryForm.controls['parent'].setValue(parentCategory);
console.log(this.addCategoryForm.value);
}
toggleAddingCategory() {
this.addingSubcategory = false;
}
// set active tab dynamically
activeTabIndex: number = 0;
setActiveTab(index: number) {
this.activeTabIndex = index;
}
constructor(
private categoryService: CategoryService,
private cdr: ChangeDetectorRef
) {}
ngOnInit(): void {
// dummy object
// {
// "name": "Keto Child 1.1.1",
// "shortText": "",
// "longText": "",
// "media": [],
// "parent": "6434fb5e0dccedc460b6575a",
// "subCategories": []
// }
this.addCategoryForm = new FormGroup({
name: new FormControl(''),
shortText: new FormControl(''),
longText: new FormControl(''),
media: new FormControl([]),
parent: new FormControl(''),
subCategories: new FormControl([]),
});
// to show categories
this.categoryService.getCategories().subscribe((res: any) => {
this.categories = res.result;
console.log(this.categories);
this.buildCategoryTree();
});
}
// this function is used to make category tree in nested way
private buildCategoryTree() {
//to map the categories by id with updated categories
const categoryMap = [];
for (const category of this.categories) {
categoryMap[category.id] = { ...category, subCategories: [] };
}
//to build the category tree with updated categories
const categoryTree = [];
for (const category of this.categories) {
if (category.parent === null) {
categoryTree.push(categoryMap[category.id]);
} else {
const parentCategory = categoryMap[category.parent];
if (parentCategory) {
parentCategory.subCategories.push(categoryMap[category.id]);
}
}
}
this.categoryTree = categoryTree;
console.log(categoryTree);
}
addCategories() {
this.saveButtonText = 'Saving';
const formData = this.addCategoryForm.value;
this.categoryService
.addCategories(formData, formData.parent)
.pipe(
concatMap((response) => {
return this.categoryService.getCategories();
})
)
.subscribe((res: any) => {
// find the parent category/subcategory by its id
const parent = formData.parent
? this.findSubcategoryById(formData.parent, this.categories)
: this.categories.find((c: any) => c.id === formData.parent);
if (parent) {
// push the new category/subcategory to the parent's subCategories array
if (!parent.subCategories) {
parent.subCategories = [];
}
parent.subCategories.push(res.newCategory);
// re-build the category tree with the updated categories data
this.buildCategoryTree();
this.cdr.detectChanges();
}
this.saveButtonText = 'Saved';
this.addCategoryForm.reset();
});
}
addSubcategory(parentSubcategoryId: any) {
this.addCategoryForm.controls['parent'].setValue(parentSubcategoryId);
const formData = this.addCategoryForm.value;
this.categoryService
.addCategories(formData, formData.parent)
.pipe(
concatMap((response) => {
return this.categoryService.getCategories();
})
) // set the parent subcategory id
.subscribe((res: any) => {
// find the parent subcategory by its id
const parentSubcategory = this.findSubcategoryById(
parentSubcategoryId,
this.categories
);
if (parentSubcategory) {
// push the new subcategory to the parent subcategory's subCategories array
if (!parentSubcategory.subCategories) {
parentSubcategory.subCategories = [];
}
parentSubcategory.subCategories.push(res.newCategory);
// re-build the category tree with the updated categories data
this.buildCategoryTree();
this.cdr.detectChanges();
}
this.saveButtonText = 'Saved';
this.addCategoryForm.reset();
});
}
// recursive function to find a subcategory by its id
findSubcategoryById(id: string, categories: any[]): any {
for (const category of categories) {
if (category.id === id) {
return category;
} else if (category.subCategories) {
const subcategory = this.findSubcategoryById(
id,
category.subCategories
);
if (subcategory) {
return subcategory;
}
}
}
return null;
}
}
你可以在这里看到,我已经尝试了两种方法来使用 ChangeDetectorRef 更新视图,并使用单独的函数使用更新的类别数据重建类别树。但它不起作用。谁能帮我克服这种情况?服务和递归组件已在下面给出以理解概念。
递归组件:
import { Component, Input, EventEmitter, Output, OnInit } from '@angular/core';
export class subCategories {
id: string = '';
name: string = '';
shortText: string = '';
longText: string = '';
parent: string = '';
media: Array<string>[] = [];
subCategories: subCategories[] = [];
}
@Component({
selector: 'app-recursive',
templateUrl: './recursive.component.html',
styleUrls: ['./recursive.component.css'],
})
export class RecursiveComponent implements OnInit {
@Input() subCategories: subCategories[] | undefined;
@Output() deleteSubCategoryEvent = new EventEmitter<any>();
@Output() addSubCategoryEvent = new EventEmitter<any>();
ngOnInit(): void {}
deleteSubcategory(subcategoryId: any) {
this.deleteSubCategoryEvent.emit(subcategoryId);
}
addSubcategory(parent: any) {
this.addSubCategoryEvent.emit(parent);
}
}
服务:
addCategories(categoryInfo: any, parentId: string | null): Observable<any> {
const newCategory = {
name: categoryInfo.name,
shortText: categoryInfo.shortText,
longText: categoryInfo.longText,
media: categoryInfo.media,
// set the parent id here
parent: parentId,
subCategories: [],
};
return this.http.post(
'https://pim-nest.vercel.app/api/v1/collections/category',
newCategory
);
}
我的看法,categories.html:
<div class="cms-body-content col-12">
<div class="cms-body-inner-content accordion expander">
<div class="expander-inner category" *ngFor="let category of categoryTree">
<div class="expander-header" [id]="'heading-' + category.id">
<div class="item btn-item">
<div class="inner-cont" *ngIf="category.subCategories.length > 0; else nosubCategories">
<span class="btn arrow collapsed" [attr.data-target]="'#collapse-' + category.id" data-toggle="collapse"
aria-expanded="false">{{ category.name }}</span>
</div>
<ng-template #nosubCategories>
<div class="inner-cont my-1">
{{ category.name }}
</div>
</ng-template>
<div class="more d-flex">
<div class="icon-hover" data-toggle="tooltip" data-placement="top">
<a class="icon-plus-circle fs20" data-target="#addCategoryModal" data-toggle="modal"
(click)="toggleAddingSubcategory(category.id)"></a>
</div>
<div class="dropdown">
<span class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i class="fal fa-ellipsis-v"></i>
</span>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" data-target="#editCategoryModal" data-toggle="modal">Edit</a>
<a class="dropdown-item" href="#" data-target="#deleteModal" data-toggle="modal"
(click)="categoryIdToDelete = category.id">Delete</a>
</div>
</div>
</div>
</div>
</div>
<div [id]="'collapse-' + category.id" class="collapse" [attr.aria-labelledby]="'heading-' + category.id">
<div class="expander-body">
<app-recursive [subCategories]="category.subCategories" (deleteSubCategoryEvent)="deleteSubCategory($event)"
(addSubCategoryEvent)="addSubcategory($event)"></app-recursive>
</div>
</div>
</div>
</div>
</div>
<!-- add category button(plus button) -->
<button data-target="#addCategoryModal" data-toggle="modal" type="button" class="btn btn-add cms-add-btn add-category"
(click)="toggleAddingCategory()"></button>
<!-- Add category modal -->
<div id="addCategoryModal" class="modal fade right add-category-modal" tabindex="-1">
<div class="modal-dialog">
<form class="modal-content ng-untouched ng-pristine ng-invalid" [formGroup]="addCategoryForm">
<div class="modal-header left-icon-tab">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<i class="icon-close"></i>
</button>
<ul class="modal-tab nav-tabs">
<li class="tab-item" [ngClass]="{ 'active': activeTabIndex === 0 }">
<a href="#newCategory" data-toggle="tab" (click)="setActiveTab(0)"> {{ addingSubcategory ? 'New subcategory'
: 'New category' }} </a>
</li>
<li class="tab-item" [ngClass]="{ 'active': activeTabIndex === 1 }">
<a href="#bulk" data-toggle="tab" (click)="setActiveTab(1)"> {{ addingSubcategory ? '' : 'Bulk Add' }} </a>
</li>
</ul>
</div>
<div class="modal-body tab-content">
<div class="tab-pane active" id="newCategory">
<div class="form-group">
<input type="text" class="form-control" formControlName="name" placeholder="Category name" />
</div>
<h5 class="brand-secondary pt-3 mb-3">Descriptions</h5>
<div class="form-group">
<label class="brand-secondary mb-0">Short text</label>
<p class="p-extra-small gray-darker mb-2">Short text is displayed under the category heading in a
site catalog</p>
<textarea class="form-control" formControlName="shortText" rows="3"></textarea>
</div>
<div class="form-group">
<label class="brand-secondary mb-0">Long text</label>
<p class="p-extra-small gray-darker mb-2">Long text is displayed in the product catalog as a
separate section after the title and short text</p>
<textarea class="form-control" formControlName="longText" rows="6"></textarea>
</div>
<div class="form-group">
<label class="brand-secondary mb-0">Media</label>
<div class="dragbox-outer">
<div class="dragBox">
<p class="drag-icon"></p>
<p class="drag-text p-medium-bold gray-dark">Drop files or click to select</p>
<input type="file" onchange="dragNdrop(event)" ondragover="drag()" ondrop="drop()" id="uploadFile"
class="uploadFile">
</div>
</div>
</div>
<div class="form-group" *ngIf="!addingSubcategory">
<h5 class="brand-secondary pt-3 mb-3">Subcategories</h5>
<p class="p-extra-small gray-darker mb-2">Enter subcategories separated by a line break. Use TAB button on
your keyboard to add another nesting level.</p>
<textarea class="form-control p-small ng-untouched ng-pristine ng-valid" rows="6">
</textarea>
</div>
</div>
<div class="tab-pane bulk" id="bulk">
<div class="form-group">
<p class="p-extra-small gray-darker mb-2">Enter categories separated by a line break. Use TAB button on
your keyboard to add another nesting level.</p>
<textarea class="form-control p-small" rows="25">
</textarea>
</div>
</div>
</div>
<div class="modal-footer">
<div class="bottom-bar">
<div class="bottom-bar-left">
<a type="button" (click)="addCategories()" class="btn btn-brand">{{saveButtonText}}</a>
<a data-dismiss="modal" class="btn btn-gray">Cancel</a>
</div>
</div>
</div>
</form>
</div>
</div>
和recursive.html:
<ul class="list">
<li class="item-border" *ngFor="let subCategory of subCategories">
<div class="expander-inner category" *ngIf="subCategory.subCategories.length > 0; else nosubCategories">
<div class="expander-header" [id]="'heading-' + subCategory.id">
<div class="item btn-item">
<div class="inner-cont">
<span class="btn arrow collapsed" [attr.data-target]="'#collapse-' + subCategory.id" data-toggle="collapse"
aria-expanded="false">{{ subCategory.name }}</span>
</div>
<div class="more d-flex">
<span class="icon-hover" data-toggle="tooltip" data-placement="top" title="New subcategory">
<i class="icon-plus-circle" data-target="#addCategoryModal" data-toggle="modal"
(click)="addSubcategory(subCategory.id)"></i>
</span>
<div class="dropdown">
<span class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i class="fal fa-ellipsis-v"></i>
</span>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" data-target="#editCategoryModal" data-toggle="modal">Edit</a>
<a class="dropdown-item" href="#" data-target="#deleteModal" data-toggle="modal"
(click)="deleteSubcategory(subCategory)">Delete</a>
</div>
</div>
</div>
</div>
</div>
<div [id]="'collapse-' + subCategory.id" class="collapse" [attr.aria-labelledby]="'heading-' + subCategory.id">
<app-recursive [subCategories]="subCategory.subCategories" (deleteSubCategoryEvent)="deleteSubcategory($event)"
(addSubCategoryEvent)="addSubcategory($event)"></app-recursive>
</div>
</div>
<ng-template #nosubCategories>
<li class="item">
<div class="inner-cont">
<a class="dropdown-item" href="#" data-target="#editCategoryModal" data-toggle="modal">{{subCategory.name}}</a>
</div>
<div class="more d-flex">
<span class="icon-hover" data-toggle="tooltip" data-placement="top" title="New subcategory">
<i class="icon-plus-circle" data-target="#addCategoryModal" data-toggle="modal"
(click)="addSubcategory(subCategory.id)"></i>
</span>
<div class="dropdown">
<span class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i class="fal fa-ellipsis-v"></i>
</span>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" data-target="#editCategoryModal" data-toggle="modal">Edit</a>
<a class="dropdown-item" href="#" data-target="#deleteModal" data-toggle="modal"
(click)="deleteSubcategory(subCategory)">Delete</a>
</div>
</div>
</div>
</li>
</ng-template>
</li>
</ul>