Logo

dev-resources.site

for different kinds of informations.

Building Custom Trees in Umbraco 14 using menus!

Published at
9/25/2024
Categories
umbraco
opensource
webcomponents
typescript
Author
yinzy00
Author
7 person written this
yinzy00
open
Building Custom Trees in Umbraco 14 using menus!

Table of Contents

  1. Some Thoughts
  2. Building Custom Menu Item Components
  3. Manifests
  4. 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.

File structure Umbraco document section

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>
Enter fullscreen mode Exit fullscreen mode

Image of result example code

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;
    }
}

Enter fullscreen mode Exit fullscreen mode

The result will be a menu that looks like the following:

Result of what our custom element would look like.

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";
Enter fullscreen mode Exit fullscreen mode

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"
            }
        ]
    }
];
Enter fullscreen mode Exit fullscreen mode

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'
    }
];
Enter fullscreen mode Exit fullscreen mode

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')
    }
];
Enter fullscreen mode Exit fullscreen mode

Final Result

Once the SectionSidebarApp is linked to our custom section, the tree will be rendered with the specified label and custom elements.

Example of what our custom section with our newly created custom tree would look like.

umbraco Article's
30 articles in total
Favicon
It doesn’t have to be complicated to build a custom Umbraco property editor
Favicon
please create a post for custom section's tree
Favicon
Umbraco package of the month: xStatic
Favicon
Umbraco and Bellissima: Swagger, Tokens, Entry Points
Favicon
Catching the Bus? How a Service Bus and Azure Functions Can Help Your Integration Reliability
Favicon
Driving Umbraco's dictionary to the edge (of your system)
Favicon
Managing Stale Carts in Umbraco Commerce
Favicon
Quick fix for IPublishedSnapshotAccessor issues when upgrading to Umbraco 15
Favicon
Umbraco package of the month: Auto dictionaries
Favicon
Umbraco package of the month: Uskinned
Favicon
Learning TDD by doing: Tagging members in Umbraco's Rich Text Editor
Favicon
Learning TDD by doing: Dealing with Umbraco's published content
Favicon
My Highlights from the Umbraco US Festival 2024
Favicon
Mastering Authorization in Umbraco 14/15: Real-World Management API Challenges and Solutions
Favicon
Adding Entity Actions to Trees in Umbraco 14
Favicon
Umbraco Forms 12+, Custom Field Type
Favicon
Make your own Umbraco Starter Kit without making a Starter Kit
Favicon
Adding Dynamic Routes to Umbraco 14 Sections
Favicon
Building Custom Trees in Umbraco 14 using menus!
Favicon
Doing custom sections in Umbraco 14
Favicon
Facetted search with Examine - Umbraco 13
Favicon
Server-side registering of Package Manifest in Umbraco 14
Favicon
How to skip login screens during development for Umbraco 13 Users and Members
Favicon
My journey through programmatically creating Umbraco `stuff`
Favicon
Deploying TypeScript Type Definitions for Umbraco v14 Packages
Favicon
Creating an Umbraco Backoffice Accessor for Conditional Page Rendering
Favicon
Create an API in Umbraco in 5 Minutes: A Quick Guide for Developers
Favicon
How to Create a New Umbraco Project: A Step-by-Step Guide
Favicon
Programmatically add a new 'Accepted file extension'
Favicon
Umbraco CodeGarden24

Featured ones: