因此,在我的应用程序中,我发现自己有许多不同的菜单,需要在应用程序的其他角落切换或显示/隐藏组件。
它可以类似于侧边栏组件中的菜单选择,显示/隐藏某个组件段,该组件段可能包含也可能不包含嵌套子菜单,该子菜单根据所选项目再次显示/隐藏某些其他组件。
我一直在使用的方法可能很麻烦、不必要的复杂、不好的做法或者根本就是错误的。但它有效。
既然它有效,我现在已经在很多项目中使用它了。
它的工作方式确实意味着我必须手动处理取消订阅,当我在考虑如何使用
RxJs
管道运算符来完成此操作时,我意识到我从未真正见过其他人如何解决菜单逻辑。我猜主要是因为我还没有真正看过。
我通常做的是构建一个菜单服务,其中包含应用程序的不同部分可以订阅的许多菜单。它是一个布尔对象,如果为 true,则显示某个关联组件,如果为 false,则使用一些
*ngIf
条件隐藏该组件。
icon_menu_items = {
section_1: true,
section_2: false,
section_3: false,
section_4: false,
}
icon_menu = new BehaviorSubject<any>(this.icon_menu_items);
icon_menu_cast = this.icon_menu.asObservable();
在同一个菜单服务中,我有一个可以从应用程序中的其他地方、其他组件调用的功能:
toggle_simple_wind_climate_menu(_type: keyof IconMenuItemsInterface) {
(Object.keys(this.icon_menu_items) as (keyof typeof this.icon_menu_items)[])
.forEach((key, index) => {
this.icon_menu_items[key] = false;
});
this.icon_menu_items[_type] = true;
this.icon_menu.next(this.icon_menu_items);
}
该函数基本上只是切换选择了哪个部分的布尔状态,然后触发
next()
来更新需要显示/隐藏的组件的状态。
大多数网站都可以有多个菜单选择,也可以有嵌套菜单选择。 在这些菜单和嵌套子菜单中选择项目将显示 Web 应用程序的不同部分,但我不清楚其他网站如何实现此类功能。
处理显示/隐藏或添加/删除不同部分、表单、表格、卡片等的逻辑,按照我的方式,随着切换显示/隐藏逻辑的位置数量的增长,我最终会得到相当大的菜单服务.
所以我的问题/要求是。
人们通常如何在中型到大型 Web 应用程序中处理 Angular 中的菜单逻辑?
人们基本上如何在 Angular Web 应用程序中处理多层显示/隐藏逻辑。
尝试这种方法:将菜单(在逻辑级别上)表示为树。
export type MenuItem = {
title: string;
id: string;
children: MenuItem[];
isExpanded: boolean;
isSelected: boolean;
hasExpandedChildren: boolean;
hasSelectedChildren: boolean;
}
export const selectedItemsIds = signal<Set<string>>(new Set());
export const expandedItemsIds = signal<Set<string>>(new Set());
export const MENU: MenuItem[] = [
{
title: 'Infrastructure',
id: 'Infrastructure',
children: [
{
title: 'Devices',
id: 'Devices',
children: [],
isExpanded: false,
isSelected: false,
hasExpandedChildren: false,
hasSelectedChildren: false
},
{
title: 'Models',
id: 'Models',
children: [],
isExpanded: false,
isSelected: false,
hasExpandedChildren: false,
hasSelectedChildren: false
}
],
isExpanded: false,
isSelected: false,
hasExpandedChildren: false,
hasSelectedChildren: false
}
] as const;
export const currentMenu = computed(() => {
const currentMenu = [];
const selectedIds = selectedItemsIds();
const expandedIds = expandedItemsIds();
for (const item of MENU) {
currentMenu.push(getCurrentItem(item, selectedIds, expandedIds));
}
});
function getCurrentItem(item: MenuItem, selectedItemsIds: Set<string>, expandedItemsIds: Set<string>): MenuItem {
return {
...item,
isExpanded: expandedItemsIds.has(item.id),
isSelected: selectedItemsIds.has(item.id),
hasSelectedChildren: itemHasSelectedChildren(item, selectedItemsIds),
hasExpandedChildren: itemHasExpandedChildren(item, expandedItemsIds),
children: item.children.map(child => getCurrentItem(child, selectedItemsIds, expandedItemsIds)),
}
}
function itemHasSelectedChildren(item: MenuItem, selectedItemsIds: Set<string>): boolean {
if (!item.children.length) {
return false;
}
return item.children.some(i => selectedItemsIds.has(i.id) || itemHasSelectedChildren(i, selectedItemsIds));
}
function itemHasExpandedChildren(item: MenuItem, expandedItemsIds: Set<string>): boolean {
if (!item.children.length) {
return false;
}
return item.children.some(i => expandedItemsIds.has(i.id) || itemHasExpandedChildren(i, expandedItemsIds));
}
然后您可以通过修改这些设置来控制选择或展开哪些项目:
export function toggleItemSelected(id: string) {
const selectedIds = selectedItemsIds();
if (selectedIds.has(id)) {
selectedIds.delete(id);
} else {
selectedIds.add(id);
}
selectedItemsIds.set(new Set(selectedIds)); // new Set() is needed to trigger Signal update
}
export function toggleItemExpanded(id: string) {
const expandedIds = expandedItemsIds();
if (expandedIds.has(id)) {
expandedIds.delete(id);
} else {
expandedIds.add(id);
}
expandedItemsIds.set(new Set(expandedIds)); // new Set() is needed to trigger Signal update
}
并在模板中使用
currentMenu()
。