处理菜单逻辑订阅的最佳方法/实践?

问题描述 投票:0回答:1

因此,在我的应用程序中,我发现自己有许多不同的菜单,需要在应用程序的其他角落切换或显示/隐藏组件。

它可以类似于侧边栏组件中的菜单选择,显示/隐藏某个组件段,该组件段可能包含也可能不包含嵌套子菜单,该子菜单根据所选项目再次显示/隐藏某些其他组件。

我一直在使用的方法可能很麻烦、不必要的复杂、不好的做法或者根本就是错误的。但它有效。

既然它有效,我现在已经在很多项目中使用它了。

它的工作方式确实意味着我必须手动处理取消订阅,当我在考虑如何使用

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()
来更新需要显示/隐藏的组件的状态。

basic illustration

大多数网站都可以有多个菜单选择,也可以有嵌套菜单选择。 在这些菜单和嵌套子菜单中选择项目将显示 Web 应用程序的不同部分,但我不清楚其他网站如何实现此类功能。

处理显示/隐藏或添加/删除不同部分、表单、表格、卡片等的逻辑,按照我的方式,随着切换显示/隐藏逻辑的位置数量的增长,我最终会得到相当大的菜单服务.

所以我的问题/要求是。

人们通常如何在中型到大型 Web 应用程序中处理 Angular 中的菜单逻辑?

人们基本上如何在 Angular Web 应用程序中处理多层显示/隐藏逻辑。

angular rxjs
1个回答
0
投票

尝试这种方法:将菜单(在逻辑级别上)表示为树。


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()

© www.soinside.com 2019 - 2024. All rights reserved.