DEV Community

Cover image for Adding Entity Actions to Trees in Umbraco 14
Yari Mariën
Yari Mariën

Posted on

Adding Entity Actions to Trees in Umbraco 14

Introduction

In this post, we’ll build upon concepts from my previous post on Dynamic Routes and Views, where we explored how to create custom sections in Umbraco 14. Specifically, we’ll cover adding actions to trees using the Sidebar Context Menu.

Using Entity Actions

For this we can use the built-in Entity Actions. This feature automatically renders buttons for various actions directly within the sidebar context menu, providing a list of all possible actions relevant to the context.

Registering Actions

To register actions, you’ll need to create manifests of type ManifestEntityAction. When defining these, you must specify the forEntityTypes property, which determines which actions are available for each item type.

You can also define an api property, which specifies the logic that runs when the action is triggered.

manifest.ts:

const folderEntityActionManifest: Array<ManifestEntityAction> = [
    {
        type: 'entityAction',
        alias: 'deleteFolder',
        name: 'Delete Folder',
        kind: 'default',
        api: () => import('./menu-actions/folder/delete-folder-action.api.ts'),
        forEntityTypes: [FORMX_ENTITY_FOLDER_ALIAS],
        meta: {
            icon: 'icon-trash',
            label: 'Delete'
        },
    }
]
Enter fullscreen mode Exit fullscreen mode

Rendering Actions

Actions can be rendered by adding a umb-entity-actions-bundle element in the action slot of a uui-menu-item. The entityType needs to match the one defined in the manifest, and the unique property represents the unique identifier of the item.

menu-items.ts:

<uui-menu-item>
    <umb-entity-actions-bundle
                            slot="actions"
                            .entityType=${element.type === 0 ? FORMX_ENTITY_FORM_ALIAS : FORMX_ENTITY_FOLDER_ALIAS}
                            .unique=${element.id}>
    </umb-entity-actions-bundle>
</uui-menu-item>
Enter fullscreen mode Exit fullscreen mode

Action Logic

The functionality of an action is implemented in its corresponding api class, where you override the execute method. You can access the unique property of the selected menu item through this.args.unique.

On the end of the action logic we can dispatch an event to notify other components using actionEventContext.dispatchEvent(event). We can use this later to handle the output of the action.

In this case we can dispatch an UmbRequestReloadStructureForEntityEvent with entityType “FORMX”, because it doesn’t matter if the deleted item is a form or folder, the whole tree should be updated.

delete-folder-action.api.ts:

export class DeleteFolder extends UmbEntityActionBase<ManifestEntityActionDefaultKind> {
    override async execute() {
        // Use ModalManager to show a delete confirmation dialog
        console.log('Delete folder with id:', this.args.unique);

        const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);

        if(this.args.unique == null)
            throw new Error('Unique is required to delete folder');

        let id = parseInt(this.args.unique);

        const modal = modalManager.open(this, FORMX_DELETE_FOLDER_MODAL, {
            data: {
                id: id
            },
        });

        modal.onSubmit().then((data) => {
            console.log(`Response from modal: ${data}`);

            this.getContext(UMB_ACTION_EVENT_CONTEXT)
            .then((actionEventContext) => {
                const event = new UmbRequestReloadStructureForEntityEvent({
                    unique: this.args.unique,
                    entityType: "FORMX",
                });

                        //Dispatch an event
                actionEventContext.dispatchEvent(event);  
            })
        })
            .catch(() => {
            });
    }
}
export default DeleteFolder ;

Enter fullscreen mode Exit fullscreen mode

Handling the Action Result

After the action is completed and the sidebar context menu is closed, the tree will not automatically refresh. However, we can listen for events being dispatched. So in this case we will listen for aUmbRequestReloadStructureForEntityEvent with entityType “FORMX”.

When the event is dispatched, fetch the tree items again and rerender the structure. Using the ?showChildren attribute, you can maintain the open state of tree items after the refresh.

constructor() {
    super();
    this.fetchTree(); // Start fetching on component load

    this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (instance) => {
    this.#actionEventContext = instance;

        this.#actionEventContext?.removeEventListener(
        UmbRequestReloadStructureForEntityEvent.TYPE,
        this.#onReloadStructureRequest as unknown as EventListener,
    );

    this.#actionEventContext.addEventListener(
        UmbRequestReloadStructureForEntityEvent.TYPE,
        this.#onReloadStructureRequest as unknown as EventListener,
    );
});
}

#onReloadStructureRequest = async (event: UmbRequestReloadStructureForEntityEvent) => {
    if(event.getEntityType() == "FORMX"){
            //Handle rerender tree
        this.fetchTree();
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, I added an "Add" action, which will result in the following:

Example result

Top comments (0)