DEV Community

Cover image for Upgrade Umbraco 13 to 17: Property Editors + Property Value Converters
Luuk Peters
Luuk Peters

Posted on

Upgrade Umbraco 13 to 17: Property Editors + Property Value Converters

This is part five in a series about common tasks you'll encounter when upgrading Umbraco 13 to 17. In this part, weโ€™ll look at updating Property Editors and Property Value Converters.

When upgrading several packages, one area that caused confusion was Property Editors. In Umbraco 17 there is a much clearer separation between the UI in the backoffice and the data handling and validation on the backend (C#).

Because of this change, Umbraco introduced migrations that convert existing Data Types (which are essentially instances of a Property Editor) to the new format. In Umbraco 13 a Data Type only referenced a single editor alias, but in Umbraco 17 it contains two: an editor alias (backend) and an editor UI alias (frontend). This makes it possible, for example, to use multiple interchangeable UIs that work with the same underlying data.

Why won't my property editor work anymore?

After upgrading my database to Umbraco 17 I ran into a couple of issues:

  • How does Umbraco know that it should use my newly created Property Editor UI for existing properties?
  • Why does Models Builder suddenly return a JsonDocument instead of my custom VideoPlayerValueConverterModel?

The answer lies in understanding what the migration from Umbraco 13 to 14+ actually does and what you need to do with the result. This post walks through that process.

This blog explains how you can update your Property Editors to work in Umbraco 17 without having to create a custom migration. It also helps to read the Umbraco documentation on Property Editor composition. That documentation explains the different pieces that make up a Property Editor in Umbraco 17 and provides useful context for what follows.

The starting point before the migration

There are two different starting points for Property Editors in Umbraco 13, and each leads to a slightly different outcome during the migration to Umbraco 14+. The difference depends on whether the Property Editor is defined in a package.manifest file or in C# code.

Starting point 1: manifest-only Property Editor

In Umbraco 13 a Property Editor can be defined purely in a package.manifest, for example:

...
  "propertyEditors": [
    {
      "alias": "proudnerds.videoplayer.editor",
      "name": "Video player",
      "icon": "icon-play",
      "group": "Proud Nerds",
      "editor": {
        "view": "~/App_Plugins/ProudNerds.Umbraco.VideoPlayer/umbraco.videoplayer.html"
      }
    }
  ]
...
Enter fullscreen mode Exit fullscreen mode

This is enough to create a simple data editor. The value is stored as a string in the database and there is no server-side validation. Anything can be stored as long as it fits into a string.

If needed, you can still transform that value later using a Property Value Converter before it ends up in the cache.

Starting point 2: Property Editor defined in code

You can also define a Property Editor in C#, for example:

using Umbraco.Cms.Core.PropertyEditors;

namespace YourProjectName;

[DataEditor(
    alias: "Suggestions editor",
    name: "Suggestions",
    view: "/App_Plugins/Suggestions/suggestion.html",
    Group = "Common",
    Icon = "icon-list")]
public class Suggestions : DataEditor
{
    public Suggestions(IDataValueEditorFactory dataValueEditorFactory)
        : base(dataValueEditorFactory)
    {            
    }
}
Enter fullscreen mode Exit fullscreen mode

Defining a Property Editor in code gives you more control over validation and how the data is stored.

The resulting database records

When you create a Data Type in Umbraco 13 that uses a Property Editor, the resulting database record is almost identical for both approaches. Each Data Type simply stores a property editor alias and configuration in the umbracoDataType table.

If you register the editor in C#, the dbType may differ, but that detail is not important for this discussion.

The data migration (13 โ†’ 14+)

The difference between the two approaches becomes visible during migration.

In one of the later Umbraco 13 versions a pre-migration was introduced that prepares Property Editors for upgrading to Umbraco 14+. During this step, a record is written to the umbracoKeyValue table describing how each Data Type should be migrated.

For a manifest-only Property Editor, the record looks like this:

[{"DataTypeId":1055,
"EditorUiAlias":"proudnerds.videoplayer.editor",
"EditorAlias":"Umbraco.Plain.String"}]
Enter fullscreen mode Exit fullscreen mode

However, if the editor was defined in C#, both aliases will be the same:

[{"DataTypeId":1055,
"EditorUiAlias":"proudnerds.videoplayer.editor",
"EditorAlias":"proudnerds.videoplayer.editor"}]
Enter fullscreen mode Exit fullscreen mode

After the database upgrade to Umbraco 17, this information is used to populate the new propertyEditorAlias and propertyEditorUiAlias columns in the umbracoDataType table.

For the video player editor, the result of the migration looks like this:

Updating the code

Once you understand what the migration produced in the database, updating the code becomes much clearer.

Creating and registering the Property Editor UI

With the new backoffice introduced in Umbraco 14+, the UI part of your Property Editor needs to be recreated because AngularJS is no longer supported.

After creating the UI, you need to register it. I won't go into the details of building a Property Editor UI here (the documentation covers that), but the aliases you use during registration are important.

Based on the migration result:

  • alias should match PropertyEditorUiAlias
  • propertyEditorSchemaAlias should match PropertyEditorAlias

For example:

export const manifests: Array<UmbExtensionManifest> = [
{
    type: 'propertyEditorUi',
    alias: "proudnerds.videoplayer.editor",
    name: "Proud Nerds video player property editor",
    js: () => import("./proud-nerds-video-property-editor-ui.element"),
    meta: {
        label: "Video player",
        icon: "icon-play",
        group: "Proud Nerds",
        propertyEditorSchemaAlias: "Umbraco.Plain.Json",
        settings: {
            properties: [
                ...
            ]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Updating the DataEditor (if it already exists)

If your editor already had a DataEditor implementation, you can continue using it, but it needs to be updated for Umbraco 17.

Most of the implementation remains similar to Umbraco 13. The main difference is that several parameters have been removed from the DataEditor attribute because of the clearer separation between UI and backend logic.

// Umbraco 13
[DataEditor(
    alias: "proudnerds.videoplayer.editor",
    name: "Video Player",
    view: "/App_Plugins/VideoPlayer/videoplayer.html",
    Group = "Proud Nerds",
    Icon = "icon-play")]
public class VideoPlayerEditor : DataEditor
...

// Umbraco 17
[DataEditor("proudnerds.videoplayer.editor")]
public class VideoPlayerEditor : DataEditor
...
Enter fullscreen mode Exit fullscreen mode

Updating the Property Value Converter (if needed)

If you used the manifest-only approach in Umbraco 13, there is a good chance your Property Value Converter no longer works.

During migration, the EditorAlias for the video editor changed from:

proudnerds.videoplayer.editor
Enter fullscreen mode Exit fullscreen mode

to:

Umbraco.Plain.Json
Enter fullscreen mode Exit fullscreen mode

So if your Property Value Converter checks the editor alias, it will no longer match:

public override bool IsConverter(IPublishedPropertyType propertyType) =>
propertyType.EditorAlias.Equals("proudnerds.videoplayer.editor");
Enter fullscreen mode Exit fullscreen mode

The simplest fix is to check the EditorUiAlias instead:

public override bool IsConverter(IPublishedPropertyType propertyType) =>
propertyType.EditorUiAlias.Equals("proudnerds.videoplayer.editor");
Enter fullscreen mode Exit fullscreen mode

This is not entirely semantically correct, but it is often the easiest solution without introducing additional migrations. After this, your Property Editor will work as expected again and the models builder will use your custom model instead of the generic JsonDocument.

Done

Once you understand how the Property Editor migration works, it becomes much easier to determine which aliases to use in umbraco-package.json and what code changes are required after upgrading.

The concepts themselves are straightforward, but the migration can be confusing the first time you encounter it. Hopefully this overview helps clarify what is happening behind the scenes.

EditorUiAlias or not?

Earlier I mentioned that checking EditorUiAlias inside the IsConverter method of a Property Value Converter is not entirely correct from a semantic point of view.

A Property Value Converter operates on the data stored in the database and converts that to something else to put in the cache. A Property Editor UI is only the interface used to edit that data. In fact, multiple UIs could theoretically exist for the same underlying editor.

Because of that, checking EditorUiAlias introduces some risk. If a different UI were introduced for the same editor, your converter might no longer match the correct data. Checking EditorAlias is the safest way to guarantee you are handling the expected data structure.

That said, in many real-world scenarios a Property Editor has exactly one UI and one DataEditor that always belong together. If you fully control the implementation, checking EditorUiAlias can be a pragmatic and perfectly workable solution that avoids writing additional migrations.

Opinions on this tend to differ. Some developers strongly prefer to always check EditorAlias for correctness, while others consider EditorUiAlias acceptable when the editor and UI are tightly coupled. In the end, the choice depends on how strictly you want to follow the separation between UI and data.

Top comments (0)