Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Learn how to work with data or request the data when extending the backoffice.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
Repositories are used for talking to the server by requesting data and getting notified about updates.
Context APIs work with “local/runtime” data and enables receiving APIs.
A store holds data throughout the session. It is used to create reactivity across different parts.
A reactive container holding data, when data is changed all its Observables will be notified.
A store holds data throughout the session. It is used to create reactivity across different parts.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
A store is the link between a Resource and a Repository. A store is mainly taken from a Context API. In other words, we have to Consume the Context (Store) to get the Store.
Generally, a Store will hold one or more State Objects, with each Subject made available for Observation via Observables.
In this example, we created an ArrayState, A State which is specific to Arrays. This holds the data for one or more Observables to convey to outsiders.
This example shows how to use the store:
Here we added a method that returns an Observable that is specific to the requested product.
An example implementation using this method:
Create many Observables
A Store must hold different Observables, some exceptionally general and others highly specific, all in perspective of the types of observers we aim to accommodate.
This example gives some inspiration to how fine-grained this can become:
An observer of an Observable will only be triggered if the specific part of that data has changed. With this we can make a high-performance application, only triggering the parts that need to update when data is changed.
Ensure unique data
For incoming data to replace existing data, we need to clarify what makes an entry of the array unique. In the examples of this guide, each product has an id
. We have clarified this to the State by giving it the little method (product) => product.id
as part of its creation:
Make reactivity with Umbraco States
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
Umbraco States enables you to create Observables based on a State. The Observables can then be observed, and when a change to the State occurs, all observers of Observables will be triggered.
Umbraco comes with a State type for the most common types of data:
Array State
Boolean State
Class State
Number State
Object State
String State
The Umbraco Element or Controllers comes with the ability to observe an Observable.
While observing all changes will result in the callback being executed.
The example below creates a State and then turns the whole state into an Observable, which then can be observed.
The value of a state can be changed via the setValue
method. This replaces the current data with new data.
The following example shows how to change the value of the state to hold item2
and item3
. As the example extends the example from above, it means that item1
is no longer part of the value of this state.
Observe part of a state
With the asObservablePart
method, you can set up an Observable that provides a transformed outcome, based on the State.
In the above example, the asObservablePart
mapping function will be executed every time there is a change to the State. If the result of the method is different than before it will trigger an update to its observers.
The Variant Context is a context that holds the data for a set of properties.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
Property Editors UIs require the Dataset Context to be present to work. This enables Property Editor UIs to have a generic relation with its ownership.
The Dataset Context holds a name and a set of properties. What makes a property can vary but an alias and a value are required.
A Dataset Context is the connection point between a Property Editor and a Workspace.
The hierarchy is as follows:
Workspace Context
Dataset Context
Property Editor UIs
A dataset context covers a set of properties, in some cases a workspace then needs to have multiple variants. An example of such is Document Workspace. Each variant has its own set of properties and a name.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
A repository is the Backoffices entry point to request data and get notified about updates. Each domain should register their own repository in the Backoffice.
With a repository we can have different data sources depending on the state of the app. It can be from a server, an offline database, a store, a Signal-R connection, etc. That means that the consumer will not have to be concerned how to access the data, add or remove items from a collection of items, etc. This means we get a loose connection between the consumer and the data-storing procedures hiding all complex implementation.
A repository has to be instanced in the context where it is used. It should take a host element as part of the constructor, so any contexts consumed in the repository (notifications, modals, etc.) get rendered in the correct DOM context.
A repository can be called directly from an element, but will often be instantiated in a context, like the Workspace Context.
Contexts
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
Below you can find some articles on how you can work with different contexts:
A Dataset Context is the connection point between a Property Editor and a Workspace and covers a set of properties.
Ease the integration with Backoffice by using a Umbraco Element
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
This provides a few methods to connect with the Backoffice, giving you the ability to:
Consume a Context —
Provide Context —
Observe a State —
Localization —
Host Controllers —
You can turn any Web Component into an Umbraco Element by using the Umbraco Element Mixin, as done in the following example:
Getting started with backoffice setup and configurations
In this section, you can find the common terms, concepts, and guides used to extend the Umbraco backoffice.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
An overview of community articles related to the New backoffice "Bellissima".
An overview of concepts on how to work with data when extending the backoffice.
An overview of concepts on how to work with contexts when extending the backoffice.
An overview of concepts on how to work with Umbraco element when extending the backoffice.
An overview of concepts on how to work with sorting when extending the backoffice.
An overview of concepts on how to work with routes when extending the backoffice.
Communicate across different boundaries with the Context API
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
The Context API enables receiving APIs. Depending on where your code is executed from, it affects which and what instances of APIs can be received.
The Context API enables an element or a controller to receive an API provided via any ascending element. In other words, it can receive APIs provided via a parent element or parent of a parent element, and so forth.
The Context API enables connections between Elements and APIs. DOM structure defines the context of which an API is exposed for. APIs are provided via an element and can then be consumed by any decending element.
There are different ways to consume a Context API. The most straightforward implementation is done on an Umbraco Element with a Context Token.
All Umbraco Context APIs have a Context Token which can be imported and used for consumption, for example:
The above example takes place in an Umbraco Element or Umbraco Controller.
The above examples utilize an Umbraco Controller to hook into an element's life cycle. This Controller is named UmbContextConsumerController
.
If you need to consume a Context API from a non-controller host, then look at the UmbContextConsumer
.
A Context Token is a context identifier and is generally a string matched with a type. In this way, users of the token can be sure to get the right type of context.
For additions to Contexts, we can use the API Aliases to identify the additional API. Using the same Context Alias for additional APIs will ensure that such API must be present with the first encounter of that Context Alias. Otherwise, a request will be rejected. In other words, if the addition is not part of the nearest matching Context, the request will be rejected.
Using API Alias only provides value when two or more APIs should share the same Context. This is needed for Context Extensions that are provided along with other Contexts.
The Token declared above can be used to provide an additional Context API at the same Element as another Context API is provided at. Below is an example of how the two APIs are made available.
This is no different than using two different Context Aliases. But it has an important effect on what happens if one of them is not provided. This is demonstrated in the example below:
The consumption of the Additional API will never happen as the token uses the same Context Alias as MY_API_TOKEN
. This means that any request containing this Context Alias will be stopped at the first API it encounters. To ensure addition to a specific context, do it locally at the nearest API that uses the same Context Alias.
This is only relevant if you are going to make multiple context API for the same context. Discriminator only gives value for consumption of Context APIs that have a varying interface. The backoffice uses this for the different types of Workspace Contexts.
If someone wants the workspace name, they might not care about the specific API of the Workspace Context. These implementations can use a standard Context Token with a type of generic Workspace Context.
The features related to Publishing in the Document Workspace Context do not require a new Context. However, we should not accidentally retrieve the workspace context of a parent workspace when in a Workspace. Therefore, we need to provide a workspace context in each workspace, and the one we retrieve is the one we will be using. Since Publishing is not part of the generic Workspace Context, check if the context is a Document Workspace Context and recast it accordingly.
To avoid each implementation taking care of this, Context Tokens can be extended with a Type Discriminator. This will discard the given API if it does not meet the necessary requirements. When it is the desired type, the API will be converted to the appropriate type.
This example shows how to create a discriminator Context Token that will discard the API if it is not a Publishable Context:
Context Token Example:
Implementation of Context Token Example:
This allows implementers to request a publishable context without needing to know the Type or how to identify the context.
In detail, the Context API will search for the first API that matches the alias My.Context.Token
, and not look further. If the API meets the type discriminator, it will be returned, otherwise the consumer will never reply.
You can provide a Context API from an Umbraco Element or Umbraco Controller:
Or with a Controller using a 'host' reference to Controller Host (Umbraco Element/Controller):
In some cases, it is needed to have different APIs for the same context. For example, the .
Learn how to manage and use the Backoffice UI Localization files.
This article describes how you can translate the Umbraco Backoffice UI into different languages. You can use the existing localizations from Umbraco or register your own localizations. You can also use the localization in your custom elements and controllers.
Localizations can be registered via the Extension Registry. Read more about the Localization Extension Type.
As Umbraco is an evolving product, new text is regularly added to the English version of these files. Therefore, some of the languages may no longer be up-to-date.
If a key is not found in the current language, the fallback language will be used. The fallback language is English with the culture code en.
If a translation is missing, the default value within umb-localize
will be shown in the user interface:
Instead of showing the default value we can show the key alias if we set debug="true"
:
The following example shows how you can display localized text with the umb-localize
element:
You can have a look and try out the element in the UI API Documentation.
In some situations, you need the localization as a variable that can be parsed. In this case, the Localization Controller can be used in your element.ts
file. This can be setup in two ways:
Using Umbraco Element
Using Umbraco Controller
When using an Umbraco Element, the Localization Controller is already initialized on the localize
property via the UmbElementMixin
.
The arguments will be passed to the function in the localization file if it is a function.
If you are working with an Umbraco Controller, then you need to initialize the Localization Controller on your own via the UmbLocalizationController
:
Sometimes you need to pass arguments to the localization to return different values based on the arguments. A localization value can be either a string or a function. Given a localization file like this, we can return different values based on the number of items:
You can try out the arguments feature in the UI API Documentation.
Using the Localize Element
You can pass arguments to the localization by adding them as additional attributes:
The arguments will be passed to the function in the localization file if it is a function. The args
attribute must be JSON-serializable and each array value will be passed to the function as an extra argument.
Using the Localize Controller
You can pass arguments to the localization by calling the term
method with the arguments:
The arguments will be passed to the function in the localization file if it is a function. Each argument of term
will be passed to the function as an extra argument.
You can also use placeholders in the localization keys to replace parts of the string with dynamic values. Placeholders are defined by curly braces {0}
or percentage signs %0%
in the localization key. The placeholders will be replaced one-to-one with the arguments passed to the localization. It works the same as the arguments feature, except you cannot calculate the value based on the arguments.
Given a localization file like this:
You can use the same args
attribute to pass the arguments:
You can add your own localization keys using the principles you have learned, and apply them in a number of ways:
You can find a localization example in the Adding localization to the dashboard article. This will get you started with using localization in your custom elements. You can apply the same principles to all extensions.
Property descriptions and labels can also be localized. They are formatted as Markdown and can contain localization keys using the built-in Umbraco Flavored Markdown syntax.
A list of some of the key concepts with working the Umbraco Backoffice.
Understanding certain key concepts is essential when customizing the backoffice. These terminologies can help you decode the purpose of code effectively:
Repository: An API enables communication with a server.
Store: An API representing data, generally coming from the server. Most stores would talk with one or more resources. You can read more about this in the Store article.
State: A reactive container holding data, when data is changed all its Observables will be notified. You can read more about state and observables in the States article.
Observable: An observable is the hook for others to subscribe to the data of a State.
Observe: Observe describes what we do when subscribing to an Observable.
Context-API: The name used to serve APIs (instances/classes) for a certain context in the DOM. An API that is served via the Context-API is called a Context. You can read more about this in the Context API article.
Context Provider: One that provides a class instance as a Context API.
Context Consumer: One that consumer subscribes to a class instance as a Context API.
Controller: An abstract term for a thing that hooks into the lifecycle of an element. Many things in our system are Controllers.
Umbraco Controller: Enables hosting controllers. Additionally, it provides a few shortcut methods for initializing core Umbraco Controllers. You can read more about this in the Controllers article.
Controller Host: A class that can host controllers.
Controller Host Element: The element that can host controllers.
Umbraco Element: The UmbLitElement
or UmbElemenMixin
enables hosting controllers. Additionally, it provides a few shortcut methods for initializing core Umbraco Controllers. You can read more about this in the Umbraco Element article.
Read more about various ways how to get started with extending the backoffice in the Backoffice Setup article.
Reuse functionality across components by writing it as a Controller
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
A Controller must follow the interface of UmbController. To ease the implementation you can base your class on the UmbControllerBase
:
Get started with Routing in the backoffice.
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
The routing in the backoffice is flexible and customizable. In this article, you can find a couple of starting points for routing.
The overall divider is the Section which is a ManifestSection
extension type. It is also used internally by the following sections: Content, Media, Settings, Members, and so on.
Depending on which section you are working on, there are different options:
SectionView: The Section View is a view in a section and one of the automatic router extension types. It can be an entry point to a section. If a section has multiple views defined (or both dashboards and views) then the tabs and icons will be rendered. As some examples, you can check the Packages and Member sections.
Dashboard: The Dashboard is an entry point to a section. If there is more than one section view or dashboard then the defined tabs and icons will be rendered to make it possible to navigate.
Workspace: The Workspace concept has built-in features to facilitate editing of an entity of a certain entity type. It is used by many entities in the backoffice like content, media, content types, data types, dictionaries and so on.
Custom element: A Custom Element is a section that can be configured to use any web component as the entry point. The element()
can be configured in the manifest. By doing this we'll disable the possibility of using dashboards and section views for the section since they will not be automatically routed/rendered. This option should be used only when necessary.
Almost any component can host routable sub-components by defining a list of routes and render a umb-router-slot
element. Let's assume we have a custom section with pathname custom-section
and a section view with pathname organization
. In this context we can create an element with routes, like this:
The order in which the routes are defined is important as the first match will be used. So make sure to add more specific routes in the beginning.
In the render method of the element, render the umb-router-slot
:
One can create links to allow navigation to a given route:
The icons from the Umbraco backoffice are based on Lucide Icons. The syntax for getting the icons starts withicon-
. You can find the list of all icons in the Icon registry list on GitHub.
Enable sorting elements via drag and drop
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
The Umbraco Sorter enables you to make a list of elements sortable via drag-and-drop interaction. You have to set up the sorter once on the Element that renders the items to be sorted. As part of the configuration, you shall provide an onChange
callback method, which will be executed every time the sorter makes a difference to the data.
The following example shows a basic setup of the Sorter.
The properties provided are the following:
itemSelector
: A query selector that matches the items that should be draggable.
containerSelector
: A query elector that matches the parent element of the items.
getUniqueOfElement
: A method that returns the unique element
getUniqueOfModel
: Provide a method that returns the unique of a given model entry
onChange
: Provide a method to retrieve the changed model. This is called every time the model is changed, including when the user is dragging around.
The model given to the Sorter must be an Array. The following example extends the example from above:
The Sorter does not move elements, instead, it updates the model as the user drags an item around. This puts higher pressure on the rendering of the sortable Elements. This means we need to make sure that the rendering re-uses the same element despite sorting the data differently.
Lit does provide a render helper method called repeat
that does this for us. The following example shows a render method that continues the work of the examples above:
A Controller enables a class to hook into the life cycle of a Web Component
This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice.
Controllers have the ability to declare the following methods:
hostConnected()
— Called when the Host Element is Connected to the DOM.
hostDisconnected()
— Called when the Host Element is Disconnected from the DOM.
destroy()
— Called when the controller is taken out of commission.
Additionally, the Umbraco Controllers implement a getHostElement()
method, which enables any Controller to receive the Element that hosts the Controllers.
A Controller will have to be assigned to a Host Element. An assignment can be indirect as Controllers can host other Controllers.
The Host Element is a Controller Host Web Component. The Umbraco Element turns any Web Component into a Controller Host. For more information check the Umbraco Element article.
Any controller can be identified by a Controller Alias, using either a String or Symbol. If you utilize a Controller with a Controller Alias, then it will be destroyed when another Controller with same Alias gets Added to same Host. In this way, you can keep your controllers tidy, without a lot of managing. The example below shows how to initialize a Controller with a Controller Alias.
The creation of this Controller will replace its previous instance. Leaving only the latest observation to be present, as the previous instance will be removed and destroyed.
You can find another example in the Write your own Controller article.