Custom menus in the new Backoffice

I am trying to create a new section in the Settings area, with a menu item that has 1 or more child menus. Each one of those may have child menus.

So, something Like:

My Section (this is he non-clickable title, like "Templating")
    My Section menu (top level menu)
        Sub Menu 1
            Item 1
            Item 2
            etc

I have got “My Section” and “My Section menu” to appear, but cannot figure out how “Sub menu 1” and its child items are added.

I “think” the parts I’ve got working are defined in a manifest file (as they are working). But I can’t find the answer for how to create the sub level menus. “Sub Menu 1” level items will eventually need to be generated from database data, however for now I’d settle for hard-coding them!

I have tried looking in uSync code and a couple of other projects, but they seem to be missing the key parts.

Can anyone show me how I could at least hard-code sub-menus, or point me to a Umbraco help page (I can’t find one!) or a blog post or something?

Let’s not call it a ‘section’, because a section is an item in the menu af the top of the screen in Umbraco. I know, it’s semantics, but that threw me off at first when reading your question. :slight_smile:

I haven’t done it myself, but I think the default menu structure can only have one level:
sectionSidebarApp > menu > menu item(s).

I think you need a tree, like in the content section to get nested items. Once again, I haven’t don’t it myself, so I standard corrected if I’m wrong.

Yeah, sorry about that! The top level is a “Sidebar App” with a menu.

I assume multi-level menus can be created because Umbraco Commerce does it. Specifically, that is the type of hierarchy I am trying to create. The top level is a single (probably) fixed item, the next level is also a fixed item, but then it needs to be a list of “n” number of menu items driven by data in a custom DB table.

So, I can create the “Sidebar App” and it’s main / top level menu - and, I assume, I could add multiple menus in the same way at this level. The question is how do I add sub-menus!? (The other question could be - why is this so hard?!?)

Hi,

The approach depends a bit on how your sub menu and items are going to be defined,

- Static menus
if these are known to you then you can extend the menu-item , and render the items in your tree there, e.g this is what uSync is doing for it’s extensions :

uSync is a menu item defined in a manifest of uSyncMenu item. which then has it’s own special ‘render children’ method which in this case it renders usync-menuItem elements

render() {
	return html`<umb-menu-item-layout
		label=${this.manifest.meta.label ?? this.manifest.name}
		icon-name=${this.manifest.meta.icon ?? 'icon-bug'}
		.href=${this.itemPath}
		?has-Children=${this.hasChildren}
		>${this.renderChildren()}
	</umb-menu-item-layout>`;
}

renderChildren() {
	return html`<umb-extension-slot
		type="usync-menuItem"
		default-element="umb-menu-item-default"></umb-extension-slot>`;
}

This means the items are in code, but they are not coming from the database, rather other extensions. this is part of the power of the menu, it can ifact be anything you want it to be.

- Dynamic menu items
(n.b this is way more involved!)
if your menu is coming from the DB then you will probably need to implement a tree
this is what uSync.Complete does for servers :

here the servers come from the database, and we have had to impliement a tree in the manifest, with a treeItem and a menu item…

const tree: UmbExtensionManifest = {
	type: 'tree',
	kind: 'default',
	alias: 'usync.publisher.server.tree',
	name: 'Publisher tree',
	meta: {
		repositoryAlias: repository.alias,
	},
};

const treeItem: UmbExtensionManifest = {
	type: 'treeItem',
	kind: 'default',
	alias: 'sync.publisher.server.tree.rootitem',
	name: 'Publisher server tree root item',
	forEntityTypes: [SYNC_PUBLISHER_SERVER_ITEM_TYPE, SYNC_PUBLISHER_SERVER_ROOT_ITEM_TYPE],
};

const menuItem: UmbExtensionManifest = {
	type: 'menuItem',
	kind: 'tree',
	alias: 'usync.publisher.server.tree.menuitem',
	name: 'Publisher server menu item',
	weight: -1,
	meta: {
		label: 'usyncpublish_publisher',
		icon: 'icon-clock-alt',
		entityType: SYNC_PUBLISHER_SERVER_ITEM_TYPE,
		menus: ['usync.menu'],
		treeAlias: tree.alias,
	},
};

then there is even more to this, because you have to impliment a treeStore to get the items, and other bits. some of this is in the docs : Trees | Umbraco CMS

but not all of it because you also need to impliment the controllers in the same way as the core, without going to deep into it - it can start to look a bit like this :

 [HttpGet("ServerRoot")]
 [MapToApiVersion("1.0")]
 [ProducesResponseType(typeof(PagedViewModel<SyncServerItemResponseModel>), StatusCodes.Status200OK)]
 public async Task<ActionResult<PagedViewModel<SyncServerItemResponseModel>>> Root(CancellationToken cancellationToken, int skip = 0, int take = 100, bool foldersOnly = false)
 {
     IEnumerable<SyncServerItemResponseModel> items = await GetServers();
     PagedViewModel<SyncServerItemResponseModel> result = PagedViewModel(items, items.Count());
     return Ok(result);
 }

there is loads to this, and its quite hard to go through it all in a single post to be honest. the documention does give you a start - but depending on how complex it can become a little bit more digging through source code to get the tree working this way. (in my experience anyway)

1 Like

So, is “UmbExtensionManifest” instead of “ManifestSectionSidebarApp” at / as the top level of my menu?

