This content originally appeared on DEV Community and was authored by Yari Mariën
Table of Contents
- Some Thoughts
-
Building Custom Menu Item Components
- Rendering Menu Items with Umbraco's UI Menu Item Component
- Example: Fetching and Rendering a Dynamic Tree
-
Manifests
- Defining the Tree Space with
ManifestSectionSidebarApp
- Defining the Menu with
ManifestMenu
- Linking the Custom Elements with
ManifestMenuItem
- Defining the Tree Space with
- Final Result
Some Thoughts
There are several methods to create custom trees. One approach is using a full Tree structure, which involves numerous components like ManifestTree
, ManifestTreeItem
, ManifestTreeStore
, DataSource
, Repository
, and many more files.
However, I find this approach overly complex and unnecessary for small, simple trees.
For a basic tree with, I prefer using a menu with custom-rendered menu items using a custom Lit element, as I will demonstrate in this example.
Building Custom Menu Item Components
Rendering Menu Items with Umbraco's UI Menu Item Component
To render your menu items in Umbraco, you can make use of the powerful Umbraco UI Menu Item component. This component allows you to easily create nested menu structures with just a few lines of code.
Example:
<uui-menu-item label="Menu Item 1" has-children>
<uui-menu-item label="Nested Menu Item 1"></uui-menu-item>
<uui-menu-item label="Nested Menu Item 2"></uui-menu-item>
</uui-menu-item>
Example: Fetching and Rendering a Dynamic Tree
In this example, the entire tree is fetched when the component is rendered. Once the data is loaded, we put the items in a @state()
. This will trigger a re-render of the tree because it is a reactive property.
With the retrieved data, I dynamically render nested menu items, each with relevant icons. The core functionality of the tree such as opening, closing, and indenting nodes comes from the menu item component itself.
To display the caret icon indicating nested items, you can set the has-children
attribute dynamically like this: ?has-children=${bool}
.
menu-items.ts:
import { UmbMenuItemElement } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { html, TemplateResult } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { FormXTreeItemResponseModel, FormXTreeResource } from '../../../api';
const elementName = 'formx-menu-item';
@customElement(elementName)
class FormXMenuItems extends UmbLitElement implements UmbMenuItemElement {
@state()
private _items: FormXTreeItemResponseModel[] = []; // Store fetched items
@state()
private _loading: boolean = true; // Track loading state
@state()
private _error: string | null = null; // Track any errors
constructor() {
super();
this.fetchInitialItems(); // Start fetching on component load
}
// Fetch initial items
async fetchInitialItems() {
try {
this._loading = true;
this._items = ((await FormXTreeResource.getFormxApiV1Tree()).items); // Fetch root-level items
} catch (e) {
this._error = 'Error fetching items';
} finally {
this._loading = false;
}
}
// Render items
renderItems(items: FormXTreeItemResponseModel[]): TemplateResult {
return html`
${items.map(element => html`
<uui-menu-item label="${element.name}" ?has-children=${element.hasChildren}>
${element.type === 1
? html`<uui-icon slot="icon" name="icon-folder"></uui-icon>`
: html`<uui-icon slot="icon" name="icon-autofill"></uui-icon>`}
${element.hasChildren ? this.renderItems(element.children) : ''}
</uui-menu-item>
`)}
`;
}
// Main render function
render() {
if (this._loading) {
return html`<uui-loader></uui-loader>`;
}
if (this._error) {
return html`<uui-menu-item active disabled label="Could not load form tree!">
</uui-menu-item>`;
}
// Render items if loading is done and no error occurred
return html`${this.renderItems(this._items)}`;
}
}
export { FormXMenuItems as element };
declare global {
interface HTMLElementTagNameMap {
[elementName]: FormXMenuItems;
}
}
The result will be a menu that looks like the following:
Manifests
For this setup, you'll need three key manifests that work together to render a tree using a custom menu.
Let’s start by defining a reusable alias:
const FORMX_MENU_ALIAS = "Our.Umbraco.FormX.menu";
Defining the Tree Space with ManifestSectionSidebarApp
The first manifest defines the space where your tree will appear. This is achieved by creating a ManifestSectionSidebarApp
. We can connect the menu to the sidebar using the menu alias.
const sidebarApps: Array<ManifestSectionSidebarApp> = [
{
type: 'sectionSidebarApp',
kind: 'menuWithEntityActions',
alias: 'Our.Umbraco.FormX.sidebar',
name: 'FormX Sidebar App',
meta: {
label: "Forms",
menu: FORMX_MENU_ALIAS
},
conditions: [
{
alias: "Umb.Condition.SectionAlias",
match: "Our.Umbraco.FormX.section"
}
]
}
];
Defining the Menu with ManifestMenu
Next, you’ll define the actual menu where your items will be rendered. For this we can use a ManifestMenu
.
const menuManifest: Array<ManifestMenu> = [
{
type: 'menu',
alias: FORMX_MENU_ALIAS,
name: 'FormX Menu'
}
];
Linking the Custom Elements with ManifestMenuItem
Lastly, we need to link our custom elements that fetch and render your menu items. This is where the ManifestMenuItem
comes in.
const menuItemManifest: Array<ManifestMenuItem> = [
{
type: 'menuItem',
alias: 'formx-menu-items',
name: 'FormX Menu Items',
meta: {
label: 'FormX',
menus: [FORMX_MENU_ALIAS]
},
element: () => import('./menu-items.ts')
}
];
Final Result
Once the SectionSidebarApp
is linked to our custom section, the tree will be rendered with the specified label and custom elements.
This content originally appeared on DEV Community and was authored by Yari Mariën
Yari Mariën | Sciencx (2024-09-25T15:01:51+00:00) Building Custom Trees in Umbraco 14 using menus!. Retrieved from https://www.scien.cx/2024/09/25/building-custom-trees-in-umbraco-14-using-menus/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.