From 7e0c59763c282a8342b72a5293aa9a05cc5a115f Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Mon, 8 Dec 2025 17:08:24 +0000 Subject: [PATCH 01/19] Restructure tree documentation into sub-articles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move tree docs from single file to dedicated tree/ directory - Add articles for tree repository, data source, store, navigation - Document hideTreeRoot property in menu-item.md - Remove duplicated/empty files from old locations πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- 17/umbraco-cms/SUMMARY.md | 7 +- .../extension-types/menu-item.md | 21 ++ .../stores-and-repositories/tree-store.md | 0 .../extension-types/tree-item.md | 0 .../extension-types/tree.md | 200 ------------- .../extension-types/tree/README.md | 263 ++++++++++++++++++ .../extension-types/tree/tree-data-source.md | 136 +++++++++ .../extension-types/tree/tree-navigation.md | 103 +++++++ .../extension-types/tree/tree-repository.md | 81 ++++++ .../extension-types/tree/tree-store.md | 67 +++++ .../repository-types/tree-repository.md | 14 - 11 files changed, 676 insertions(+), 216 deletions(-) delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/stores-and-repositories/tree-store.md delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree-item.md delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md delete mode 100644 17/umbraco-cms/customizing/foundation/repositories/repository-types/tree-repository.md diff --git a/17/umbraco-cms/SUMMARY.md b/17/umbraco-cms/SUMMARY.md index 0743edc8530..c926ea980da 100644 --- a/17/umbraco-cms/SUMMARY.md +++ b/17/umbraco-cms/SUMMARY.md @@ -188,7 +188,11 @@ * [Section](customizing/extending-overview/extension-types/sections/section.md) * [Section Sidebar](customizing/extending-overview/extension-types/sections/section-sidebar.md) * [Section View](customizing/extending-overview/extension-types/sections/section-view.md) - * [Trees](customizing/extending-overview/extension-types/tree.md) + * [Trees](customizing/extending-overview/extension-types/tree/README.md) + * [Tree Repository](customizing/extending-overview/extension-types/tree/tree-repository.md) + * [Tree Data Source](customizing/extending-overview/extension-types/tree/tree-data-source.md) + * [Tree Store](customizing/extending-overview/extension-types/tree/tree-store.md) + * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) * [Workspaces](customizing/extending-overview/extension-types/workspaces/README.md) * [Workspace Actions](customizing/extending-overview/extension-types/workspaces/workspace-editor-actions.md) * [Workspace Action Menu Items](customizing/extending-overview/extension-types/workspaces/workspace-action-menu-items.md) @@ -219,7 +223,6 @@ * [Collection Repository](customizing/foundation/repositories/repository-types/collection-repository.md) * [Detail Repository](customizing/foundation/repositories/repository-types/detail-repository.md) * [Item Repository](customizing/foundation/repositories/repository-types/item-repository.md) - * [Tree Repository](customizing/foundation/repositories/repository-types/tree-repository.md) * [States](customizing/foundation/states.md) * [Routes](customizing/foundation/routes.md) * [Backoffice Localization](customizing/foundation/localization.md) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/menu-item.md b/17/umbraco-cms/customizing/extending-overview/extension-types/menu-item.md index a7a0598c407..ca09421f2b7 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/menu-item.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/menu-item.md @@ -199,6 +199,27 @@ export const menuItemManifest: ManifestMenuItem = { ``` {% endcode %} +#### Hiding the Tree Root + +To display tree items at the root level without a parent folder node, add `hideTreeRoot: true` to the menu item's meta: + +```typescript +{ + type: 'menuItem', + kind: 'tree', + alias: 'My.MenuItem.Tree', + meta: { + treeAlias: 'My.Tree', + menus: ['My.Menu'], + hideTreeRoot: true, + }, +} +``` + +{% hint style="info" %} +The `hideTreeRoot` property must be set on the menuItem manifest, not the tree manifest. +{% endhint %} + ## Custom Menu Items {% hint style="info" %} diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/stores-and-repositories/tree-store.md b/17/umbraco-cms/customizing/extending-overview/extension-types/stores-and-repositories/tree-store.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree-item.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree-item.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree.md deleted file mode 100644 index f4b813dc8b1..00000000000 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -description: A guide to creating a custom tree in Umbraco ---- - -# Trees - -The tree is a hierarchical structure of nodes and is registered in the Backoffice extension registry. A tree can be rendered anywhere in the Backoffice with the help of the `` element. - -{% hint style="info" %} -To see how to register a tree as a menu in a section, refer to the [Section Sidebar](./sections/section-sidebar.md) article. -{% endhint %} - -## Creating trees - -To create a Tree in the Backoffice, you need to take multiple steps: - -### Registering a tree - -To register a tree, you need to create a manifest: - -```json -{ - "type": "tree", - "alias": "My.Tree.Alias", - "name": "My Tree", - "meta": { - "repositoryAlias": "My.Repository.Alias" - } -} -``` - -### Rendering a tree - -To render a tree in the Backoffice, you can use the `` element. You need to provide the alias of the tree you want to render. The alias is the same as the one you registered in the manifest. - -```typescript - -``` - -### Render a Custom Tree Item - -The `` element will render the tree items based on the registered tree item alias. The tree will be rendered using the [](https://apidocs.umbraco.com/v16/ui-api/classes/packages_core_tree.UmbDefaultTreeItemElement.html) element by default. If you want to render a custom tree item, you need to register a tree item manifest. This manifest can then show a custom element for the tree item. - -#### **The Tree Item Manifest** - -```json -{ - "type": "treeItem", - "alias": "Umb.TreeItem.Alias", - "name": "My Tree Item", - "element": "./my-tree-item.element.js", - "conditions": { - "entityType": "my-entity-type", - }, -}; -``` - -#### The Tree Item Element - -To create a custom tree item, you need to create a custom element. This element can optionally extend the [UmbTreeItemElementBase](https://apidocs.umbraco.com/v16/ui-api/classes/packages_core_tree.UmbTreeItemElementBase.html) class. However, it can also be used as a standalone element if you prefer to implement the tree item logic yourself. - -This example creates a custom tree item that extends the base class. The base class provides the necessary context and functionality for the tree item. - -```typescript -import type { MyTreeItemDataModel } from './my-tree-item.model.js'; -import { UmbTreeItemElementBase } from '@umbraco-cms/backoffice/tree'; -import { html, nothing, customElement } from '@umbraco-cms/backoffice/external/lit'; - -@customElement('my-tree-item') -export class MyTreeItemElement extends UmbTreeItemElementBase { - override render() { - if (!this.item) return nothing; - return html` -
- - ${this.item.name} -
- `; - } -} - -export default MyTreeItemElement; -``` - -#### The Tree Item Model - -To define the data model for your tree item, you can create a model that extends the [UmbTreeItemModel](https://apidocs.umbraco.com/v16/ui-api/interfaces/packages_core_tree.UmbTreeItemModel.html). This model will be used to provide the data for your custom tree item. - -{% code title="my-tree-item.model.ts" %} -```typescript -import type { UmbTreeItemModel } from '@umbraco-cms/backoffice/tree'; - -export interface MyTreeItemDataModel extends UmbTreeItemModel { - // Add any additional properties you need for your tree item -} -``` -{% endcode %} - - -### Adding data to the tree - -To add data to the tree, you need to create a repository that will provide the data for the tree. The repository is defined in the manifest of the tree and linked through its `repositoryAlias`. - -```json -{ - "type": "repository", - "alias": "My.Repository.Alias", - "name": "My Repository", - "api": "./my-repository.js" -} -``` - -#### Implementing the repository - -The repository needs to be able to fetch data for the tree. You can implement the repository as a class that extends the [UmbTreeRepositoryBase](https://apidocs.umbraco.com/v16/ui-api/classes/packages_core_tree.UmbTreeRepositoryBase.html) class. This class provides the necessary methods to fetch data for the tree. - -{% code title="my-repository.ts" %} -```typescript -import { MyTreeDataSource } from './my-tree-data-source.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbTreeRepositoryBase } from '@umbraco-cms/backoffice/tree'; - -export class MyRepository extends UmbTreeRepositoryBase { - constructor(host: UmbControllerHost) { - super(host, MyTreeDataSource); - } -} -``` -{% endcode %} - -#### Implementing the data source - -The data source is responsible for fetching the data for the tree. You can implement the data source as a class that implements the [UmbTreeDataSource](https://apidocs.umbraco.com/v16/ui-api/interfaces/packages_core_tree.UmbTreeDataSource.html) interface. - -{% code title="my-tree-data-source.ts" %} -```typescript -import type { MyTreeItemModel } from './my-tree-item.model.js'; -import type { - UmbTreeAncestorsOfRequestArgs, - UmbTreeChildrenOfRequestArgs, - UmbTreeDataSource, - UmbTreeRootItemsRequestArgs, -} from '@umbraco-cms/backoffice/tree'; - -export class MyTreeDataSource implements UmbTreeDataSource { - async getRootItems(args: UmbTreeRootItemsRequestArgs) { - // Fetch the root items for the tree - return [ - { - id: 'root1', - name: 'Root Item 1', - icon: 'icon-folder', - }, - { - id: 'root2', - name: 'Root Item 2', - icon: 'icon-folder', - }, - ]; - } - - async getChildrenOf(args: UmbTreeChildrenOfRequestArgs) { - // Fetch the children of the specified item - if (args.id === 'root1') { - return [ - { - id: 'child1', - name: 'Child Item 1', - icon: 'icon-document', - }, - { - id: 'child2', - name: 'Child Item 2', - icon: 'icon-document', - }, - ]; - } - return []; - } - - async getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs) { - // Fetch the ancestors of the specified item - if (args.id === 'child1') { - return [ - { - id: 'root1', - name: 'Root Item 1', - icon: 'icon-folder', - }, - ]; - } - return []; - } -} -``` -{% endcode %} - -## Further reading - -For more information on trees, you can refer to the examples folder in the GitHub repository: [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md new file mode 100644 index 00000000000..eb4a1202783 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -0,0 +1,263 @@ +--- +description: A guide to creating a custom tree in Umbraco +--- + +# Trees + +{% hint style="info" %} +**Looking to add sidebar navigation?** You probably want [Menus](../menu.md) and [Menu Items](../menu-item.md) first. Trees are just data providers that feed into menus - they don't display anything on their own. +{% endhint %} + +{% hint style="warning" %} +**Trees are data providers, not UI components.** A tree on its own does nothing visible. To create a working tree in the backoffice, you need to understand three things: + +1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar +2. **[Populating Trees](#populating-trees)** - How to provide data to your tree +3. **[Tree Navigation](#tree-navigation)** - How clicking tree items navigates to workspaces +{% endhint %} + +## Displaying Trees + +Trees provide hierarchical data to **Menus**, which display the actual navigation you see in the backoffice sidebar. To display a tree, you need to connect several extensions: + +``` +Tree (data provider) + ↓ referenced by +MenuItem (kind: 'tree', treeAlias: 'My.Tree') + ↓ belongs to +Menu + ↓ displayed by +SectionSidebarApp + ↓ appears in +Section (Content, Media, Settings, or custom) +``` + +### Tree Manifest + +Register your tree with a manifest. The `repositoryAlias` links to how the tree gets its data: + +```typescript +{ + type: 'tree', + kind: 'default', + alias: 'My.Tree', + name: 'My Tree', + meta: { + repositoryAlias: 'My.Tree.Repository', + }, +} +``` + +### Tree Item Manifest + +Tree items define how individual items render. Use `kind: 'default'` for standard rendering: + +```typescript +{ + type: 'treeItem', + kind: 'default', + alias: 'My.TreeItem', + forEntityTypes: ['my-entity-type'], +} +``` + +{% hint style="info" %} +The `forEntityTypes` array must match the `entityType` values returned by your data source. +{% endhint %} + +### Connecting to a Menu + +To display your tree in a section sidebar, create a MenuItem with `kind: 'tree'`: + +```typescript +{ + type: 'menuItem', + kind: 'tree', + alias: 'My.MenuItem.Tree', + meta: { + treeAlias: 'My.Tree', + menus: ['My.Menu'], + hideTreeRoot: true, // Optional: show items at root level + }, +} +``` + +See [Menu Items (Tree kind)](../menu-item.md#tree) and [Section Sidebar](../sections/section-sidebar.md) for the complete setup. + +### Standalone Rendering + +Trees can also be rendered directly in custom components using the `` element: + +```html + +``` + +This is less common than displaying via menus but useful for custom UIs. + +--- + +## Populating Trees + +Trees need data. You can populate a tree in two ways: + +### Option 1: Manual Data (Simple) + +For simple, static trees you can implement a basic data source that returns hardcoded or locally-computed items: + +```typescript +export class MySimpleDataSource implements UmbTreeDataSource { + async getRootItems() { + return { + items: [ + { unique: '1', entityType: 'my-item', name: 'Item 1', hasChildren: false, icon: 'icon-document' }, + { unique: '2', entityType: 'my-item', name: 'Item 2', hasChildren: false, icon: 'icon-document' }, + ], + total: 2, + }; + } + + async getChildrenOf(args) { + return { items: [], total: 0 }; + } + + async getAncestorsOf(args) { + return []; + } +} +``` + +### Option 2: Repository Pattern (Recommended for APIs) + +For trees backed by server APIs, use the full repository pattern with caching: + +``` +Repository (coordinates data + caching) + ↓ uses +Data Source (fetches from API) + ↓ caches in +Store (in-memory cache) +``` + +Register these in your manifests: + +```typescript +// Repository - referenced by tree via repositoryAlias +{ + type: 'repository', + alias: 'My.Tree.Repository', + name: 'My Tree Repository', + api: () => import('./my-tree.repository.js'), +} + +// Store - caches tree items +{ + type: 'treeStore', + alias: 'My.TreeStore', + name: 'My Tree Store', + api: () => import('./my-tree.store.js'), +} +``` + +For detailed implementation guides, see: + +- **[Tree Repository](./tree-repository.md)** - Extends `UmbTreeRepositoryBase` +- **[Tree Data Source](./tree-data-source.md)** - Implements `getRootItems`, `getChildrenOf`, `getAncestorsOf` +- **[Tree Store](./tree-store.md)** - Extends `UmbUniqueTreeStore` + +--- + +## Tree Navigation + +When users click a tree item, Umbraco navigates to a **workspace** to edit that item. This connection is made through the `entityType` - a string that links tree items to workspaces. + +{% hint style="warning" %} +**Clicking tree items shows endless loading?** You need a workspace registered for that `entityType`. +{% endhint %} + +### Quick Setup + +For tree items to navigate correctly: + +1. Your data source returns items with an `entityType` +2. Your workspace manifest has a matching `meta.entityType` +3. Your workspace uses `kind: 'routable'` + +```typescript +// Data source returns items with entityType +{ unique: '123', entityType: 'my-custom-item', name: 'My Item', ... } + +// Workspace receives navigation for that entityType +{ + type: 'workspace', + kind: 'routable', // Required for tree navigation + alias: 'My.Workspace', + meta: { + entityType: 'my-custom-item', // Must match + }, +} +``` + +For the full explanation including troubleshooting, see **[Tree Navigation & Workspaces](./tree-navigation.md)**. + +--- + +## Further Reading + +- [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) - Working examples in the Umbraco repository +- [Workspaces](../workspace/README.md) - Creating workspace extensions + +## Custom Tree Items + +For specialized tree item rendering, you can create custom elements instead of using `kind: 'default'`. See below for details. + +
+Custom Tree Item Element + +Create a custom element extending `UmbTreeItemElementBase`: + +{% code title="Manifest" %} +```json +{ + "type": "treeItem", + "alias": "My.TreeItem.Custom", + "name": "My Custom Tree Item", + "element": "./my-tree-item.element.js", + "forEntityTypes": ["my-entity-type"] +} +``` +{% endcode %} + +{% code title="my-tree-item.element.ts" %} +```typescript +import type { MyTreeItemDataModel } from './my-tree-item.model.js'; +import { UmbTreeItemElementBase } from '@umbraco-cms/backoffice/tree'; +import { html, nothing, customElement } from '@umbraco-cms/backoffice/external/lit'; + +@customElement('my-tree-item') +export class MyTreeItemElement extends UmbTreeItemElementBase { + override render() { + if (!this.item) return nothing; + return html` +
+ + ${this.item.name} +
+ `; + } +} + +export default MyTreeItemElement; +``` +{% endcode %} + +{% code title="my-tree-item.model.ts" %} +```typescript +import type { UmbTreeItemModel } from '@umbraco-cms/backoffice/tree'; + +export interface MyTreeItemDataModel extends UmbTreeItemModel { + // Add any additional properties you need +} +``` +{% endcode %} + +
diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md new file mode 100644 index 00000000000..452a1cd7c55 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md @@ -0,0 +1,136 @@ +--- +description: Implementing a tree data source to provide tree data in Umbraco +--- + +# Tree Data Source + +A tree data source is responsible for fetching data for a tree. It implements methods to retrieve root items, children of a specific item, and ancestors of an item. + +## Interface + +The `UmbTreeDataSource` interface defines three methods: + +```typescript +interface UmbTreeDataSource { + getRootItems(args: UmbTreeRootItemsRequestArgs): Promise>; + getChildrenOf(args: UmbTreeChildrenOfRequestArgs): Promise>; + getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs): Promise>; +} +``` + +## Implementing a Data Source + +For most cases, extend `UmbTreeServerDataSourceBase` which handles common tree operations and provides a mapper for transforming API responses. + +{% code title="my-tree.data-source.ts" %} +```typescript +import type { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; +import type { + UmbTreeAncestorsOfRequestArgs, + UmbTreeChildrenOfRequestArgs, + UmbTreeRootItemsRequestArgs, +} from "@umbraco-cms/backoffice/tree"; +import { UmbTreeServerDataSourceBase } from "@umbraco-cms/backoffice/tree"; +import type { MyTreeItemResponseModel } from "../api/index.js"; +import { MyTreeClientService } from "../api/index.js"; +import { + MY_TREE_ITEM_ENTITY_TYPE, + MY_TREE_ROOT_ENTITY_TYPE, + type MyTreeItemModel, +} from "./types.js"; + +export class MyTreeDataSource extends UmbTreeServerDataSourceBase< + MyTreeItemResponseModel, + MyTreeItemModel +> { + constructor(host: UmbControllerHost) { + super(host, { + getRootItems, + getChildrenOf, + getAncestorsOf, + mapper, + }); + } +} + +const getRootItems = async (args: UmbTreeRootItemsRequestArgs) => + await MyTreeClientService.getRoot({ + query: { skip: args.skip, take: args.take }, + }); + +const getChildrenOf = async (args: UmbTreeChildrenOfRequestArgs) => { + if (args.parent?.unique === null) { + return await getRootItems(args); + } else { + return await MyTreeClientService.getChildren({ + query: { parent: args.parent.unique }, + }); + } +}; + +const getAncestorsOf = async (args: UmbTreeAncestorsOfRequestArgs) => { + return await MyTreeClientService.getAncestors({ + query: { id: args.treeItem.unique }, + }); +}; + +const mapper = (item: MyTreeItemResponseModel): MyTreeItemModel => { + return { + unique: item.id ?? "", + parent: { unique: "", entityType: MY_TREE_ROOT_ENTITY_TYPE }, + name: item.name ?? "unknown", + entityType: MY_TREE_ITEM_ENTITY_TYPE, + hasChildren: item.hasChildren, + isFolder: false, + icon: item.icon ?? "icon-document", + }; +}; +``` +{% endcode %} + +## Method Details + +### getRootItems + +Fetches the top-level items in the tree. Receives pagination arguments (`skip`, `take`). + +### getChildrenOf + +Fetches children of a specific parent item. The `args.parent` contains the parent's `unique` identifier and `entityType`. + +{% hint style="info" %} +When `args.parent.unique` is `null`, it typically means the root level, so you may delegate to `getRootItems`. +{% endhint %} + +### getAncestorsOf + +Returns the ancestor path for a given item. Used for breadcrumb navigation and expanding the tree to a specific item. + +## The Mapper Function + +The mapper transforms your API response model into the tree item model format Umbraco expects: + +```typescript +interface UmbTreeItemModel { + unique: string; // Unique identifier + entityType: string; // Must match workspace entityType for navigation + name: string; // Display name + hasChildren: boolean; // Shows expand arrow if true + isFolder: boolean; // Visual styling hint + icon?: string; // Icon to display + parent?: { // Parent reference + unique: string; + entityType: string; + }; +} +``` + +{% hint style="warning" %} +The `entityType` in your tree item model must match the `entityType` in your workspace manifest for tree item clicks to navigate correctly. +{% endhint %} + +## Related + +- [Tree Repository](./tree-repository.md) - Uses the data source +- [Tree Store](./tree-store.md) - Caches tree items +- [Trees](./README.md) - Main tree extension documentation diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md new file mode 100644 index 00000000000..da7edeb39d5 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md @@ -0,0 +1,103 @@ +--- +description: How tree items navigate to workspaces when clicked in Umbraco +--- + +# Tree Navigation & Workspaces + +Trees and workspaces are tightly coupled. When users click a tree item, Umbraco navigates to a workspace to edit that item. This connection is made through the `entityType` - a string identifier that links tree items to their corresponding workspace. + +## How Tree Items Connect to Workspaces + +When you click a tree item: + +1. Umbraco reads the `entityType` from the tree item data +2. It looks for a workspace registered with a matching `meta.entityType` +3. It navigates to that workspace, passing the item's `unique` identifier + +``` +Tree Item clicked (entityType: 'my-custom-item', unique: '123') + ↓ +Workspace found (meta.entityType: 'my-custom-item') + ↓ +Navigation to: /section/my-section/workspace/my-custom-item/edit/123 +``` + +## Workspace Kind: Routable vs Default + +For tree navigation to work correctly, your workspace must use `kind: 'routable'`: + +```typescript +// Tree item manifest +{ + type: 'treeItem', + kind: 'default', + alias: 'My.TreeItem', + forEntityTypes: ['my-custom-item'], +} + +// Workspace manifest - MUST be routable for tree navigation +{ + type: 'workspace', + kind: 'routable', + alias: 'My.Workspace', + name: 'My Custom Item Workspace', + meta: { + entityType: 'my-custom-item', // Must match tree item entityType + }, +} +``` + +{% hint style="info" %} +**Why `kind: 'routable'`?** Routable workspaces generate proper URLs and handle navigation state. This allows: +- Direct linking to specific items +- Browser back/forward navigation +- Correct tree item selection highlighting when switching between items +{% endhint %} + +## Common Issues + +{% hint style="warning" %} +**Endless loading when clicking tree items?** This usually means: +- No workspace is registered for that `entityType` +- The `entityType` in your tree data doesn't match the workspace's `meta.entityType` +- The workspace is using `kind: 'default'` instead of `kind: 'routable'` +{% endhint %} + +## Complete Example + +Here's how tree items, tree item manifests, and workspaces connect: + +```typescript +// 1. Your data source returns items with entityType +const mapper = (item: ApiResponse): MyTreeItemModel => ({ + unique: item.id, + entityType: 'my-custom-item', // This links to the workspace + name: item.name, + hasChildren: false, + icon: 'icon-document', +}); + +// 2. Tree item manifest handles this entityType +{ + type: 'treeItem', + kind: 'default', + alias: 'My.TreeItem', + forEntityTypes: ['my-custom-item'], +} + +// 3. Workspace manifest receives navigation for this entityType +{ + type: 'workspace', + kind: 'routable', + alias: 'My.Workspace', + name: 'My Workspace', + meta: { + entityType: 'my-custom-item', + }, +} +``` + +## Related + +- [Trees](./README.md) - Main tree extension documentation +- [Workspaces](../workspace/README.md) - Creating workspace extensions diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md new file mode 100644 index 00000000000..5472e4c2a24 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -0,0 +1,81 @@ +# Tree Repository + +A tree repository is a specific type of repository designed to handle operations related to tree data structures. It provides methods to request tree roots, tree items, and their ancestors. + +The interface below is simplified for clarity and omits return types and arguments. See full interfaces in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). + +```typescript +interface UmbTreeRepository { + requestTreeRoot(); + requestTreeRootItems(); + requestTreeItemsOf(); + requestTreeItemAncestors(); +} +``` + +## Implementing a Tree Repository + +To implement a tree repository, extend the `UmbTreeRepositoryBase` class. The base class requires: + +1. A **Data Source** - Fetches tree data from your API +2. A **Store Context** - Caches tree items for performance + +{% code title="my-tree.repository.ts" %} +```typescript +import type { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; +import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; +import { UmbTreeRepositoryBase } from "@umbraco-cms/backoffice/tree"; +import { MY_TREE_STORE_CONTEXT } from "./my-tree.store.js"; +import { MyTreeDataSource } from "./my-tree.data-source.js"; +import { + MY_TREE_ROOT_ENTITY_TYPE, + type MyTreeItemModel, + type MyTreeRootModel, +} from "./types.js"; + +export class MyTreeRepository + extends UmbTreeRepositoryBase + implements UmbApi +{ + constructor(host: UmbControllerHost) { + super(host, MyTreeDataSource, MY_TREE_STORE_CONTEXT); + } + + async requestTreeRoot() { + const data: MyTreeRootModel = { + unique: null, + entityType: MY_TREE_ROOT_ENTITY_TYPE, + name: "My Tree Root", + icon: "icon-folder", + hasChildren: true, + isFolder: true, + }; + + return { data }; + } +} + +export { MyTreeRepository as api }; +``` +{% endcode %} + +### Registering the Repository + +Register the repository in your manifest: + +```typescript +{ + type: 'repository', + alias: 'My.Tree.Repository', + name: 'My Tree Repository', + api: () => import('./my-tree.repository.js'), +} +``` + +The repository alias is referenced by your tree manifest via `meta.repositoryAlias`. + +## Related + +- [Tree Data Source](./tree-data-source.md) - Implements data fetching methods +- [Tree Store](./tree-store.md) - Caches tree items +- [Trees](./README.md) - Main tree extension documentation \ No newline at end of file diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md new file mode 100644 index 00000000000..578209ad629 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md @@ -0,0 +1,67 @@ +--- +description: Implementing a tree store to cache tree data in Umbraco +--- + +# Tree Store + +A tree store caches tree items in memory, improving performance by avoiding repeated API calls for the same data. The store is used by the tree repository to manage tree item state. + +## Implementing a Tree Store + +Extend `UmbUniqueTreeStore` and create a context token for dependency injection: + +{% code title="my-tree.store.ts" %} +```typescript +import { UmbContextToken } from "@umbraco-cms/backoffice/context-api"; +import { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; +import { UmbUniqueTreeStore } from "@umbraco-cms/backoffice/tree"; + +export class MyTreeStore extends UmbUniqueTreeStore { + constructor(host: UmbControllerHost) { + super(host, MY_TREE_STORE_CONTEXT.toString()); + } +} + +export { MyTreeStore as api }; + +export const MY_TREE_STORE_CONTEXT = new UmbContextToken( + "MY_TREE_STORE_CONTEXT" +); +``` +{% endcode %} + +## Registering the Store + +Register the store in your manifest alongside your tree: + +```typescript +{ + type: 'treeStore', + alias: 'My.TreeStore', + name: 'My Tree Store', + api: () => import('./my-tree.store.js'), +} +``` + +## Using the Store + +The store context is passed to the tree repository constructor: + +```typescript +export class MyTreeRepository extends UmbTreeRepositoryBase { + constructor(host: UmbControllerHost) { + super(host, MyTreeDataSource, MY_TREE_STORE_CONTEXT); + } +} +``` + +The base repository class handles all store operations automatically: +- Caching fetched items +- Retrieving cached items before making API calls +- Updating cached items when data changes + +## Related + +- [Tree Repository](./tree-repository.md) - Uses the store for caching +- [Tree Data Source](./tree-data-source.md) - Fetches data that gets cached +- [Trees](./README.md) - Main tree extension documentation diff --git a/17/umbraco-cms/customizing/foundation/repositories/repository-types/tree-repository.md b/17/umbraco-cms/customizing/foundation/repositories/repository-types/tree-repository.md deleted file mode 100644 index 68f562ffd84..00000000000 --- a/17/umbraco-cms/customizing/foundation/repositories/repository-types/tree-repository.md +++ /dev/null @@ -1,14 +0,0 @@ -# Tree Repository - -A tree repository is a specific type of repository designed to handle operations related to tree data structures. It provides methods to request tree roots, tree items, and their ancestors. - -The interface below is simplified for clarity and omits return types and arguments. See full interfaces in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). - -```typescript -interface UmbTreeRepository { - requestTreeRoot(); - requestTreeRootItems(); - requestTreeItemsOf(); - requestTreeItemAncestors(); -} -``` \ No newline at end of file From 66e0fa985ae97ea206716c6520050bb695838620 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Mon, 8 Dec 2025 17:22:09 +0000 Subject: [PATCH 02/19] Clarify API responsibilities and reduce duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add hints to repository, data source, and store docs clarifying that only the data source communicates with APIs - Remove duplicated navigation content from README (now in tree-navigation.md) - Remove incomplete custom tree item example πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extension-types/tree/README.md | 87 +------------------ .../extension-types/tree/tree-data-source.md | 6 +- .../extension-types/tree/tree-repository.md | 6 +- .../extension-types/tree/tree-store.md | 4 + 4 files changed, 17 insertions(+), 86 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index eb4a1202783..9a8156e815a 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -168,96 +168,15 @@ For detailed implementation guides, see: ## Tree Navigation -When users click a tree item, Umbraco navigates to a **workspace** to edit that item. This connection is made through the `entityType` - a string that links tree items to workspaces. +When users click a tree item, Umbraco navigates to a **workspace** to edit that item. The `entityType` in your tree items must match the `meta.entityType` in your workspace manifest. {% hint style="warning" %} -**Clicking tree items shows endless loading?** You need a workspace registered for that `entityType`. +**Clicking tree items shows endless loading?** See [Tree Navigation & Workspaces](./tree-navigation.md) for setup and troubleshooting. {% endhint %} -### Quick Setup - -For tree items to navigate correctly: - -1. Your data source returns items with an `entityType` -2. Your workspace manifest has a matching `meta.entityType` -3. Your workspace uses `kind: 'routable'` - -```typescript -// Data source returns items with entityType -{ unique: '123', entityType: 'my-custom-item', name: 'My Item', ... } - -// Workspace receives navigation for that entityType -{ - type: 'workspace', - kind: 'routable', // Required for tree navigation - alias: 'My.Workspace', - meta: { - entityType: 'my-custom-item', // Must match - }, -} -``` - -For the full explanation including troubleshooting, see **[Tree Navigation & Workspaces](./tree-navigation.md)**. - --- ## Further Reading - [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) - Working examples in the Umbraco repository -- [Workspaces](../workspace/README.md) - Creating workspace extensions - -## Custom Tree Items - -For specialized tree item rendering, you can create custom elements instead of using `kind: 'default'`. See below for details. - -
-Custom Tree Item Element - -Create a custom element extending `UmbTreeItemElementBase`: - -{% code title="Manifest" %} -```json -{ - "type": "treeItem", - "alias": "My.TreeItem.Custom", - "name": "My Custom Tree Item", - "element": "./my-tree-item.element.js", - "forEntityTypes": ["my-entity-type"] -} -``` -{% endcode %} - -{% code title="my-tree-item.element.ts" %} -```typescript -import type { MyTreeItemDataModel } from './my-tree-item.model.js'; -import { UmbTreeItemElementBase } from '@umbraco-cms/backoffice/tree'; -import { html, nothing, customElement } from '@umbraco-cms/backoffice/external/lit'; - -@customElement('my-tree-item') -export class MyTreeItemElement extends UmbTreeItemElementBase { - override render() { - if (!this.item) return nothing; - return html` -
- - ${this.item.name} -
- `; - } -} - -export default MyTreeItemElement; -``` -{% endcode %} - -{% code title="my-tree-item.model.ts" %} -```typescript -import type { UmbTreeItemModel } from '@umbraco-cms/backoffice/tree'; - -export interface MyTreeItemDataModel extends UmbTreeItemModel { - // Add any additional properties you need -} -``` -{% endcode %} - -
+- [Workspaces](../workspaces/README.md) - Creating workspace extensions diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md index 452a1cd7c55..d02c755e72f 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md @@ -4,7 +4,11 @@ description: Implementing a tree data source to provide tree data in Umbraco # Tree Data Source -A tree data source is responsible for fetching data for a tree. It implements methods to retrieve root items, children of a specific item, and ancestors of an item. +The data source is the **only component that communicates with your API**. It implements methods to retrieve root items, children of a specific item, and ancestors of an item. + +{% hint style="info" %} +This is where you call your API endpoints. The [Repository](./tree-repository.md) and [Store](./tree-store.md) never make API calls directlyβ€”all server communication goes through the data source. +{% endhint %} ## Interface diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index 5472e4c2a24..c5eb5b7ad5f 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -1,6 +1,10 @@ # Tree Repository -A tree repository is a specific type of repository designed to handle operations related to tree data structures. It provides methods to request tree roots, tree items, and their ancestors. +A tree repository orchestrates tree data operations by coordinating between a **Data Source** (which fetches data from APIs) and a **Store** (which caches data in memory). The repository itself does not communicate with APIs directly. + +{% hint style="info" %} +The repository delegates all API communication to the [Data Source](./tree-data-source.md). It handles caching through the [Store](./tree-store.md). +{% endhint %} The interface below is simplified for clarity and omits return types and arguments. See full interfaces in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md index 578209ad629..f0bf5b5b63c 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md @@ -6,6 +6,10 @@ description: Implementing a tree store to cache tree data in Umbraco A tree store caches tree items in memory, improving performance by avoiding repeated API calls for the same data. The store is used by the tree repository to manage tree item state. +{% hint style="info" %} +The store does not communicate with APIs. It only holds cached data. All API communication happens in the [Data Source](./tree-data-source.md). +{% endhint %} + ## Implementing a Tree Store Extend `UmbUniqueTreeStore` and create a context token for dependency injection: From 41b5cefa07ac641c98c03e12af029d65c0690efe Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 08:54:12 +0000 Subject: [PATCH 03/19] Improve tree navigation link visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make navigation link a clear call-to-action instead of buried in warning - Move Tree Navigation first in SUMMARY.md for better discoverability πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- 17/umbraco-cms/SUMMARY.md | 2 +- .../extending-overview/extension-types/tree/README.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/17/umbraco-cms/SUMMARY.md b/17/umbraco-cms/SUMMARY.md index c926ea980da..793ab5edb7a 100644 --- a/17/umbraco-cms/SUMMARY.md +++ b/17/umbraco-cms/SUMMARY.md @@ -189,10 +189,10 @@ * [Section Sidebar](customizing/extending-overview/extension-types/sections/section-sidebar.md) * [Section View](customizing/extending-overview/extension-types/sections/section-view.md) * [Trees](customizing/extending-overview/extension-types/tree/README.md) + * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) * [Tree Repository](customizing/extending-overview/extension-types/tree/tree-repository.md) * [Tree Data Source](customizing/extending-overview/extension-types/tree/tree-data-source.md) * [Tree Store](customizing/extending-overview/extension-types/tree/tree-store.md) - * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) * [Workspaces](customizing/extending-overview/extension-types/workspaces/README.md) * [Workspace Actions](customizing/extending-overview/extension-types/workspaces/workspace-editor-actions.md) * [Workspace Action Menu Items](customizing/extending-overview/extension-types/workspaces/workspace-action-menu-items.md) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 9a8156e815a..e2e61d5fdba 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -170,9 +170,7 @@ For detailed implementation guides, see: When users click a tree item, Umbraco navigates to a **workspace** to edit that item. The `entityType` in your tree items must match the `meta.entityType` in your workspace manifest. -{% hint style="warning" %} -**Clicking tree items shows endless loading?** See [Tree Navigation & Workspaces](./tree-navigation.md) for setup and troubleshooting. -{% endhint %} +See **[Tree Navigation & Workspaces](./tree-navigation.md)** for setup details and troubleshooting. --- From 3c810a78e76a8d2783bdc3707999cc16ce9e31f8 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 09:14:51 +0000 Subject: [PATCH 04/19] Add tree models documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create tree-models.md explaining UmbTreeItemModel and UmbTreeRootModel - Document all properties with descriptions and usage examples - Add to SUMMARY.md navigation and README.md implementation guides πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- 17/umbraco-cms/SUMMARY.md | 1 + .../extension-types/tree/README.md | 1 + .../extension-types/tree/tree-models.md | 114 ++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md diff --git a/17/umbraco-cms/SUMMARY.md b/17/umbraco-cms/SUMMARY.md index 793ab5edb7a..e57a0b26415 100644 --- a/17/umbraco-cms/SUMMARY.md +++ b/17/umbraco-cms/SUMMARY.md @@ -190,6 +190,7 @@ * [Section View](customizing/extending-overview/extension-types/sections/section-view.md) * [Trees](customizing/extending-overview/extension-types/tree/README.md) * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) + * [Tree Models](customizing/extending-overview/extension-types/tree/tree-models.md) * [Tree Repository](customizing/extending-overview/extension-types/tree/tree-repository.md) * [Tree Data Source](customizing/extending-overview/extension-types/tree/tree-data-source.md) * [Tree Store](customizing/extending-overview/extension-types/tree/tree-store.md) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index e2e61d5fdba..fe89de28e71 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -160,6 +160,7 @@ Register these in your manifests: For detailed implementation guides, see: +- **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces - **[Tree Repository](./tree-repository.md)** - Extends `UmbTreeRepositoryBase` - **[Tree Data Source](./tree-data-source.md)** - Implements `getRootItems`, `getChildrenOf`, `getAncestorsOf` - **[Tree Store](./tree-store.md)** - Extends `UmbUniqueTreeStore` diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md new file mode 100644 index 00000000000..542a7d289a3 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -0,0 +1,114 @@ +--- +description: Understanding tree item and root models in Umbraco +--- + +# Tree Models + +Trees use two model types to represent data: **Tree Item Model** for individual nodes and **Tree Root Model** for the root node. Your [Data Source](./tree-data-source.md) transforms API responses into these models. + +## UmbTreeItemModel + +The base interface for tree items. All tree items must include these properties: + +```typescript +interface UmbTreeItemModel { + unique: string; // Unique identifier for this item + entityType: string; // Links to workspace for navigation (must match workspace meta.entityType) + name: string; // Display name shown in the tree + hasChildren: boolean; // Shows expand arrow when true + isFolder: boolean; // Visual styling hint + icon?: string; // Icon name (e.g., 'icon-document', 'icon-folder') + parent?: { // Parent reference for hierarchy + unique: string; + entityType: string; + }; +} +``` + +### Property Details + +| Property | Required | Description | +|----------|----------|-------------| +| `unique` | Yes | Identifier used for selection, navigation, and API calls. Usually maps to your entity's ID. | +| `entityType` | Yes | Must match your workspace's `meta.entityType` for tree clicks to navigate correctly. | +| `name` | Yes | Displayed as the tree item label. | +| `hasChildren` | Yes | Controls whether the expand/collapse arrow appears. Set `true` if the item can have children, even if currently empty. | +| `isFolder` | Yes | Affects visual styling. Folders typically show differently than leaf items. | +| `icon` | No | Icon displayed next to the name. Uses Umbraco icon names. | +| `parent` | No | Reference to parent item. Used for building hierarchy and breadcrumbs. | + +### Extending the Model + +Add custom properties by extending the base interface: + +```typescript +import type { UmbTreeItemModel } from '@umbraco-cms/backoffice/tree'; + +export interface MyTreeItemModel extends UmbTreeItemModel { + // Add custom properties + status: 'draft' | 'published'; + lastModified: string; +} +``` + +Your [Data Source mapper](./tree-data-source.md#the-mapper-function) populates these properties from your API response. + +## UmbTreeRootModel + +The root model represents the top-level node of your tree. It extends `UmbTreeItemModel` but typically has `unique: null`: + +```typescript +interface UmbTreeRootModel extends UmbTreeItemModel { + unique: null; // Root has no parent, so unique is null +} +``` + +### Defining a Root Model + +```typescript +import type { UmbTreeRootModel } from '@umbraco-cms/backoffice/tree'; + +export interface MyTreeRootModel extends UmbTreeRootModel { + // Root-specific properties if needed +} + +// Example root data returned by repository +const rootData: MyTreeRootModel = { + unique: null, + entityType: 'my-tree-root', + name: 'My Tree', + hasChildren: true, + isFolder: true, + icon: 'icon-folder', +}; +``` + +The root model is returned by `requestTreeRoot()` in your [Tree Repository](./tree-repository.md). + +## Entity Types + +You typically define two entity types - one for the root and one for items: + +```typescript +// types.ts +export const MY_TREE_ROOT_ENTITY_TYPE = 'my-tree-root'; +export const MY_TREE_ITEM_ENTITY_TYPE = 'my-tree-item'; + +export interface MyTreeRootModel extends UmbTreeRootModel { + entityType: typeof MY_TREE_ROOT_ENTITY_TYPE; +} + +export interface MyTreeItemModel extends UmbTreeItemModel { + entityType: typeof MY_TREE_ITEM_ENTITY_TYPE; +} +``` + +{% hint style="info" %} +The `entityType` values must match your workspace and tree item manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. +{% endhint %} + +## Related + +- [Tree Data Source](./tree-data-source.md) - Transforms API responses into tree models +- [Tree Repository](./tree-repository.md) - Returns root model via `requestTreeRoot()` +- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to workspaces From cc7148a3b26fd148526a1853b4a8a8e8f924bd25 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 09:29:13 +0000 Subject: [PATCH 05/19] Clarify when to use trees vs menu items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extending-overview/extension-types/tree/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index fe89de28e71..1b491a4d587 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -5,7 +5,7 @@ description: A guide to creating a custom tree in Umbraco # Trees {% hint style="info" %} -**Looking to add sidebar navigation?** You probably want [Menus](../menu.md) and [Menu Items](../menu-item.md) first. Trees are just data providers that feed into menus - they don't display anything on their own. +**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For simple, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. Trees link to menus as a data source via the `menuItem` with `kind: 'tree'`. {% endhint %} {% hint style="warning" %} From 864ea707c2752e2f15e3d129463f22e7c9358903 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 09:43:03 +0000 Subject: [PATCH 06/19] Simplify tree documentation intro hints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extending-overview/extension-types/tree/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 1b491a4d587..20465070cc9 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -5,15 +5,15 @@ description: A guide to creating a custom tree in Umbraco # Trees {% hint style="info" %} -**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For simple, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. Trees link to menus as a data source via the `menuItem` with `kind: 'tree'`. +**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For simple, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. {% endhint %} {% hint style="warning" %} **Trees are data providers, not UI components.** A tree on its own does nothing visible. To create a working tree in the backoffice, you need to understand three things: -1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar -2. **[Populating Trees](#populating-trees)** - How to provide data to your tree -3. **[Tree Navigation](#tree-navigation)** - How clicking tree items navigates to workspaces +1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar or elsewhere +2. **[Populating Trees](#populating-trees)** - How to populate tree with data +3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces byt clicking menu items {% endhint %} ## Displaying Trees From 89073a226fabd587498304e97b25a00dbc44a4c8 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 09:54:30 +0000 Subject: [PATCH 07/19] Remove duplicate property table from tree models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extension-types/tree/tree-models.md | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md index 542a7d289a3..c6e01bc8071 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -12,31 +12,19 @@ The base interface for tree items. All tree items must include these properties: ```typescript interface UmbTreeItemModel { - unique: string; // Unique identifier for this item - entityType: string; // Links to workspace for navigation (must match workspace meta.entityType) + unique: string; // Identifier for selection, navigation, and API calls + entityType: string; // Must match workspace meta.entityType for navigation name: string; // Display name shown in the tree hasChildren: boolean; // Shows expand arrow when true isFolder: boolean; // Visual styling hint icon?: string; // Icon name (e.g., 'icon-document', 'icon-folder') - parent?: { // Parent reference for hierarchy - unique: string; - entityType: string; + parent?: { // Parent reference for hierarchy and breadcrumbs + unique: string; // Parent's identifier + entityType: string; // Parent's entity type }; } ``` -### Property Details - -| Property | Required | Description | -|----------|----------|-------------| -| `unique` | Yes | Identifier used for selection, navigation, and API calls. Usually maps to your entity's ID. | -| `entityType` | Yes | Must match your workspace's `meta.entityType` for tree clicks to navigate correctly. | -| `name` | Yes | Displayed as the tree item label. | -| `hasChildren` | Yes | Controls whether the expand/collapse arrow appears. Set `true` if the item can have children, even if currently empty. | -| `isFolder` | Yes | Affects visual styling. Folders typically show differently than leaf items. | -| `icon` | No | Icon displayed next to the name. Uses Umbraco icon names. | -| `parent` | No | Reference to parent item. Used for building hierarchy and breadcrumbs. | - ### Extending the Model Add custom properties by extending the base interface: From 296a9776a7625e24af8f2ad34154cf557886ba17 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Tue, 9 Dec 2025 10:08:01 +0000 Subject: [PATCH 08/19] Fix tree manifest examples to match working implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Include both root and item entity types in treeItem forEntityTypes - Add missing menuItem properties: name, weight, label, icon, entityType πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extending-overview/extension-types/tree/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 20465070cc9..4146bc5cedf 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -57,12 +57,12 @@ Tree items define how individual items render. Use `kind: 'default'` for standar type: 'treeItem', kind: 'default', alias: 'My.TreeItem', - forEntityTypes: ['my-entity-type'], + forEntityTypes: ['my-tree-root', 'my-tree-item'], } ``` {% hint style="info" %} -The `forEntityTypes` array must match the `entityType` values returned by your data source. +Include both your root entity type and item entity type in `forEntityTypes` so the tree item renderer handles all nodes in your tree. {% endhint %} ### Connecting to a Menu @@ -74,9 +74,14 @@ To display your tree in a section sidebar, create a MenuItem with `kind: 'tree'` type: 'menuItem', kind: 'tree', alias: 'My.MenuItem.Tree', + name: 'My Tree Menu Item', + weight: 100, meta: { - treeAlias: 'My.Tree', + label: 'My Tree', + icon: 'icon-folder', + entityType: 'my-tree-root', menus: ['My.Menu'], + treeAlias: 'My.Tree', hideTreeRoot: true, // Optional: show items at root level }, } From c33ea461098cddb908f0f4d43d9b7d0056673cd9 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Wed, 10 Dec 2025 09:32:20 +0000 Subject: [PATCH 09/19] Minor wording improvements to tree documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extending-overview/extension-types/tree/README.md | 8 ++++---- .../extension-types/tree/tree-repository.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 4146bc5cedf..0beb3049905 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -5,7 +5,7 @@ description: A guide to creating a custom tree in Umbraco # Trees {% hint style="info" %} -**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For simple, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. +**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For basic, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. {% endhint %} {% hint style="warning" %} @@ -18,7 +18,7 @@ description: A guide to creating a custom tree in Umbraco ## Displaying Trees -Trees provide hierarchical data to **Menus**, which display the actual navigation you see in the backoffice sidebar. To display a tree, you need to connect several extensions: +Trees provide hierarchical data to **Menus**, which display the actual navigation you see in the backoffice sidebar. To display a tree, you need to connect multiple extensions: ``` Tree (data provider) @@ -105,12 +105,12 @@ This is less common than displaying via menus but useful for custom UIs. Trees need data. You can populate a tree in two ways: -### Option 1: Manual Data (Simple) +### Option 1: Manual Data (Basic) For simple, static trees you can implement a basic data source that returns hardcoded or locally-computed items: ```typescript -export class MySimpleDataSource implements UmbTreeDataSource { +export class MyBasicDataSource implements UmbTreeDataSource { async getRootItems() { return { items: [ diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index c5eb5b7ad5f..2de4cc56fac 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -1,6 +1,6 @@ # Tree Repository -A tree repository orchestrates tree data operations by coordinating between a **Data Source** (which fetches data from APIs) and a **Store** (which caches data in memory). The repository itself does not communicate with APIs directly. +A tree repository orchestrates tree data operations. It coordinates between a **Data Source** (which fetches from APIs) and a **Store** (which caches data in memory). {% hint style="info" %} The repository delegates all API communication to the [Data Source](./tree-data-source.md). It handles caching through the [Store](./tree-store.md). From 8b8ef1a5bce29ad5010b88b75b35263a7480b61e Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Wed, 10 Dec 2025 09:49:26 +0000 Subject: [PATCH 10/19] Use consistent wording in tree docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../extending-overview/extension-types/tree/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 0beb3049905..5f2582ffbfe 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -107,7 +107,7 @@ Trees need data. You can populate a tree in two ways: ### Option 1: Manual Data (Basic) -For simple, static trees you can implement a basic data source that returns hardcoded or locally-computed items: +For basic, static trees you can implement a basic data source that returns hardcoded or locally-computed items: ```typescript export class MyBasicDataSource implements UmbTreeDataSource { From 2349d666c9a103ca5d7c4c1ec92f93d7406242ac Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Wed, 10 Dec 2025 10:02:05 +0000 Subject: [PATCH 11/19] Add redirects for moved tree documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- 17/umbraco-cms/.gitbook.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/17/umbraco-cms/.gitbook.yaml b/17/umbraco-cms/.gitbook.yaml index 3ed40560275..c418b02c70b 100644 --- a/17/umbraco-cms/.gitbook.yaml +++ b/17/umbraco-cms/.gitbook.yaml @@ -154,4 +154,8 @@ redirects: customizing/extending-overview/extension-types/modals/confirm-dialog: customizing/utilities/modals/confirm-dialog.md customizing/searchable-trees: customizing/overview.md customizing/section-trees: customizing/overview.md + customizing/extending-overview/extension-types/tree: customizing/extending-overview/extension-types/tree/README.md + customizing/extending-overview/extension-types/tree-item: customizing/extending-overview/extension-types/tree/README.md + customizing/extending-overview/extension-types/stores-and-repositories/tree-store: customizing/extending-overview/extension-types/tree/tree-store.md + customizing/extending-overview/foundation/repositories/repository-types/tree-repository.md: customizing/extending-overview/extension-types/tree/tree-repository.md From 89e73d1b11df2231b1b06cf9183ece3f06ce2530 Mon Sep 17 00:00:00 2001 From: Phil Whittaker Date: Mon, 15 Dec 2025 14:28:46 +0000 Subject: [PATCH 12/19] Simplify tree documentation by removing data source and store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove Tree Data Source and Tree Store documentation as these are optional implementation details being phased out. The repository now shows how to call APIs directly, which is the recommended approach for custom trees. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- 17/umbraco-cms/.gitbook.yaml | 2 +- 17/umbraco-cms/SUMMARY.md | 2 - .../extension-types/tree/README.md | 111 ++++++----- .../extension-types/tree/tree-data-source.md | 140 ------------- .../extension-types/tree/tree-models.md | 7 +- .../extension-types/tree/tree-navigation.md | 22 ++- .../extension-types/tree/tree-repository.md | 185 +++++++++++++++--- .../extension-types/tree/tree-store.md | 71 ------- 8 files changed, 238 insertions(+), 302 deletions(-) delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md diff --git a/17/umbraco-cms/.gitbook.yaml b/17/umbraco-cms/.gitbook.yaml index c418b02c70b..0473609271e 100644 --- a/17/umbraco-cms/.gitbook.yaml +++ b/17/umbraco-cms/.gitbook.yaml @@ -156,6 +156,6 @@ redirects: customizing/section-trees: customizing/overview.md customizing/extending-overview/extension-types/tree: customizing/extending-overview/extension-types/tree/README.md customizing/extending-overview/extension-types/tree-item: customizing/extending-overview/extension-types/tree/README.md - customizing/extending-overview/extension-types/stores-and-repositories/tree-store: customizing/extending-overview/extension-types/tree/tree-store.md + customizing/extending-overview/extension-types/stores-and-repositories/tree-store: customizing/extending-overview/extension-types/tree/tree-repository.md customizing/extending-overview/foundation/repositories/repository-types/tree-repository.md: customizing/extending-overview/extension-types/tree/tree-repository.md diff --git a/17/umbraco-cms/SUMMARY.md b/17/umbraco-cms/SUMMARY.md index e57a0b26415..01b759aa0fe 100644 --- a/17/umbraco-cms/SUMMARY.md +++ b/17/umbraco-cms/SUMMARY.md @@ -192,8 +192,6 @@ * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) * [Tree Models](customizing/extending-overview/extension-types/tree/tree-models.md) * [Tree Repository](customizing/extending-overview/extension-types/tree/tree-repository.md) - * [Tree Data Source](customizing/extending-overview/extension-types/tree/tree-data-source.md) - * [Tree Store](customizing/extending-overview/extension-types/tree/tree-store.md) * [Workspaces](customizing/extending-overview/extension-types/workspaces/README.md) * [Workspace Actions](customizing/extending-overview/extension-types/workspaces/workspace-editor-actions.md) * [Workspace Action Menu Items](customizing/extending-overview/extension-types/workspaces/workspace-action-menu-items.md) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 5f2582ffbfe..4ebd16697de 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -13,7 +13,7 @@ description: A guide to creating a custom tree in Umbraco 1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar or elsewhere 2. **[Populating Trees](#populating-trees)** - How to populate tree with data -3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces byt clicking menu items +3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces by clicking menu items {% endhint %} ## Displaying Trees @@ -103,72 +103,93 @@ This is less common than displaying via menus but useful for custom UIs. ## Populating Trees -Trees need data. You can populate a tree in two ways: +Trees get their data from a **Repository**. The repository implements methods to return tree items and is referenced by the tree manifest via `repositoryAlias`. -### Option 1: Manual Data (Basic) +### Repository Implementation -For basic, static trees you can implement a basic data source that returns hardcoded or locally-computed items: +Create a repository that extends `UmbControllerBase` and implements the tree repository interface: ```typescript -export class MyBasicDataSource implements UmbTreeDataSource { - async getRootItems() { - return { - items: [ - { unique: '1', entityType: 'my-item', name: 'Item 1', hasChildren: false, icon: 'icon-document' }, - { unique: '2', entityType: 'my-item', name: 'Item 2', hasChildren: false, icon: 'icon-document' }, - ], - total: 2, - }; +import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; +import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; + +export class MyTreeRepository extends UmbControllerBase implements UmbApi { + async requestTreeRoot() { + return { + data: { + unique: null, + entityType: "my-tree-root", + name: "My Tree", + hasChildren: true, + isFolder: true, + icon: "icon-folder", + }, + }; + } + + async requestTreeRootItems() { + // Call your API here + const response = await MyTreeService.getRoot(); + + const items = response.items.map((item) => ({ + unique: item.id, + entityType: "my-tree-item", + parent: { unique: null, entityType: "my-tree-root" }, + name: item.name, + hasChildren: item.hasChildren, + isFolder: false, + icon: "icon-document", + })); + + return { data: { items, total: response.total } }; + } + + async requestTreeItemsOf(args) { + if (args.parent.unique === null) { + return this.requestTreeRootItems(); } - async getChildrenOf(args) { - return { items: [], total: 0 }; - } - - async getAncestorsOf(args) { - return []; - } + // Call your API for children + const response = await MyTreeService.getChildren({ + parentId: args.parent.unique, + }); + + const items = response.items.map((item) => ({ + unique: item.id, + entityType: "my-tree-item", + parent: { unique: args.parent.unique, entityType: args.parent.entityType }, + name: item.name, + hasChildren: item.hasChildren, + isFolder: false, + icon: "icon-document", + })); + + return { data: { items, total: response.total } }; + } + + async requestTreeItemAncestors() { + return { data: [] }; + } } -``` - -### Option 2: Repository Pattern (Recommended for APIs) -For trees backed by server APIs, use the full repository pattern with caching: - -``` -Repository (coordinates data + caching) - ↓ uses -Data Source (fetches from API) - ↓ caches in -Store (in-memory cache) +export { MyTreeRepository as api }; ``` -Register these in your manifests: +### Register the Repository ```typescript -// Repository - referenced by tree via repositoryAlias { type: 'repository', alias: 'My.Tree.Repository', name: 'My Tree Repository', api: () => import('./my-tree.repository.js'), } - -// Store - caches tree items -{ - type: 'treeStore', - alias: 'My.TreeStore', - name: 'My Tree Store', - api: () => import('./my-tree.store.js'), -} ``` -For detailed implementation guides, see: +For detailed implementation guidance, see: +- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples - **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces -- **[Tree Repository](./tree-repository.md)** - Extends `UmbTreeRepositoryBase` -- **[Tree Data Source](./tree-data-source.md)** - Implements `getRootItems`, `getChildrenOf`, `getAncestorsOf` -- **[Tree Store](./tree-store.md)** - Extends `UmbUniqueTreeStore` --- diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md deleted file mode 100644 index d02c755e72f..00000000000 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-data-source.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -description: Implementing a tree data source to provide tree data in Umbraco ---- - -# Tree Data Source - -The data source is the **only component that communicates with your API**. It implements methods to retrieve root items, children of a specific item, and ancestors of an item. - -{% hint style="info" %} -This is where you call your API endpoints. The [Repository](./tree-repository.md) and [Store](./tree-store.md) never make API calls directlyβ€”all server communication goes through the data source. -{% endhint %} - -## Interface - -The `UmbTreeDataSource` interface defines three methods: - -```typescript -interface UmbTreeDataSource { - getRootItems(args: UmbTreeRootItemsRequestArgs): Promise>; - getChildrenOf(args: UmbTreeChildrenOfRequestArgs): Promise>; - getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs): Promise>; -} -``` - -## Implementing a Data Source - -For most cases, extend `UmbTreeServerDataSourceBase` which handles common tree operations and provides a mapper for transforming API responses. - -{% code title="my-tree.data-source.ts" %} -```typescript -import type { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; -import type { - UmbTreeAncestorsOfRequestArgs, - UmbTreeChildrenOfRequestArgs, - UmbTreeRootItemsRequestArgs, -} from "@umbraco-cms/backoffice/tree"; -import { UmbTreeServerDataSourceBase } from "@umbraco-cms/backoffice/tree"; -import type { MyTreeItemResponseModel } from "../api/index.js"; -import { MyTreeClientService } from "../api/index.js"; -import { - MY_TREE_ITEM_ENTITY_TYPE, - MY_TREE_ROOT_ENTITY_TYPE, - type MyTreeItemModel, -} from "./types.js"; - -export class MyTreeDataSource extends UmbTreeServerDataSourceBase< - MyTreeItemResponseModel, - MyTreeItemModel -> { - constructor(host: UmbControllerHost) { - super(host, { - getRootItems, - getChildrenOf, - getAncestorsOf, - mapper, - }); - } -} - -const getRootItems = async (args: UmbTreeRootItemsRequestArgs) => - await MyTreeClientService.getRoot({ - query: { skip: args.skip, take: args.take }, - }); - -const getChildrenOf = async (args: UmbTreeChildrenOfRequestArgs) => { - if (args.parent?.unique === null) { - return await getRootItems(args); - } else { - return await MyTreeClientService.getChildren({ - query: { parent: args.parent.unique }, - }); - } -}; - -const getAncestorsOf = async (args: UmbTreeAncestorsOfRequestArgs) => { - return await MyTreeClientService.getAncestors({ - query: { id: args.treeItem.unique }, - }); -}; - -const mapper = (item: MyTreeItemResponseModel): MyTreeItemModel => { - return { - unique: item.id ?? "", - parent: { unique: "", entityType: MY_TREE_ROOT_ENTITY_TYPE }, - name: item.name ?? "unknown", - entityType: MY_TREE_ITEM_ENTITY_TYPE, - hasChildren: item.hasChildren, - isFolder: false, - icon: item.icon ?? "icon-document", - }; -}; -``` -{% endcode %} - -## Method Details - -### getRootItems - -Fetches the top-level items in the tree. Receives pagination arguments (`skip`, `take`). - -### getChildrenOf - -Fetches children of a specific parent item. The `args.parent` contains the parent's `unique` identifier and `entityType`. - -{% hint style="info" %} -When `args.parent.unique` is `null`, it typically means the root level, so you may delegate to `getRootItems`. -{% endhint %} - -### getAncestorsOf - -Returns the ancestor path for a given item. Used for breadcrumb navigation and expanding the tree to a specific item. - -## The Mapper Function - -The mapper transforms your API response model into the tree item model format Umbraco expects: - -```typescript -interface UmbTreeItemModel { - unique: string; // Unique identifier - entityType: string; // Must match workspace entityType for navigation - name: string; // Display name - hasChildren: boolean; // Shows expand arrow if true - isFolder: boolean; // Visual styling hint - icon?: string; // Icon to display - parent?: { // Parent reference - unique: string; - entityType: string; - }; -} -``` - -{% hint style="warning" %} -The `entityType` in your tree item model must match the `entityType` in your workspace manifest for tree item clicks to navigate correctly. -{% endhint %} - -## Related - -- [Tree Repository](./tree-repository.md) - Uses the data source -- [Tree Store](./tree-store.md) - Caches tree items -- [Trees](./README.md) - Main tree extension documentation diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md index c6e01bc8071..c04cdffed13 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -4,7 +4,7 @@ description: Understanding tree item and root models in Umbraco # Tree Models -Trees use two model types to represent data: **Tree Item Model** for individual nodes and **Tree Root Model** for the root node. Your [Data Source](./tree-data-source.md) transforms API responses into these models. +Trees use two model types to represent data: **Tree Item Model** for individual nodes and **Tree Root Model** for the root node. Your [Repository](./tree-repository.md) returns these models from its methods. ## UmbTreeItemModel @@ -39,7 +39,7 @@ export interface MyTreeItemModel extends UmbTreeItemModel { } ``` -Your [Data Source mapper](./tree-data-source.md#the-mapper-function) populates these properties from your API response. +Your repository methods transform API responses into these models when returning data. ## UmbTreeRootModel @@ -97,6 +97,5 @@ The `entityType` values must match your workspace and tree item manifests. See [ ## Related -- [Tree Data Source](./tree-data-source.md) - Transforms API responses into tree models -- [Tree Repository](./tree-repository.md) - Returns root model via `requestTreeRoot()` +- [Tree Repository](./tree-repository.md) - Returns tree models from its methods - [Tree Navigation](./tree-navigation.md) - How `entityType` connects to workspaces diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md index da7edeb39d5..680a591beda 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md @@ -68,14 +68,20 @@ For tree navigation to work correctly, your workspace must use `kind: 'routable' Here's how tree items, tree item manifests, and workspaces connect: ```typescript -// 1. Your data source returns items with entityType -const mapper = (item: ApiResponse): MyTreeItemModel => ({ - unique: item.id, - entityType: 'my-custom-item', // This links to the workspace - name: item.name, - hasChildren: false, - icon: 'icon-document', -}); +// 1. Your repository returns items with entityType +async requestTreeRootItems() { + const response = await MyTreeService.getRoot(); + + const items = response.items.map((item) => ({ + unique: item.id, + entityType: 'my-custom-item', // This links to the workspace + name: item.name, + hasChildren: false, + icon: 'icon-document', + })); + + return { data: { items, total: response.total } }; +} // 2. Tree item manifest handles this entityType { diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index 2de4cc56fac..09fad6324ee 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -1,61 +1,104 @@ # Tree Repository -A tree repository orchestrates tree data operations. It coordinates between a **Data Source** (which fetches from APIs) and a **Store** (which caches data in memory). +A tree repository provides data to populate your tree. It implements methods to return the root, root items, children of items, and ancestors. -{% hint style="info" %} -The repository delegates all API communication to the [Data Source](./tree-data-source.md). It handles caching through the [Store](./tree-store.md). -{% endhint %} +The repository is referenced by your tree manifest via `meta.repositoryAlias`. -The interface below is simplified for clarity and omits return types and arguments. See full interfaces in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). +## Interface + +The `UmbTreeRepository` interface defines the methods your repository must implement: ```typescript interface UmbTreeRepository { requestTreeRoot(); requestTreeRootItems(); - requestTreeItemsOf(); - requestTreeItemAncestors(); + requestTreeItemsOf(args); + requestTreeItemAncestors(args); } ``` -## Implementing a Tree Repository +See the full interface in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). -To implement a tree repository, extend the `UmbTreeRepositoryBase` class. The base class requires: +## Implementing a Tree Repository -1. A **Data Source** - Fetches tree data from your API -2. A **Store Context** - Caches tree items for performance +Extend `UmbControllerBase` and implement the `UmbTreeRepository` interface. Call your API directly within the repository methods: {% code title="my-tree.repository.ts" %} ```typescript -import type { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; +import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; -import { UmbTreeRepositoryBase } from "@umbraco-cms/backoffice/tree"; -import { MY_TREE_STORE_CONTEXT } from "./my-tree.store.js"; -import { MyTreeDataSource } from "./my-tree.data-source.js"; +import type { + UmbTreeChildrenOfRequestArgs, + UmbTreeRepository, + UmbTreeRootModel, +} from "@umbraco-cms/backoffice/tree"; +import { MyTreeService } from "./api/index.js"; import { + MY_TREE_ITEM_ENTITY_TYPE, MY_TREE_ROOT_ENTITY_TYPE, type MyTreeItemModel, - type MyTreeRootModel, } from "./types.js"; export class MyTreeRepository - extends UmbTreeRepositoryBase - implements UmbApi + extends UmbControllerBase + implements UmbTreeRepository, UmbApi { - constructor(host: UmbControllerHost) { - super(host, MyTreeDataSource, MY_TREE_STORE_CONTEXT); - } - async requestTreeRoot() { - const data: MyTreeRootModel = { + const root: UmbTreeRootModel = { unique: null, entityType: MY_TREE_ROOT_ENTITY_TYPE, - name: "My Tree Root", - icon: "icon-folder", + name: "My Items", hasChildren: true, isFolder: true, + icon: "icon-folder", }; - return { data }; + return { data: root }; + } + + async requestTreeRootItems() { + const response = await MyTreeService.getRoot(); + + const items: Array = response.items.map((item) => ({ + unique: item.id, + entityType: MY_TREE_ITEM_ENTITY_TYPE, + parent: { unique: null, entityType: MY_TREE_ROOT_ENTITY_TYPE }, + name: item.name, + hasChildren: item.hasChildren, + isFolder: false, + icon: "icon-document", + })); + + return { data: { items, total: response.total } }; + } + + async requestTreeItemsOf(args: UmbTreeChildrenOfRequestArgs) { + if (args.parent.unique === null) { + return this.requestTreeRootItems(); + } + + const response = await MyTreeService.getChildren({ + parentId: args.parent.unique, + }); + + const items: Array = response.items.map((item) => ({ + unique: item.id, + entityType: MY_TREE_ITEM_ENTITY_TYPE, + parent: { + unique: args.parent.unique, + entityType: args.parent.entityType, + }, + name: item.name, + hasChildren: item.hasChildren, + isFolder: false, + icon: "icon-document", + })); + + return { data: { items, total: response.total } }; + } + + async requestTreeItemAncestors() { + return { data: [] }; } } @@ -63,7 +106,7 @@ export { MyTreeRepository as api }; ``` {% endcode %} -### Registering the Repository +## Registering the Repository Register the repository in your manifest: @@ -76,10 +119,90 @@ Register the repository in your manifest: } ``` -The repository alias is referenced by your tree manifest via `meta.repositoryAlias`. +The tree manifest references this via `repositoryAlias`: + +```typescript +{ + type: 'tree', + alias: 'My.Tree', + name: 'My Tree', + meta: { + repositoryAlias: 'My.Tree.Repository', + }, +} +``` + +## Static Data Example + +For trees with static or hardcoded data, you can return items directly without API calls: + +{% code title="static-tree.repository.ts" %} +```typescript +import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; +import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; +import type { UmbTreeRepository } from "@umbraco-cms/backoffice/tree"; + +const staticItems = [ + { + unique: "1", + entityType: "my-item", + parent: { unique: null, entityType: "my-root" }, + name: "First Item", + hasChildren: false, + isFolder: false, + icon: "icon-document", + }, + { + unique: "2", + entityType: "my-item", + parent: { unique: null, entityType: "my-root" }, + name: "Second Item", + hasChildren: false, + isFolder: false, + icon: "icon-document", + }, +]; + +export class StaticTreeRepository + extends UmbControllerBase + implements UmbTreeRepository, UmbApi +{ + async requestTreeRoot() { + return { + data: { + unique: null, + entityType: "my-root", + name: "My Static Tree", + hasChildren: true, + isFolder: true, + icon: "icon-folder", + }, + }; + } + + async requestTreeRootItems() { + const items = staticItems.filter((item) => item.parent.unique === null); + return { data: { items, total: items.length } }; + } + + async requestTreeItemsOf(args) { + const items = staticItems.filter( + (item) => item.parent.unique === args.parent.unique + ); + return { data: { items, total: items.length } }; + } + + async requestTreeItemAncestors() { + return { data: [] }; + } +} + +export { StaticTreeRepository as api }; +``` +{% endcode %} ## Related -- [Tree Data Source](./tree-data-source.md) - Implements data fetching methods -- [Tree Store](./tree-store.md) - Caches tree items -- [Trees](./README.md) - Main tree extension documentation \ No newline at end of file +- [Tree Models](./tree-models.md) - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces +- [Tree Navigation](./tree-navigation.md) - How tree clicks navigate to workspaces +- [Trees](./README.md) - Main tree extension documentation diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md deleted file mode 100644 index f0bf5b5b63c..00000000000 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-store.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -description: Implementing a tree store to cache tree data in Umbraco ---- - -# Tree Store - -A tree store caches tree items in memory, improving performance by avoiding repeated API calls for the same data. The store is used by the tree repository to manage tree item state. - -{% hint style="info" %} -The store does not communicate with APIs. It only holds cached data. All API communication happens in the [Data Source](./tree-data-source.md). -{% endhint %} - -## Implementing a Tree Store - -Extend `UmbUniqueTreeStore` and create a context token for dependency injection: - -{% code title="my-tree.store.ts" %} -```typescript -import { UmbContextToken } from "@umbraco-cms/backoffice/context-api"; -import { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; -import { UmbUniqueTreeStore } from "@umbraco-cms/backoffice/tree"; - -export class MyTreeStore extends UmbUniqueTreeStore { - constructor(host: UmbControllerHost) { - super(host, MY_TREE_STORE_CONTEXT.toString()); - } -} - -export { MyTreeStore as api }; - -export const MY_TREE_STORE_CONTEXT = new UmbContextToken( - "MY_TREE_STORE_CONTEXT" -); -``` -{% endcode %} - -## Registering the Store - -Register the store in your manifest alongside your tree: - -```typescript -{ - type: 'treeStore', - alias: 'My.TreeStore', - name: 'My Tree Store', - api: () => import('./my-tree.store.js'), -} -``` - -## Using the Store - -The store context is passed to the tree repository constructor: - -```typescript -export class MyTreeRepository extends UmbTreeRepositoryBase { - constructor(host: UmbControllerHost) { - super(host, MyTreeDataSource, MY_TREE_STORE_CONTEXT); - } -} -``` - -The base repository class handles all store operations automatically: -- Caching fetched items -- Retrieving cached items before making API calls -- Updating cached items when data changes - -## Related - -- [Tree Repository](./tree-repository.md) - Uses the store for caching -- [Tree Data Source](./tree-data-source.md) - Fetches data that gets cached -- [Trees](./README.md) - Main tree extension documentation From f10604e866bb31b791ae462ddb246f60d741b11a Mon Sep 17 00:00:00 2001 From: sofietoft Date: Wed, 17 Dec 2025 08:53:05 +0100 Subject: [PATCH 13/19] Fix formatting and punctuation in README.md --- .../extension-types/tree/README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 4ebd16697de..dd600933ba4 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -11,16 +11,16 @@ description: A guide to creating a custom tree in Umbraco {% hint style="warning" %} **Trees are data providers, not UI components.** A tree on its own does nothing visible. To create a working tree in the backoffice, you need to understand three things: -1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar or elsewhere -2. **[Populating Trees](#populating-trees)** - How to populate tree with data -3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces by clicking menu items +1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar or elsewhere. +2. **[Populating Trees](#populating-trees)** - How to populate tree with data. +3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces by clicking menu items. {% endhint %} ## Displaying Trees Trees provide hierarchical data to **Menus**, which display the actual navigation you see in the backoffice sidebar. To display a tree, you need to connect multiple extensions: -``` +```none Tree (data provider) ↓ referenced by MenuItem (kind: 'tree', treeAlias: 'My.Tree') @@ -50,7 +50,7 @@ Register your tree with a manifest. The `repositoryAlias` links to how the tree ### Tree Item Manifest -Tree items define how individual items render. Use `kind: 'default'` for standard rendering: +Tree-items define how individual items render. Use `kind: 'default'` for standard rendering: ```typescript { @@ -97,7 +97,7 @@ Trees can also be rendered directly in custom components using the `` ``` -This is less common than displaying via menus but useful for custom UIs. +This is less common than displaying via menus, but is useful for custom UIs. --- @@ -188,8 +188,8 @@ export { MyTreeRepository as api }; For detailed implementation guidance, see: -- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples -- **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces +- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples. +- **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. --- @@ -203,5 +203,5 @@ See **[Tree Navigation & Workspaces](./tree-navigation.md)** for setup details a ## Further Reading -- [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) - Working examples in the Umbraco repository -- [Workspaces](../workspaces/README.md) - Creating workspace extensions +- [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) - Working examples in the Umbraco repository. +- [Workspaces](../workspaces/README.md) - Creating workspace extensions. From b46ffafaee4b3ecb09aa0e0fed1a465f80e3a83c Mon Sep 17 00:00:00 2001 From: sofietoft Date: Wed, 17 Dec 2025 08:53:58 +0100 Subject: [PATCH 14/19] Fix grammar and punctuation in tree-models.md --- .../extension-types/tree/tree-models.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md index c04cdffed13..ad1217d737d 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -8,7 +8,7 @@ Trees use two model types to represent data: **Tree Item Model** for individual ## UmbTreeItemModel -The base interface for tree items. All tree items must include these properties: +The base interface for tree items. All tree-items must include these properties: ```typescript interface UmbTreeItemModel { @@ -92,10 +92,10 @@ export interface MyTreeItemModel extends UmbTreeItemModel { ``` {% hint style="info" %} -The `entityType` values must match your workspace and tree item manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. +The `entityType` values must match the values in your workspace and tree item manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. {% endhint %} ## Related -- [Tree Repository](./tree-repository.md) - Returns tree models from its methods -- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to workspaces +- [Tree Repository](./tree-repository.md) - Returns tree models from its methods. +- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to workspaces. From 6484b08ba82dc83eab4bc44ed21229158b1f92d1 Mon Sep 17 00:00:00 2001 From: sofietoft Date: Wed, 17 Dec 2025 08:55:46 +0100 Subject: [PATCH 15/19] Fix grammar and punctuation in tree-navigation.md --- .../extension-types/tree/tree-navigation.md | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md index 680a591beda..37dc935c3dd 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md @@ -4,15 +4,15 @@ description: How tree items navigate to workspaces when clicked in Umbraco # Tree Navigation & Workspaces -Trees and workspaces are tightly coupled. When users click a tree item, Umbraco navigates to a workspace to edit that item. This connection is made through the `entityType` - a string identifier that links tree items to their corresponding workspace. +Trees and workspaces are tightly coupled. When users click a tree item, Umbraco navigates to a workspace to edit that item. This connection is established through the `entityType`, a string identifier that links tree items to their corresponding workspace. ## How Tree Items Connect to Workspaces When you click a tree item: -1. Umbraco reads the `entityType` from the tree item data -2. It looks for a workspace registered with a matching `meta.entityType` -3. It navigates to that workspace, passing the item's `unique` identifier +1. Umbraco reads the `entityType` from the tree item data. +2. It looks for a workspace registered with a matching `meta.entityType`. +3. It navigates to that workspace, passing the items `unique` identifier. ``` Tree Item clicked (entityType: 'my-custom-item', unique: '123') @@ -49,18 +49,22 @@ For tree navigation to work correctly, your workspace must use `kind: 'routable' {% hint style="info" %} **Why `kind: 'routable'`?** Routable workspaces generate proper URLs and handle navigation state. This allows: -- Direct linking to specific items -- Browser back/forward navigation -- Correct tree item selection highlighting when switching between items + +- Direct linking to specific items. +- Browser back/forward navigation. +- Correct tree item selection highlighting when switching between items. +- {% endhint %} ## Common Issues {% hint style="warning" %} **Endless loading when clicking tree items?** This usually means: -- No workspace is registered for that `entityType` -- The `entityType` in your tree data doesn't match the workspace's `meta.entityType` -- The workspace is using `kind: 'default'` instead of `kind: 'routable'` + +- No workspace is registered for that `entityType`. +- The `entityType` in your tree data doesn't match the workspace's `meta.entityType`. +- The workspace is using `kind: 'default'` instead of `kind: 'routable'`. +- {% endhint %} ## Complete Example @@ -105,5 +109,5 @@ async requestTreeRootItems() { ## Related -- [Trees](./README.md) - Main tree extension documentation -- [Workspaces](../workspace/README.md) - Creating workspace extensions +- [Trees](./README.md) - Main tree extension documentation. +- [Workspaces](../workspace/README.md) - Creating workspace extensions. From 35b8cbcb063d47eb90ca07749148cf74084d6a32 Mon Sep 17 00:00:00 2001 From: sofietoft Date: Wed, 17 Dec 2025 08:57:13 +0100 Subject: [PATCH 16/19] Fix formatting of related links in tree-repository.md --- .../extension-types/tree/tree-repository.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index 09fad6324ee..d2c5d43a2b0 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -203,6 +203,6 @@ export { StaticTreeRepository as api }; ## Related -- [Tree Models](./tree-models.md) - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces -- [Tree Navigation](./tree-navigation.md) - How tree clicks navigate to workspaces -- [Trees](./README.md) - Main tree extension documentation +- [Tree Models](./tree-models.md) - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. +- [Tree Navigation](./tree-navigation.md) - How tree clicks navigate to workspaces. +- [Trees](./README.md) - Main tree extension documentation. From d4b67ff6be9864b288fcbf2e502f8f5d651f97b6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 17 Dec 2025 12:53:39 +0100 Subject: [PATCH 17/19] adjust article flow - tree data provisioning before display --- .../extension-types/tree/README.md | 180 ++++++------------ .../extension-types/tree/tree-navigation.md | 113 ----------- .../extension-types/tree/tree-repository.md | 111 +---------- .../tree/trees-and-workspaces.md | 55 ++++++ 4 files changed, 119 insertions(+), 340 deletions(-) delete mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md create mode 100644 17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index dd600933ba4..30b3c0f8a5d 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -9,29 +9,50 @@ description: A guide to creating a custom tree in Umbraco {% endhint %} {% hint style="warning" %} -**Trees are data providers, not UI components.** A tree on its own does nothing visible. To create a working tree in the backoffice, you need to understand three things: +To create a working Tree in the Backoffice, you need to understand three things: -1. **[Displaying Trees](#displaying-trees)** - How trees connect to menus to appear in the sidebar or elsewhere. -2. **[Populating Trees](#populating-trees)** - How to populate tree with data. -3. **[Tree Navigation](#tree-navigation)** - How to navigate to workspaces by clicking menu items. +1. **[Populating Trees](#populating-trees)** - How to populate a Tree with data. +2. **[Displaying Trees](#displaying-trees)** - How to display Trees and connect them to Menus to appear in the Section Sidebar or elsewhere. +3. **[Trees and Workspaces](#trees-and-workspaces)** - How to navigate to Workspaces by clicking Menu Items. {% endhint %} -## Displaying Trees +## Populating Trees + +Trees get their data from a **Repository**. The Repository implements methods to return Tree Items and is referenced by the Tree Manifest via `repositoryAlias`. + +### Register the Repository + +```typescript +{ + type: 'repository', + alias: 'My.Tree.Repository', + name: 'My Tree Repository', + api: () => import('./my-tree.repository.js'), +} +``` -Trees provide hierarchical data to **Menus**, which display the actual navigation you see in the backoffice sidebar. To display a tree, you need to connect multiple extensions: - -```none -Tree (data provider) - ↓ referenced by -MenuItem (kind: 'tree', treeAlias: 'My.Tree') - ↓ belongs to -Menu - ↓ displayed by -SectionSidebarApp - ↓ appears in -Section (Content, Media, Settings, or custom) +### Repository Implementation + +Create a Repository that implements the `TreeRepository` interface. The interface below is simplified for clarity and omits return types and arguments. See full interfaces in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html) + +```typescript +interface UmbTreeRepository { + requestTreeRoot(); + requestTreeRootItems(); + requestTreeItemsOf(); + requestTreeItemAncestors(); +} ``` +For detailed implementation guidance, see: + +- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples. +- **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. + +--- + +## Displaying Trees + ### Tree Manifest Register your tree with a manifest. The `repositoryAlias` links to how the tree gets its data: @@ -65,9 +86,18 @@ Tree-items define how individual items render. Use `kind: 'default'` for standar Include both your root entity type and item entity type in `forEntityTypes` so the tree item renderer handles all nodes in your tree. {% endhint %} +### Standalone Rendering + +Trees can be rendered directly in custom components using the `` element: + +```html + +``` + ### Connecting to a Menu -To display your tree in a section sidebar, create a MenuItem with `kind: 'tree'`: +Trees can also provide hierarchical data to **Menu Items**, which display the navigation you see in the Backoffice Section Sidebar. +To register your Tree-based Menu Item use the `kind: 'tree'` in the Menu Item Manifest. ```typescript { @@ -80,124 +110,28 @@ To display your tree in a section sidebar, create a MenuItem with `kind: 'tree'` label: 'My Tree', icon: 'icon-folder', entityType: 'my-tree-root', - menus: ['My.Menu'], + menus: ['My.Menu'] // The Menu alias where this item should appear, treeAlias: 'My.Tree', hideTreeRoot: true, // Optional: show items at root level }, } ``` -See [Menu Items (Tree kind)](../menu-item.md#tree) and [Section Sidebar](../sections/section-sidebar.md) for the complete setup. - -### Standalone Rendering - -Trees can also be rendered directly in custom components using the `` element: - -```html - -``` - -This is less common than displaying via menus, but is useful for custom UIs. - ---- - -## Populating Trees - -Trees get their data from a **Repository**. The repository implements methods to return tree items and is referenced by the tree manifest via `repositoryAlias`. - -### Repository Implementation - -Create a repository that extends `UmbControllerBase` and implements the tree repository interface: - -```typescript -import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; -import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; - -export class MyTreeRepository extends UmbControllerBase implements UmbApi { - async requestTreeRoot() { - return { - data: { - unique: null, - entityType: "my-tree-root", - name: "My Tree", - hasChildren: true, - isFolder: true, - icon: "icon-folder", - }, - }; - } - - async requestTreeRootItems() { - // Call your API here - const response = await MyTreeService.getRoot(); - - const items = response.items.map((item) => ({ - unique: item.id, - entityType: "my-tree-item", - parent: { unique: null, entityType: "my-tree-root" }, - name: item.name, - hasChildren: item.hasChildren, - isFolder: false, - icon: "icon-document", - })); - - return { data: { items, total: response.total } }; - } - - async requestTreeItemsOf(args) { - if (args.parent.unique === null) { - return this.requestTreeRootItems(); - } - - // Call your API for children - const response = await MyTreeService.getChildren({ - parentId: args.parent.unique, - }); - - const items = response.items.map((item) => ({ - unique: item.id, - entityType: "my-tree-item", - parent: { unique: args.parent.unique, entityType: args.parent.entityType }, - name: item.name, - hasChildren: item.hasChildren, - isFolder: false, - icon: "icon-document", - })); - - return { data: { items, total: response.total } }; - } - - async requestTreeItemAncestors() { - return { data: [] }; - } -} - -export { MyTreeRepository as api }; -``` - -### Register the Repository - -```typescript -{ - type: 'repository', - alias: 'My.Tree.Repository', - name: 'My Tree Repository', - api: () => import('./my-tree.repository.js'), -} -``` +Examples of built-in menus include: -For detailed implementation guidance, see: +* Content - `Umb.Menu.Content` +* Media - `Umb.Menu.Media` +* Advanced Settings - `Umb.Menu.AdvancedSettings` -- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples. -- **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. +See [Menu Items (Tree kind)](../menu-item.md#tree) and [Section Sidebar](../sections/section-sidebar.md) for the complete setup. --- -## Tree Navigation +## Trees and Workspaces -When users click a tree item, Umbraco navigates to a **workspace** to edit that item. The `entityType` in your tree items must match the `meta.entityType` in your workspace manifest. +When users click a Tree Item, Umbraco navigates to a **Workspace** to edit that item. The `entityType` in your Tree Items must match the `meta.entityType` in your Workspace Manifest. -See **[Tree Navigation & Workspaces](./tree-navigation.md)** for setup details and troubleshooting. +See **[Trees & Workspaces](./trees-and-workspaces.md)** for setup details and troubleshooting. --- diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md deleted file mode 100644 index 37dc935c3dd..00000000000 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-navigation.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -description: How tree items navigate to workspaces when clicked in Umbraco ---- - -# Tree Navigation & Workspaces - -Trees and workspaces are tightly coupled. When users click a tree item, Umbraco navigates to a workspace to edit that item. This connection is established through the `entityType`, a string identifier that links tree items to their corresponding workspace. - -## How Tree Items Connect to Workspaces - -When you click a tree item: - -1. Umbraco reads the `entityType` from the tree item data. -2. It looks for a workspace registered with a matching `meta.entityType`. -3. It navigates to that workspace, passing the items `unique` identifier. - -``` -Tree Item clicked (entityType: 'my-custom-item', unique: '123') - ↓ -Workspace found (meta.entityType: 'my-custom-item') - ↓ -Navigation to: /section/my-section/workspace/my-custom-item/edit/123 -``` - -## Workspace Kind: Routable vs Default - -For tree navigation to work correctly, your workspace must use `kind: 'routable'`: - -```typescript -// Tree item manifest -{ - type: 'treeItem', - kind: 'default', - alias: 'My.TreeItem', - forEntityTypes: ['my-custom-item'], -} - -// Workspace manifest - MUST be routable for tree navigation -{ - type: 'workspace', - kind: 'routable', - alias: 'My.Workspace', - name: 'My Custom Item Workspace', - meta: { - entityType: 'my-custom-item', // Must match tree item entityType - }, -} -``` - -{% hint style="info" %} -**Why `kind: 'routable'`?** Routable workspaces generate proper URLs and handle navigation state. This allows: - -- Direct linking to specific items. -- Browser back/forward navigation. -- Correct tree item selection highlighting when switching between items. -- -{% endhint %} - -## Common Issues - -{% hint style="warning" %} -**Endless loading when clicking tree items?** This usually means: - -- No workspace is registered for that `entityType`. -- The `entityType` in your tree data doesn't match the workspace's `meta.entityType`. -- The workspace is using `kind: 'default'` instead of `kind: 'routable'`. -- -{% endhint %} - -## Complete Example - -Here's how tree items, tree item manifests, and workspaces connect: - -```typescript -// 1. Your repository returns items with entityType -async requestTreeRootItems() { - const response = await MyTreeService.getRoot(); - - const items = response.items.map((item) => ({ - unique: item.id, - entityType: 'my-custom-item', // This links to the workspace - name: item.name, - hasChildren: false, - icon: 'icon-document', - })); - - return { data: { items, total: response.total } }; -} - -// 2. Tree item manifest handles this entityType -{ - type: 'treeItem', - kind: 'default', - alias: 'My.TreeItem', - forEntityTypes: ['my-custom-item'], -} - -// 3. Workspace manifest receives navigation for this entityType -{ - type: 'workspace', - kind: 'routable', - alias: 'My.Workspace', - name: 'My Workspace', - meta: { - entityType: 'my-custom-item', - }, -} -``` - -## Related - -- [Trees](./README.md) - Main tree extension documentation. -- [Workspaces](../workspace/README.md) - Creating workspace extensions. diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index d2c5d43a2b0..fa82c970e4d 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -1,6 +1,6 @@ # Tree Repository -A tree repository provides data to populate your tree. It implements methods to return the root, root items, children of items, and ancestors. +A Tree Repository provides data to populate your Tree. It implements methods to return the root, root items, children of items, and ancestors. The repository is referenced by your tree manifest via `meta.repositoryAlias`. @@ -21,94 +21,11 @@ See the full interface in the [UI API Documentation](https://apidocs.umbraco.com ## Implementing a Tree Repository -Extend `UmbControllerBase` and implement the `UmbTreeRepository` interface. Call your API directly within the repository methods: - -{% code title="my-tree.repository.ts" %} -```typescript -import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; -import type { UmbApi } from "@umbraco-cms/backoffice/extension-api"; -import type { - UmbTreeChildrenOfRequestArgs, - UmbTreeRepository, - UmbTreeRootModel, -} from "@umbraco-cms/backoffice/tree"; -import { MyTreeService } from "./api/index.js"; -import { - MY_TREE_ITEM_ENTITY_TYPE, - MY_TREE_ROOT_ENTITY_TYPE, - type MyTreeItemModel, -} from "./types.js"; - -export class MyTreeRepository - extends UmbControllerBase - implements UmbTreeRepository, UmbApi -{ - async requestTreeRoot() { - const root: UmbTreeRootModel = { - unique: null, - entityType: MY_TREE_ROOT_ENTITY_TYPE, - name: "My Items", - hasChildren: true, - isFolder: true, - icon: "icon-folder", - }; - - return { data: root }; - } - - async requestTreeRootItems() { - const response = await MyTreeService.getRoot(); - - const items: Array = response.items.map((item) => ({ - unique: item.id, - entityType: MY_TREE_ITEM_ENTITY_TYPE, - parent: { unique: null, entityType: MY_TREE_ROOT_ENTITY_TYPE }, - name: item.name, - hasChildren: item.hasChildren, - isFolder: false, - icon: "icon-document", - })); - - return { data: { items, total: response.total } }; - } - - async requestTreeItemsOf(args: UmbTreeChildrenOfRequestArgs) { - if (args.parent.unique === null) { - return this.requestTreeRootItems(); - } - - const response = await MyTreeService.getChildren({ - parentId: args.parent.unique, - }); - - const items: Array = response.items.map((item) => ({ - unique: item.id, - entityType: MY_TREE_ITEM_ENTITY_TYPE, - parent: { - unique: args.parent.unique, - entityType: args.parent.entityType, - }, - name: item.name, - hasChildren: item.hasChildren, - isFolder: false, - icon: "icon-document", - })); - - return { data: { items, total: response.total } }; - } - - async requestTreeItemAncestors() { - return { data: [] }; - } -} - -export { MyTreeRepository as api }; -``` -{% endcode %} +Extend `UmbControllerBase` and implement the `UmbTreeRepository` interface. ## Registering the Repository -Register the repository in your manifest: +Register the Repository in your manifest: ```typescript { @@ -119,23 +36,8 @@ Register the repository in your manifest: } ``` -The tree manifest references this via `repositoryAlias`: - -```typescript -{ - type: 'tree', - alias: 'My.Tree', - name: 'My Tree', - meta: { - repositoryAlias: 'My.Tree.Repository', - }, -} -``` - ## Static Data Example -For trees with static or hardcoded data, you can return items directly without API calls: - {% code title="static-tree.repository.ts" %} ```typescript import { UmbControllerBase } from "@umbraco-cms/backoffice/class-api"; @@ -163,9 +65,9 @@ const staticItems = [ }, ]; -export class StaticTreeRepository +export class MyStaticTreeRepository extends UmbControllerBase - implements UmbTreeRepository, UmbApi + implements UmbTreeRepository, UmbApi { async requestTreeRoot() { return { @@ -193,11 +95,12 @@ export class StaticTreeRepository } async requestTreeItemAncestors() { + // Implement as needed return { data: [] }; } } -export { StaticTreeRepository as api }; +export { MyStaticTreeRepository as api }; ``` {% endcode %} diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md new file mode 100644 index 00000000000..24894f21dc2 --- /dev/null +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md @@ -0,0 +1,55 @@ +--- +description: How tree items navigate to workspaces when clicked in Umbraco +--- + +# Trees & Workspaces + +Trees and workspaces are tightly coupled. When users click a Tree Item, Umbraco navigates to a workspace to edit that item. This connection is established through the `entityType`, a string identifier that links Tree Items to their corresponding Workspace. + +## How Tree Items Connect to Workspaces + +When you click a Tree Item: + +1. Umbraco reads the `entityType` from the Tree Item data. +2. It navigates to the edit Workspace URL, passing the items `unique` identifier. + +## Workspace Kind: Routable vs Default + +To support different routes for new vs existing items, your workspace must be routable to have different routes for each case. Use `kind: 'routable'` and set up matching routes in your Workspace API: + +```typescript +// Tree item manifest +{ + type: 'treeItem', + kind: 'default', + alias: 'My.TreeItem', + forEntityTypes: ['my-custom-item'], +} + +// Workspace manifest - MUST be routable for tree navigation +{ + type: 'workspace', + kind: 'routable', + alias: 'My.Workspace', + name: 'My Custom Item Workspace', + api: () => import('./my-custom-item-workspace.api.js'), + meta: { + entityType: 'my-custom-item', // Must match tree item entityType + }, +} +``` + +## Common Issues + +{% hint style="warning" %} +**Endless loading when clicking Tree Items?** This usually means: + +- No workspace is registered for that `entityType`. +- The `entityType` in your tree data doesn't match the workspace's `meta.entityType`. +- +{% endhint %} + +## Related + +- [Trees](./README.md) - Main tree extension documentation. +- [Workspaces](../workspace/README.md) - Creating workspace extensions. From 9145b7f7a77bbdec0030b3c6ee2cce67479c020a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 17 Dec 2025 13:06:53 +0100 Subject: [PATCH 18/19] capitalize named concepts --- .../extension-types/tree/README.md | 14 ++++++------ .../extension-types/tree/tree-models.md | 14 ++++++------ .../extension-types/tree/tree-repository.md | 22 +++++++++---------- .../tree/trees-and-workspaces.md | 20 ++++++++--------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md index 30b3c0f8a5d..6e73353f6ec 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/README.md @@ -1,11 +1,11 @@ --- -description: A guide to creating a custom tree in Umbraco +description: A guide to creating a custom Tree in Umbraco --- # Trees {% hint style="info" %} -**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For basic, static navigation you can use menu items alone. Trees are for **data-driven hierarchical structures** - use them when your menu needs to display dynamic content from an API. +**New to sidebar navigation?** Read [Menus](../menu.md) and [Menu Items](../menu-item.md) first. For basic, static navigation you can use Menu Items alone. Trees are for **data-driven hierarchical structures** - use them when your Menu needs to display dynamic content from an API. {% endhint %} {% hint style="warning" %} @@ -46,7 +46,7 @@ interface UmbTreeRepository { For detailed implementation guidance, see: -- **[Tree Repository](./tree-repository.md)** - Full repository implementation with API examples. +- **[Tree Repository](./tree-repository.md)** - Full repository implementation with static data example. - **[Tree Models](./tree-models.md)** - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. --- @@ -55,7 +55,7 @@ For detailed implementation guidance, see: ### Tree Manifest -Register your tree with a manifest. The `repositoryAlias` links to how the tree gets its data: +Register your tree with a Manifest. The `repositoryAlias` links to how the Tree gets its data: ```typescript { @@ -83,7 +83,7 @@ Tree-items define how individual items render. Use `kind: 'default'` for standar ``` {% hint style="info" %} -Include both your root entity type and item entity type in `forEntityTypes` so the tree item renderer handles all nodes in your tree. +Include both your root entity type and item entity type in `forEntityTypes` so the Tree Item renderer handles all nodes in your Tree. {% endhint %} ### Standalone Rendering @@ -117,7 +117,7 @@ To register your Tree-based Menu Item use the `kind: 'tree'` in the Menu Item Ma } ``` -Examples of built-in menus include: +Examples of built-in Menus include: * Content - `Umb.Menu.Content` * Media - `Umb.Menu.Media` @@ -138,4 +138,4 @@ See **[Trees & Workspaces](./trees-and-workspaces.md)** for setup details and tr ## Further Reading - [Umbraco UI Examples - Trees](https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/examples/tree) - Working examples in the Umbraco repository. -- [Workspaces](../workspaces/README.md) - Creating workspace extensions. +- [Workspaces](../workspaces/README.md) - Creating Workspace extensions. diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md index ad1217d737d..30266eb96f7 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -1,5 +1,5 @@ --- -description: Understanding tree item and root models in Umbraco +description: Understanding Tree Item and Root models in Umbraco --- # Tree Models @@ -8,12 +8,12 @@ Trees use two model types to represent data: **Tree Item Model** for individual ## UmbTreeItemModel -The base interface for tree items. All tree-items must include these properties: +The base interface for Tree Items. All Tree Items must include these properties: ```typescript interface UmbTreeItemModel { unique: string; // Identifier for selection, navigation, and API calls - entityType: string; // Must match workspace meta.entityType for navigation + entityType: string; // Must match Workspace meta.entityType for navigation name: string; // Display name shown in the tree hasChildren: boolean; // Shows expand arrow when true isFolder: boolean; // Visual styling hint @@ -43,7 +43,7 @@ Your repository methods transform API responses into these models when returning ## UmbTreeRootModel -The root model represents the top-level node of your tree. It extends `UmbTreeItemModel` but typically has `unique: null`: +The root model represents the top-level node of your Tree. It extends `UmbTreeItemModel` but typically has `unique: null`: ```typescript interface UmbTreeRootModel extends UmbTreeItemModel { @@ -92,10 +92,10 @@ export interface MyTreeItemModel extends UmbTreeItemModel { ``` {% hint style="info" %} -The `entityType` values must match the values in your workspace and tree item manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. +The `entityType` values must match the values in your Workspace and Tree Item Manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. {% endhint %} ## Related -- [Tree Repository](./tree-repository.md) - Returns tree models from its methods. -- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to workspaces. +- [Tree Repository](./tree-repository.md) - Returns Tree models from its methods. +- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to Workspaces. diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index fa82c970e4d..8aa9cbefbf5 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -2,7 +2,7 @@ A Tree Repository provides data to populate your Tree. It implements methods to return the root, root items, children of items, and ancestors. -The repository is referenced by your tree manifest via `meta.repositoryAlias`. +The repository is referenced by your Tree Manifest via `meta.repositoryAlias`. ## Interface @@ -12,20 +12,16 @@ The `UmbTreeRepository` interface defines the methods your repository must imple interface UmbTreeRepository { requestTreeRoot(); requestTreeRootItems(); - requestTreeItemsOf(args); - requestTreeItemAncestors(args); + requestTreeItemsOf(); + requestTreeItemAncestors(); } ``` See the full interface in the [UI API Documentation](https://apidocs.umbraco.com/v17/ui-api/interfaces/packages_core_tree.UmbTreeRepository.html). -## Implementing a Tree Repository - -Extend `UmbControllerBase` and implement the `UmbTreeRepository` interface. - ## Registering the Repository -Register the Repository in your manifest: +Register the Repository in your Manifest: ```typescript { @@ -36,7 +32,11 @@ Register the Repository in your manifest: } ``` -## Static Data Example +## Implementing a Tree Repository + +Extend `UmbControllerBase` and implement the `UmbTreeRepository` interface. + +### Static Data Example {% code title="static-tree.repository.ts" %} ```typescript @@ -107,5 +107,5 @@ export { MyStaticTreeRepository as api }; ## Related - [Tree Models](./tree-models.md) - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. -- [Tree Navigation](./tree-navigation.md) - How tree clicks navigate to workspaces. -- [Trees](./README.md) - Main tree extension documentation. +- [Tree Navigation](./tree-navigation.md) - How Tree clicks navigate to Workspaces. +- [Trees](./README.md) - Main Tree extension documentation. diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md index 24894f21dc2..febf8715dcb 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/trees-and-workspaces.md @@ -1,10 +1,10 @@ --- -description: How tree items navigate to workspaces when clicked in Umbraco +description: How Tree Items navigate to Workspaces when clicked in Umbraco --- # Trees & Workspaces -Trees and workspaces are tightly coupled. When users click a Tree Item, Umbraco navigates to a workspace to edit that item. This connection is established through the `entityType`, a string identifier that links Tree Items to their corresponding Workspace. +Trees and Workspaces are tightly coupled. When users click a Tree Item, Umbraco navigates to a Workspace to edit that item. This connection is established through the `entityType`, a string identifier that links Tree Items to their corresponding Workspace. ## How Tree Items Connect to Workspaces @@ -15,10 +15,10 @@ When you click a Tree Item: ## Workspace Kind: Routable vs Default -To support different routes for new vs existing items, your workspace must be routable to have different routes for each case. Use `kind: 'routable'` and set up matching routes in your Workspace API: +To support different routes for new and existing items, your Workspace must be routable to have different routes for each case. Use `kind: 'routable'` and set up matching routes in your Workspace API: ```typescript -// Tree item manifest +// Tree Item Manifest { type: 'treeItem', kind: 'default', @@ -26,7 +26,7 @@ To support different routes for new vs existing items, your workspace must be ro forEntityTypes: ['my-custom-item'], } -// Workspace manifest - MUST be routable for tree navigation +// Workspace Manifest - MUST be routable for Tree navigation { type: 'workspace', kind: 'routable', @@ -34,7 +34,7 @@ To support different routes for new vs existing items, your workspace must be ro name: 'My Custom Item Workspace', api: () => import('./my-custom-item-workspace.api.js'), meta: { - entityType: 'my-custom-item', // Must match tree item entityType + entityType: 'my-custom-item', // Must match Tree Item entityType }, } ``` @@ -44,12 +44,12 @@ To support different routes for new vs existing items, your workspace must be ro {% hint style="warning" %} **Endless loading when clicking Tree Items?** This usually means: -- No workspace is registered for that `entityType`. -- The `entityType` in your tree data doesn't match the workspace's `meta.entityType`. +- No Workspace is registered for that `entityType`. +- The `entityType` in your Tree data doesn't match the Workspace's `meta.entityType`. - {% endhint %} ## Related -- [Trees](./README.md) - Main tree extension documentation. -- [Workspaces](../workspace/README.md) - Creating workspace extensions. +- [Trees](./README.md) - Main Tree extension documentation. +- [Workspaces](../workspace/README.md) - Creating Workspace extensions. From 562b96d0d2330aeeec161cee92771a0695066654 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 17 Dec 2025 13:27:36 +0100 Subject: [PATCH 19/19] correct links after renaming --- 17/umbraco-cms/SUMMARY.md | 4 ++-- .../extending-overview/extension-types/tree/tree-models.md | 4 ++-- .../extension-types/tree/tree-repository.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/17/umbraco-cms/SUMMARY.md b/17/umbraco-cms/SUMMARY.md index 01b759aa0fe..c71b8ca26e6 100644 --- a/17/umbraco-cms/SUMMARY.md +++ b/17/umbraco-cms/SUMMARY.md @@ -189,9 +189,9 @@ * [Section Sidebar](customizing/extending-overview/extension-types/sections/section-sidebar.md) * [Section View](customizing/extending-overview/extension-types/sections/section-view.md) * [Trees](customizing/extending-overview/extension-types/tree/README.md) - * [Tree Navigation](customizing/extending-overview/extension-types/tree/tree-navigation.md) - * [Tree Models](customizing/extending-overview/extension-types/tree/tree-models.md) * [Tree Repository](customizing/extending-overview/extension-types/tree/tree-repository.md) + * [Tree Models](customizing/extending-overview/extension-types/tree/tree-models.md) + * [Trees & Workspaces](customizing/extending-overview/extension-types/tree/trees-and-workspaces.md) * [Workspaces](customizing/extending-overview/extension-types/workspaces/README.md) * [Workspace Actions](customizing/extending-overview/extension-types/workspaces/workspace-editor-actions.md) * [Workspace Action Menu Items](customizing/extending-overview/extension-types/workspaces/workspace-action-menu-items.md) diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md index 30266eb96f7..42c135f69a3 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-models.md @@ -92,10 +92,10 @@ export interface MyTreeItemModel extends UmbTreeItemModel { ``` {% hint style="info" %} -The `entityType` values must match the values in your Workspace and Tree Item Manifests. See [Tree Navigation](./tree-navigation.md) for how these connect. +The `entityType` values must match the values in your Workspace and Tree Item Manifests. See [Trees & Workspaces](./trees-and-workspaces.md) for how these connect. {% endhint %} ## Related - [Tree Repository](./tree-repository.md) - Returns Tree models from its methods. -- [Tree Navigation](./tree-navigation.md) - How `entityType` connects to Workspaces. +- [Trees & Workspaces](./trees-and-workspaces.md) - How `entityType` connects to Workspaces. diff --git a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md index 8aa9cbefbf5..ca6b97e60fc 100644 --- a/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md +++ b/17/umbraco-cms/customizing/extending-overview/extension-types/tree/tree-repository.md @@ -107,5 +107,5 @@ export { MyStaticTreeRepository as api }; ## Related - [Tree Models](./tree-models.md) - `UmbTreeItemModel` and `UmbTreeRootModel` interfaces. -- [Tree Navigation](./tree-navigation.md) - How Tree clicks navigate to Workspaces. +- [Trees & Workspaces](./trees-and-workspaces.md) - How Tree clicks navigate to Workspaces. - [Trees](./README.md) - Main Tree extension documentation.