Get route parameter in custom dashboard umbLitElemement

I’m trying to set up a custom dashboard for my Umbraco 17 backend so I have the following manifest:

export const dashboardInstallerDetailsManifest: UmbExtensionManifest = {
    type: 'dashboard',
    alias: 'MyAccount.Dashboard.InstallerDetails',
    name: 'Installer Details Dashboard',
    meta: {
        label: 'Details',
        pathname: 'details/:accountNumber'
    },
    conditions: [{
        alias: Constants.SectionCondition,
        match: Constants.SectionAlias,
    }],
    element: () => import('../Elements/InstallerDetails'),
};

This works fine, but I’m unable to get the account number from the URL.

I have tried using onBeforeEnter and onAfterEnter like in this answer but my console.log never seems to be hit:

export class InstallerDetailsElement extends UmbLitElement implements BeforeEnterObserver {

    // I have tried with and without the implements BeforeEnterObserver

    @state()
    private _accountNumber?: string;

    constructor() {
        super();
    }

    // I have tried with and without the async and public before this method
    async onBeforeEnter(location: RouterLocation) {
        console.log('onBeforeEnter');
        this._accountNumber = location.params.accountNumber as string;
    }

    public onAfterEnter(
        location: RouterLocation,
        commands: PreventAndRedirectCommands,
        router: Router
    ): void {
        console.log('onAfterEnter');
        this._accountNumber = location.params.accountNumber as string;
    }

Does anyone know how to get the account number from the URL in the umbLitElement?

Hi @Pete0000

I asked AI and got this response, does this help?

The reason your onBeforeEnter/onAfterEnter never fire is that the dashboard element isn’t what the Vaadin router actually mounts as the route component, so those lifecycle hooks won’t get called on it. That approach works when your element is the routed component, but not for a dashboard.

The pattern the core uses is to give your dashboard element a umb-router-slot, define the parameterised route there, and read the value from the route’s setup callback. So your dashboard element becomes the host for the slot (no more onBeforeEnter/onAfterEnter or @state for the account number):

import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { html, customElement } from '@umbraco-cms/backoffice/external/lit';
import type { UmbRoute } from '@umbraco-cms/backoffice/router';

const elementName = 'installer-details-dashboard';

@customElement(elementName)
export class InstallerDetailsElement extends UmbLitElement {

    #routes: UmbRoute[] = [
        {
            path: 'details/:accountNumber',
            component: () => import('./InstallerDetailsView'),
            setup: (component, info) => {
                (component as any).accountNumber = info.match.params.accountNumber;
            },
        },
        {
            path: '',
            redirectTo: 'details/',
        },
        {
            path: `**`,
            component: async () =>
                (await import('@umbraco-cms/backoffice/router')).UmbRouteNotFoundElement,
        },
    ];

    render() {
        return html`<umb-router-slot .routes=${this.#routes}></umb-router-slot>`;
    }
}

export default InstallerDetailsElement;

declare global {
    interface HTMLElementTagNameMap {
        [elementName]: InstallerDetailsElement;
    }
}

Then your actual view just takes the account number as a property — the setup callback assigns it, so use @property (not @state) so it stays reactive:

import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';

const elementName = 'installer-details-view';

@customElement(elementName)
export class InstallerDetailsViewElement extends UmbLitElement {

    @property({ type: String })
    public accountNumber?: string;

    render() {
        return html`<h2>Account: ${this.accountNumber}</h2>`;
    }
}

export default InstallerDetailsViewElement;

declare global {
    interface HTMLElementTagNameMap {
        [elementName]: InstallerDetailsViewElement;
    }
}

A couple of things to note:

Keep the :accountNumber param on the internal route (in the slot), not on the dashboard manifest’s pathname — set meta.pathname to just details. The dashboard is really only the entry point, and the slot handles the param routing. Your manifest’s element keeps pointing at the dashboard element above.

info.match.params is the reliable way to get route params in the new backoffice — it’s what the docs use in the routing section and what the core does for workspace views.

There’s a decent write-up of the whole routing setup here if it helps: Routes | CMS | Umbraco Documentation

Hope that helps!

Justin

Thanks for the reply @justin-nevitech . I’ve finally got around to testing this and all it does is change the links for my dashboard but it doesn’t render the view

Ok, I found a decent tutorial on this:

and it seems that the routes only work when I do it for a section view - this works out better too as I wanted a sidebar tree to start with rather than just dashboard tabs