Or do I still use “ManifestSectionSidebarApp” to create the Sidebar App and the top level menu, and then use “UmbExtensionManifest” to build my custom tree? If so, how do I link them?

I looked at the Trees documentation - and it seemed very minimal. I had trouble relating that to what you posted.

UmbExtrensionManifest is the new “generic type” for all manifest items in v16.

eg in v14 you defined a sidebar app like this :

const menuSidebarApp: ManifestTypes = {
	type: 'sectionSidebarApp',
	kind: 'menu',
	alias: 'usync.sidebarapp',
	name: 'uSync section sidebar menu',
	weight: 150,
	meta: {
		label: 'Syncronisation',
		menu: menu.alias,
	},
	conditions: [
		{
			alias: 'Umb.Condition.SectionAlias',
			match: sectionAlias,
		},
	],
};

but in v16 its like this :

const menuSidebarApp: UmbExtensionManifest = {
	type: 'sectionSidebarApp',
	kind: 'menu',
	alias: 'usync.sidebarapp',
	name: 'uSync section sidebar menu',
	weight: 150,
	meta: {
		label: 'Synchronisation',
		menu: menu.alias,
	},
	conditions: [
		{
			alias: 'Umb.Condition.SectionAlias',
			match: sectionAlias,
		},
	],
};

they are the same thing, the UmbExtensionManifest is just defined at a different place it means you have less imports across your code when you use them…

1 Like

Yeah, Its hard to explain to be honest: Its not super simple.

deep breath ! (or skip to the link to core code at the bottom!)

the core principles (without the technical bits) and working backwards in a manifest

  • you have a menu item,

    • kind “tree”
    • has a treeAlias, to tell it what the tree is.
  • so you have a “tree”

    • the point of the tree is to tell umbrco where the ‘treeRepository’ is
  • so you have a treeRepository.

    • the repository, is another pointer, this time to your store and data source,
    • it also has a method that tells umbraco how to get your tree route model. (the top level icon for the tree)
  • so next you have a ‘store’ and a data source :sweat_smile:

  • the store is fairly basic it extends UmbUniqueTreeStore

  • the source has three methods, getRootItems, getChildrenOf and getAncestorsOf, and these are what connect to your Controller to get the info for the tree


most of this i have lifted from the core a good place to look is the DictionaryItem tree.

So, I should probably upgrade to Umbraco 16? (currently using 15)

If I create a sidebar App like this

const sidebarApp: UmbExtensionManifest = {
    type: "sectionSidebarApp",
    kind: "menu",
    alias: "LC.Settings.Sidebar",
    name: "LC Settings Sidebar",
    weight: 1000,
    meta: {
        label: "LC",
        menu: MENU_ALIAS,
    },
    conditions: [{
        alias: "Umb.Condition.SectionAlias",
        match: SECTION_ALIAS,
    }],
};

const settingsMenuItem: UmbExtensionManifest = {
    type: 'menuItem',
    alias: 'LC.Settings.MenuItem',
    name: 'LC Settings Item',
    weight: 100,
    meta: {
       label: 'LC',
       icon: 'icon-folder',
       menus: [MENU_ALIAS]
    }
};

const menu: UmbExtensionManifest = {
    type: "menu",
    alias: MENU_ALIAS,
    name: "LC Settings Menu",
    meta: {
        menuItems: [settingsMenuItem.alias]
    }
};

How do I then make a custom tree appear under the “LC” top level menu?

yes upgrade to v16, no point staying on v15 (v16 has lots of bug fixes, stability improvements and if you are writing UI stuff you want to be on the latest release to minimise any documentation mismatches etc).

very short answer if you have a menu item with type tree, that then points to the tree that points to the repo. like the dictionary items do

then the thing you return in the repo’s requestTreeRoot will appear beneath the LC section of your menu.

(note the dictionary repo returns a list here, you can return a single item at the root, and render the other bits as it’s children).

when i did this, i got it working at one level and then worried later about moving it to the right place in the tree.

I can’t believe how much trouble I am having with this! I can create the sidebar app and the first / top menu item. I just cannot figure out how to add anything below that!

So - Do I start the same way and create a “sectionSidebarApp”, and a ‘menuItem’ and “menu” for it? (I have that and get a sidebar app with a single menu item)

Then - if I create a ‘tree’, a ‘treeItem’ and a ‘menuItem’ (as well as a repository) … how do I make the tree appear under the top level menu above?

Yeah this isn’t “Simple” and it’s really quite hard to explain with out doing all the code :frowning:

… so

here is all the code :

this impliments a menu in the setting section under it’s own sidebar.

The client code is here :

and the composer (that gets the tree items is here:

there might be things you don’t need in the manifest (i am not sure if you need both menuitem and treeitem) but this code works so :person_shrugging:

The best way to figure these things out is just by looking at existing code. Ofcourse you have the source code for Umbraco, but uSync is good as well and less massive in scale. Checkout the uSync sources Kevin pointed to.

I have tried looking at existing code, from various sources. However, I find that if you have no clue how something works, that is quite a difficult way to figure it out. Part of the problem is that it’s new and the Umbraco documentation is not there to support it.

I did manage to get a menu item to display yesterday (but not quite in the right place) so hopefully Kevin’s sample code will get me there :slight_smile: