From d91de95dcecf0ded2f40b0468c08c4711cb85ffb Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 30 May 2024 14:39:20 +0200 Subject: [PATCH 01/19] Added details of client-side extension points with Forms 14. --- .../developer/extending/adding-a-fieldtype.md | 547 ++++++++++++------ 1 file changed, 362 insertions(+), 185 deletions(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 619be0a5bca..408aa1398dd 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -2,53 +2,54 @@ _This builds on the "_[_adding a type to the provider model_](adding-a-type.md)_" chapter_ -## C\# +In this article we'll illustrate how to add a custom forms field type via server and client-side components. We'll take the example of rendering a "slider" field type, allowing the user to select a number between a range of values. -Add a new class to the Visual Studio solution, make it inherit from `Umbraco.Forms.Core.FieldType`, and fill in the constructor: +## Server-side Field Type Definition + +Add a new class to the Visual Studio solution, make it inherit from `Umbraco.Forms.Core.FieldType`, and complete as follows: ```csharp -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; +using Umbraco.Cms.Core.Composing; +using Umbraco.Forms.Core.Attributes; using Umbraco.Forms.Core.Enums; -using Umbraco.Forms.Core.Models; -using Umbraco.Forms.Core.Services; +using Umbraco.Forms.Core.Providers; -namespace MyFormsExtensions +namespace MyProject; + +public class SliderFieldType : Core.FieldType { - public class MyCustomField : Umbraco.Forms.Core.FieldType + public SliderFieldType() { - public MyCustomField() - { - Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this! - Name = "My Custom Field"; - Description = "Render a custom text field."; - Icon = "icon-autofill"; - DataType = FieldDataType.String; - SortOrder = 10; - SupportsRegex = true; - FieldTypeViewName = "FieldType.MyCustomField.cshtml"; - EditView = "Umb.PropertyEditorUi.TextBox"; - PreviewView = "Forms.FieldPreview.TextBox"; - } - - // You can do custom validation in here which will occur when the form is submitted. - // Any strings returned will cause the submission to be considered invalid. - // Returning an empty collection of strings will indicate that it's valid to proceed. - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage) - { - var returnStrings = new List(); - - if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom"))) - { - returnStrings.Add("You need to include 'custom' in the field!"); - } - - // Also validate it against the default method (to handle mandatory fields and regular expressions) - return base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, returnStrings); - } + Id = new Guid("6dff0075-598c-4345-89d7-e0db8684c819"); + Name = "Slider"; + Alias = "slider"; + Description = "Render a UUI Slider field."; + Icon = "icon-autofill"; + DataType = FieldDataType.String; + SortOrder = 10; + + FieldTypeViewName = "FieldType.Slider.cshtml"; + EditView = "My.PropertyEditorUi.InputNumber"; + PreviewView = "My.FieldPreview.Slider"; } + + [Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)] + public virtual string? Min { get; set; } = "1"; + + [Setting("Maximum", Description = "Maximum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 20)] + public virtual string? Max { get; set; } = "1"; + + [Setting("Step", Description = "Step size", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 30)] + public virtual string? Step { get; set; } = "1"; + + [Setting("Default Value", Description = "Default value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 40)] + public virtual string? DefaultValue { get; set; } = "1"; + + [Setting("Hide step values", Description = "Hides the numbers representing the value of each steps. Dots will still be visible", View = "Umb.PropertyEditorUi.Toggle", DisplayOrder = 50)] + public virtual string? HideStepValues { get; set; } + + [Setting("Background color", Description = "Background color for the input field", View = "My.PropertyEditorUi.InputColor", DisplayOrder = 60)] + public virtual string? BgColor { get; set; } = "1"; } ``` @@ -64,10 +65,10 @@ In the constructor, or via overridden properties, we can specify details of the - `MandatoryByDefault` - indicates whether the field will be mandatory by default when added to a form (defaults to `false`). - `SupportsRegex` - indicates whether pattern based validation using regular expressions can be used with the field (defaults to `false`). - `SupportsPreValues` - indicates whether prevalues are supported by the field (defaults to `false`). -- `FieldTypeViewName` - indicates the name of the partial view used to render the field. +- `RenderInputType`- indicates how the field should be rendered within the theme, as defined with the `RenderInputType` enum. The default is `Single` for a single input field. `Multiple` should be used for multiple input fields such as checkbox lists. `Custom` is used for fields without visible input fields. +- `FieldTypeViewName` - indicates the name of the partial view used to render the field on the website. - `EditView` - indicates the name of a property editor UI that is used for editing the field in the backoffice. If nothing is provided, the built-in label will be used and the field won't be editable. - `PreviewView` - indicates the name of a manifest registered client-side resource that is used for previewing the field in the backoffice. If nothing is provided, the name of the field type will be used as the preview. -- `RenderInputType`- indicates how the field should be rendered within the theme, as defined with the `RenderInputType` enum. The default is `Single` for a single input field. `Multiple` should be used for multiple input fields such as checkbox lists. `Custom` is used for fields without visible input fields. You will then need to register this new field as a dependency. @@ -76,32 +77,43 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Forms.Core.Providers; -namespace MyFormsExtensions +namespace MyProject; + +public class Startup : IComposer { - public class Startup : IComposer + public void Compose(IUmbracoBuilder builder) { - public void Compose(IUmbracoBuilder builder) - { - builder.WithCollectionBuilder() - .Add(); - } + builder.WithCollectionBuilder() + .Add(); } } ``` ## Partial view -Then we will start building the view for the default theme of the Form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.MyCustomField.cshtml`. +Then we will start building the view for the default theme of the Form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.Slider.cshtml`. The file name for the partial view should match the value set on the `FieldTypeViewName` property. ```csharp +@using Umbraco.Forms.Web @model Umbraco.Forms.Web.Models.FieldViewModel - placeholder="@Model.PlaceholderText" }} - @{if (Model.Mandatory || Model.Validate) { data-val="true" }} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" }} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> +@{ + var min = Model.GetSettingValue("Min", 1); + var max = Model.GetSettingValue("Max", 10); + var step = Model.GetSettingValue("Step", 1); + var bgColor = Model.GetSettingValue("BgColor", "#fff"); +} +
This is a custom "slider" field type. We'll just use an input to mock this up.
+ ``` This will be rendered when the default theme is used. @@ -110,210 +122,375 @@ If working with Umbraco 9 or earlier versions, you'll find the `Views\Partials\F For Umbraco 10 and above, we've moved to [distributing the theme as part of a Razor Class Library](../../upgrading/version-specific.md#views-and-client-side-files) so the folder won't exist. However, you can create it for your custom field type. If you would like to reference the partial views of the default theme, you can download them as mentioned in the [Themes](../themes.md) article. +## Field settings + +Field settings that will be managed in the backoffice by editors creating forms using the custom field type can be added to the C# class as properties with a `Setting` attribute: + +```csharp +[Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)] +public virtual string? Min { get; set; } = "1"; +``` + +The property `Name` names the setting in the backoffice with the `Description` providing the help text. Both of these are translatable as will be discussed in the backoffice components section below. + +The `View` property indicates a property editor UI used for editing the setting value. You can use a built-in property editor UI, one from a package, or a custom one registered with your solution. The default value if not provided is `Umb.PropertyEditorUi.TextBox`, which will use the standard Umbraco text box property editor UI. + +`SupportsPlaceholders` is a flag indicating whether the setting can contain ["magic string" placeholders](../magic-strings.md) and controls whether they are parsed on rendering. + +`HtmlEncodeReplacedPlaceholderValues` takes effect only if `SupportsPlaceholders` is `true`. It controls whether the replaced placeholder values should be HTML encoded (as is necessary for rendering within content from a rich text editor). + +`IsMandatory` if set to `true` will provide client-side validation in the backoffice to ensure the value is completed. + +When creating a field or other provider type, you might choose to inherit from an existing class. This could be if one of the types provided with Umbraco Forms almost meets your needs but you want to make some changes. + +All setting properties for the Forms provider types are marked as `virtual`, so you can override them and change the setting values: + ## Umbraco backoffice components -Two aspects of the presentation and functionality of the custom field are handled by client-side components, registered via manifests: +With Forms 14, aspects of the presentation and functionality of the custom field are handled by client-side components, registered via manifests: - The preview, displayed on the form definition editor. - The property editor UI used for editing the the submitted values via the backoffice. +- The property editor UI used for editing settings. +- A settings converter, that handles configuring the property editor and translating between the editor and persisted values. +- Translations for setting labels and descriptions. + +In order to create custom backoffice components for Umbraco 14, it's recommended to use a front-end build setup using Vite, TypeScript and Lit. Getting started with this is [described in the CMS documentation](https://docs.umbraco.com/umbraco-cms/tutorials/creating-your-first-extension#extension-with-vite-typescript-and-lit). -These are referenced server-side using the `PreviewView` and `EditView` respectively. +### Field preview -For the edit view, you can use a built-in property editor UI, one from a package, or a custom one registered with your solution. +The alias of the preview to use is defined on the field type via the `PreviewView` property. -To help with creating your own preview element, the following example shows the built-in text field preview: +A preview for our slider, representing the selected setting values could look as follows: ```javascript +import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api"; import { - html, + LitElement, + css, customElement, + html, property, } from "@umbraco-cms/backoffice/external/lit"; -const elementName = "forms-field-preview-text-box" -@customElement(elementName) -export class FormsFieldPreviewTextBox extends UmbLitElement { +const elementName = "my-field-preview-slider"; +@customElement(elementName) +export class MyFieldPreviewSliderElement extends UmbElementMixin(LitElement) { @property() - settings: Record = {} + settings = {}; + + @property({ type: Array }) + prevalues = []; - #getSettingValue(alias: string) : string { - return this.settings[alias]; + getSettingValue(key: string) { + return this.settings[key]; } render() { - return html``; + return html`
+ +
`; } + + static styles = css` + div { + padding: var(--uui-size-4); + } + `; } -export default FormsFieldPreviewTextBox; +export default MyFieldPreviewSliderElement; declare global { interface HTMLElementTagNameMap { - [elementName]: FormsFieldPreviewTextBox; + [elementName]: MyFieldPreviewSliderElement; } } ``` -It's registered using a manifest as follows: +And it is registered via a manifest: ```javascript -export const manifest: ManifestFormsFieldPreview = - { +import MyFieldPreviewSliderElement from './slider-preview.element.js'; + +const sliderPreviewManifest = { type: "formsFieldPreview", - alias: "Forms.FieldPreview.TextBox", - name: "Text Box Field Preview", - element: () => import('./text-box-field-preview.element.js'), + alias: "My.FieldPreview.Slider", + name: "Forms UUI Slider Field Preview", + api: MyFieldPreviewSliderElement, + element: () => import('./slider-preview.element.js') }; + + export const manifests = [sliderPreviewManifest]; ``` -## Field settings +### Field editor -Field settings that will be managed in the backoffice by editors creating forms using the custom field type can be added to the C# class as properties with a `Setting` attribute: +Umbraco Forms supports editing of the entries submitted by website visitors via the backoffice. The property editor interface to use for this is defined in the field type's `EditView` property. -```csharp -[Setting("My Setting", Description = "Help text for the setting", View = "Umb.PropertyEditorUi.TextBox", SupportsPlaceholders = "true", DisplayOrder = 10)] -public virtual string MySetting { get; set; } +If not using a built-in property editor, you can create your own. The following example shows how the numerical entries could be edited using an input control. + +```javascript +import { + html, + customElement, +} from "@umbraco-cms/backoffice/external/lit"; +import type { UmbPropertyEditorUiElement } from "@umbraco-cms/backoffice/extension-registry"; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; +import { + UmbPropertyValueChangeEvent, +} from "@umbraco-cms/backoffice/property-editor"; +import { UmbFormControlMixin } from "@umbraco-cms/backoffice/validation"; + +const elementName = "my-property-editor-ui-number"; + +@customElement(elementName) +export class MyPropertyEditorUINumberElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ + private onChange(e: Event) { + const newValue = (e.target as HTMLInputElement).value; + if (newValue === this.value) return; + this.value = newValue; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + + render() { + return html``; + } +} + +export default MyPropertyEditorUINumberElement; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: MyPropertyEditorUINumberElement; + } +} ``` -The property `Name` names the setting in the backoffice with the `Description` providing the help text. Both of these are translatable by providing a [user or package language file](../../../umbraco-cms/extending/language-files.md) containing appropriate keys: +Again, it's registered via a manifest. -```xml - - My Setting - Help text for the setting - +```javascript +const numberPropertyEditorManifest = { + type: 'propertyEditorUi', + alias: 'My.PropertyEditorUi.InputNumber', + name: 'Number Input Property Editor UI', + element: () => import('./property-editor-ui-number.element.js'), + meta: { + label: 'Number Input', + icon: 'icon-autofill', + }, +}; +export const manifests = [numberPropertyEditorManifest]; ``` -The area aliases for the other provider types are as follows: +### Setting value editor -- Data sources - `formProviderDataSources` -- Export types - `formProviderExportTypes` -- Prevalue sources - `formProviderPrevalueSources` -- Recordset actions - `formRecordSetActions` -- Workflows - `formProviderWorkflows` +Field type settings also use a property editor UI for editing the values in the backoffice. The one to use is defined via the `View` property on the `Setting` attribute. -The `View` property indicates a property editor UI used for editing the setting value. You can use a built-in property editor UI, one from a package, or a custom one registered with your solution. The default value if not provided is `Umb.PropertyEditorUi.TextBox`, which will use the standard Umbraco text box property editor UI. +In our example we use a custom one, allowing the value for the background color to the field to be selected via an input control. +```javascript +import { + html, + customElement, + type PropertyValueMap, +} from "@umbraco-cms/backoffice/external/lit"; +import type { UmbPropertyEditorUiElement } from "@umbraco-cms/backoffice/extension-registry"; +import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element"; +import { + UmbPropertyValueChangeEvent, +} from "@umbraco-cms/backoffice/property-editor"; +import { UmbFormControlMixin } from "@umbraco-cms/backoffice/validation"; + +const elementName = "my-property-editor-ui-color"; + +@customElement(elementName) +export class MyPropertyEditorUIColorElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ + protected firstUpdated( + _changedProperties: PropertyValueMap | Map + ): void { + super.firstUpdated(_changedProperties); + this.addFormControlElement(this.shadowRoot!.querySelector("input")!); + } + + private onChange(e: Event) { + const newValue = (e.target as HTMLInputElement).value; + if (newValue === this.value) return; + this.value = newValue; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + + render() { + return html``; + } +} + +export default MyPropertyEditorUIColorElement; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: MyPropertyEditorUIColorElement; + } +} +``` + +And once more, register via a manifest: + +```javascript +const colorPropertyEditorManifest = { + type: 'propertyEditorUi', + alias: 'My.PropertyEditorUi.InputColor', + name: 'Color Input Property Editor UI', + element: () => import('./property-editor-ui-color.element.js'), + meta: { + label: 'Color Input', + icon: 'icon-autofill', + }, +}; + +export const manifests = [colorPropertyEditorManifest]; +``` -You may want to consider registering a settings value converter. This is a client-side component that is registered in a manifest. It converts between the setting value required for the editor and the value persisted with the form definition. A converter defines three methods: +### Setting value converter + +You may want to consider registering a settings value converter. This is another client-side component that is registered in a manifest. It converts between the setting value required for the editor and the value persisted with the form definition. A converter defines three methods: - `getSettingValueForEditor` - converts the persisted string value into one suitable for the editor - `getSettingValueForPersistence` - converts the editor value into the string needed for persistence - `getSettingPropertyConfig` - creates the configuration needed for the property editor -The following code shows how the built-in slider setting element used for selecting a number within a range for the reCAPTCHA field is defined. - -```csharp -[Setting( - "Score threshold", - Description = "A reCAPTCHA v3 determined score between 0 and 10, above which form submissions are accepted. A higher value will catch more spam submissions, but also increase the risk of rejections of valid entries. For most sites, 5 is a sensible choice.", - View = "Umb.PropertyEditorUi.Slider", - PreValues = "0.0,1.0,0.1,0.5", - DisplayOrder = 10)] -public virtual string ScoreThreshold { get; set; } = string.Empty; -``` +The following code shows the structure for these converter elements. ```javascript -import { UmbPropertyValueData } from "@umbraco-cms/backoffice/property"; -import { FormsSettingValueConverterApi } from "./manifests"; -import { Setting } from "@umbraco-forms/generated"; -import { UmbPropertyEditorConfig } from "@umbraco-cms/backoffice/property-editor"; - -export class FormsSliderSettingValueConverter implements FormsSettingValueConverterApi { - async getSettingValueForEditor(setting: Setting, alias: string, value: string) { - // Multiply by 10 to get the integer value we need for the editor. - const editorValue = Math.trunc(parseFloat(value) * 10); - return Promise.resolve({ from: editorValue, to: editorValue }); - } +import type { UmbPropertyValueData } from "@umbraco-cms/backoffice/property"; - async getSettingValueForPersistence(setting: Setting, valueData: UmbPropertyValueData) { - // Divide by 10 to get the 0.0 to 1.0 range we actually want. - return Promise.resolve(((valueData.value ? parseInt(valueData.value["from"]) : 5) / 10).toFixed(1)); - } +export class SliderSettingValueConverter { - async getSettingPropertyConfig(setting: Setting, alias: string, values: UmbPropertyValueData[]) { - const config: UmbPropertyEditorConfig = []; - - // Min, max, step and default are provided in prevalues. - // As the slider only supports integers, we have to multiply by 10 for the UI and then divide again when we save. - config.push({ - alias: "enableRange", - value: false, - }); - - const settingValue = values.find(s => s.alias === alias)?.value?.toString() || ""; - - if (setting.prevalues.length >= 1) { - config.push({ - alias: "minVal", - value: parseFloat(setting.prevalues[0]) * 10, - }); - if (setting.prevalues.length >= 2) { - config.push({ - alias: "maxVal", - value: parseFloat(setting.prevalues[1]) * 10, - }); - if (setting.prevalues.length >= 3) { - config.push({ - alias: "step", - value: parseFloat(setting.prevalues[2]) * 10, - }); - if (setting.prevalues.length >= 3 && settingValue.length === 0) { - config.push({ - alias: "initVal1", - value: parseFloat(setting.prevalues[3]) * 10, - }); - } else { - config.push({ - alias: "initVal1", - value: parseFloat(settingValue), - }); - } - } - } - } + async getSettingValueForEditor(setting, alias: string, value: string) { + return Promise.resolve(value); + } - return Promise.resolve(config); + async getSettingValueForPersistence(setting, valueData: UmbPropertyValueData) { + return Promise.resolve(valueData.value); } - destroy() { + async getSettingPropertyConfig(setting, alias: string, values: UmbPropertyValueData[]) { + return Promise.resolve([]); } } ``` -It's registered using a manifest as follows. We provide the `propertyEditorUiAlias` to associated the converter with the appropriate property editor UI. +It's registered as follows. The `propertyEditorUiAlias` matches with the property editor UI that requires the conversions. ```javascript -export const manifest: ManifestFormsSettingValueConverterPreview = - { - type: "formsSettingValueConverter", - alias: "Forms.SettingValueConverter.Slider", - name: "Number Slider Value Converter", - propertyEditorUiAlias: "Umb.PropertyEditorUi.Slider", - api: FormsSliderSettingValueConverter, - }; +import { SliderSettingValueConverter } from "./slider-setting-value-converter.api"; + +const sliderValueConverterManifest = { + type: "formsSettingValueConverter", + alias: "My.SettingValueConverter.Slider", + name: "Slider Value Converter", + propertyEditorUiAlias: "My.PropertyEditorUi.Slider", + api: SliderSettingValueConverter, +}; + +export const manifests = [sliderValueConverterManifest]; ``` -`SupportsPlaceholders` is a flag indicating whether the setting can contain ["magic string" placeholders](../magic-strings.md) and controls whether they are parsed on rendering. +### Language files -`HtmlEncodeReplacedPlaceholderValues` takes effect only if `SupportsPlaceholders` is `true`. It controls whether the replaced placeholder values should be HTML encoded (as is necessary for rendering within content from a rich text editor). +Setting labels and descriptions are translated via language files. The following example shows how this is created for the settings on our example field type: -`IsMandatory` if set to `true` will provide client-side validation in the backoffice to ensure the value is completed. +```javascript +import type { UmbLocalizationDictionary } from "@umbraco-cms/backoffice/localization-api"; + +export default { + formProviderFieldTypes: { + sliderMinLabel: `Minimum`, + sliderMinDescription: `Minimum value`, + sliderMaxLabel: `Maximum`, + sliderMaxDescription: `Maximum value`, + sliderStepLabel: `Step`, + sliderStepDescription: `Step size`, + sliderDefaultValueLabel: `Default Value`, + sliderDefaultValueDescription: `Default value shown when the slider is displayed`, + sliderHideStepValuesLabel: `Hide step values`, + sliderHideStepValuesDescription: `Indicate whether the the field's label should be shown when rendering the form`, + sliderBgColorLabel: `Background color`, + sliderBgColorDescription: `Background color for the field`, + }, +} +``` -### Settings when inheriting +Each different type of extension for Forms uses a different root value: -When creating a field or other provider type, you might choose to inherit from an existing class. This could be if one of the types provided with Umbraco Forms almost meets your needs but you want to make some changes. +- Data sources - `formProviderDataSources` +- Export types - `formProviderExportTypes` +- Field types - `formProviderFieldTypes` +- Prevalue sources - `formProviderPrevalueSources` +- Recordset actions - `formRecordSetActions` +- Workflows - `formProviderWorkflows` -All setting properties for the Forms provider types are marked as `virtual`, so you can override them and change the setting values: -```csharp -[Setting("My Setting", Description = "My custom help text for the setting", View = "Umb.PropertyEditorUi.TextBox", SupportsPlaceholders = "true", DisplayOrder = 10)] -public override string MySetting { get; set; } -``` +The language files are registered with: -If you want to hide a setting in your derived class you can use the `IsHidden` property: +```javascript +import type { ManifestLocalization } from "@umbraco-cms/backoffice/extension-registry"; -```csharp -[Setting("My Setting", IsHidden = true)] -public override string MySetting { get; set; } +const localizationManifests: Array = [ + { + type: "localization", + alias: "My.Localization.En_US", + weight: -100, + name: "English (US)", + meta: { + culture: "en-us", + }, + js: () => import("./en-us.js"), + }, +]; +export const manifests = [...localizationManifests]; ``` + +### Registering the components + +Finally you will need an entry point to your client-side components that will register the manifests with Umbraco's extension registry. For example: + +```javascript +import { manifests as propertyEditorManifests } from "./property-editor/manifests.js"; +import { manifests as fieldPreviewManifests } from "./field-preview/manifests.js"; +import { manifests as settingValueConverterManifests } from "./setting-value-converter/manifests.js"; +import { manifests as localizationManifests } from "./lang/manifests.js"; + +const manifests = [ + ...propertyEditorManifests, + ...fieldPreviewManifests, + ...settingValueConverterManifests, + ...localizationManifests +]; + +export const onInit = async (host, extensionRegistry) => { + extensionRegistry.registerMany(manifests); +}; +``` \ No newline at end of file From 6ece2d77f4a6bfa9441bb125f6b3dd7d709597cb Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 30 May 2024 14:42:59 +0200 Subject: [PATCH 02/19] Linting --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 408aa1398dd..692bfb68af9 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -124,7 +124,7 @@ For Umbraco 10 and above, we've moved to [distributing the theme as part of a Ra ## Field settings -Field settings that will be managed in the backoffice by editors creating forms using the custom field type can be added to the C# class as properties with a `Setting` attribute: +Field settings will be managed in the backoffice by editors creating forms using the custom field type. They can be added to the C# class as properties with a `Setting` attribute: ```csharp [Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)] From 8ee36fb1cb73cc94621ee0937cdf8e68a202c070 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:41:30 +0200 Subject: [PATCH 03/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 692bfb68af9..7b71e890dba 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -2,7 +2,7 @@ _This builds on the "_[_adding a type to the provider model_](adding-a-type.md)_" chapter_ -In this article we'll illustrate how to add a custom forms field type via server and client-side components. We'll take the example of rendering a "slider" field type, allowing the user to select a number between a range of values. +In this article, we will illustrate how to add a custom form field type using server-side and client-side components. We will use the example of rendering a "slider" field type that allows the user to select a number within a specific range of values. ## Server-side Field Type Definition From 03328ac57316f41cd64f96fd4e3cccd668b4930c Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:41:38 +0200 Subject: [PATCH 04/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 7b71e890dba..c1808454a42 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -6,7 +6,7 @@ In this article, we will illustrate how to add a custom form field type using se ## Server-side Field Type Definition -Add a new class to the Visual Studio solution, make it inherit from `Umbraco.Forms.Core.FieldType`, and complete as follows: +Add a new class to the Visual Studio solution. Inherit from `Umbraco.Forms.Core.FieldType` and complete as follows: ```csharp using Umbraco.Cms.Core.Composing; From ef9e39b6b16637fc67b187ef9042e84be8e1df63 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:41:44 +0200 Subject: [PATCH 05/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index c1808454a42..6e75da637ec 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -63,7 +63,7 @@ In the constructor, or via overridden properties, we can specify details of the - `DataType` - specifies the type of data stored by the field. Options are `String`, `LongString`, `Integer`, `DataTime` or `Bit` (boolean). - `SupportsMandatory` - indicates whether mandatory validation can be used with the field (defaults to `true`). - `MandatoryByDefault` - indicates whether the field will be mandatory by default when added to a form (defaults to `false`). -- `SupportsRegex` - indicates whether pattern based validation using regular expressions can be used with the field (defaults to `false`). +- `SupportsRegex` - indicates whether pattern-based validation using regular expressions can be used with the field (defaults to `false`). - `SupportsPreValues` - indicates whether prevalues are supported by the field (defaults to `false`). - `RenderInputType`- indicates how the field should be rendered within the theme, as defined with the `RenderInputType` enum. The default is `Single` for a single input field. `Multiple` should be used for multiple input fields such as checkbox lists. `Custom` is used for fields without visible input fields. - `FieldTypeViewName` - indicates the name of the partial view used to render the field on the website. From a0a513bd209dc4e27d674176503a52f95d320f7b Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:41:51 +0200 Subject: [PATCH 06/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 6e75da637ec..8cae6ca8ee7 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -65,7 +65,10 @@ In the constructor, or via overridden properties, we can specify details of the - `MandatoryByDefault` - indicates whether the field will be mandatory by default when added to a form (defaults to `false`). - `SupportsRegex` - indicates whether pattern-based validation using regular expressions can be used with the field (defaults to `false`). - `SupportsPreValues` - indicates whether prevalues are supported by the field (defaults to `false`). -- `RenderInputType`- indicates how the field should be rendered within the theme, as defined with the `RenderInputType` enum. The default is `Single` for a single input field. `Multiple` should be used for multiple input fields such as checkbox lists. `Custom` is used for fields without visible input fields. +- `RenderInputType`- indicates how the field should be rendered within the theme as defined with the `RenderInputType` enum. + - The default is `Single` for a single input field. + - `Multiple` should be used for multiple input fields such as checkbox lists. + - `Custom` is used for fields without visible input fields. - `FieldTypeViewName` - indicates the name of the partial view used to render the field on the website. - `EditView` - indicates the name of a property editor UI that is used for editing the field in the backoffice. If nothing is provided, the built-in label will be used and the field won't be editable. - `PreviewView` - indicates the name of a manifest registered client-side resource that is used for previewing the field in the backoffice. If nothing is provided, the name of the field type will be used as the preview. From 78f0cf569da7e9192442a781640468df2ec67e51 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:41:58 +0200 Subject: [PATCH 07/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 8cae6ca8ee7..ff31737837f 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -125,7 +125,7 @@ If working with Umbraco 9 or earlier versions, you'll find the `Views\Partials\F For Umbraco 10 and above, we've moved to [distributing the theme as part of a Razor Class Library](../../upgrading/version-specific.md#views-and-client-side-files) so the folder won't exist. However, you can create it for your custom field type. If you would like to reference the partial views of the default theme, you can download them as mentioned in the [Themes](../themes.md) article. -## Field settings +## Field Settings Field settings will be managed in the backoffice by editors creating forms using the custom field type. They can be added to the C# class as properties with a `Setting` attribute: From 6dd68860ef2db9316077eda5e9a14bc5bd2bdc98 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:06 +0200 Subject: [PATCH 08/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index ff31737837f..fd1bc422890 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -127,7 +127,7 @@ For Umbraco 10 and above, we've moved to [distributing the theme as part of a Ra ## Field Settings -Field settings will be managed in the backoffice by editors creating forms using the custom field type. They can be added to the C# class as properties with a `Setting` attribute: +Field settings will be managed in the backoffice by editors who will create forms using the custom field type. These settings can be added to the C# class as properties with a `Setting` attribute: ```csharp [Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)] From 022320576e1f546379182ddb76724764031e7e68 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:15 +0200 Subject: [PATCH 09/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index fd1bc422890..857d15a336b 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -134,7 +134,7 @@ Field settings will be managed in the backoffice by editors who will create form public virtual string? Min { get; set; } = "1"; ``` -The property `Name` names the setting in the backoffice with the `Description` providing the help text. Both of these are translatable as will be discussed in the backoffice components section below. +The property `Name` names the setting in the backoffice with the `Description` providing the help text. Both of these can be translated, as discussed in the backoffice components section below. The `View` property indicates a property editor UI used for editing the setting value. You can use a built-in property editor UI, one from a package, or a custom one registered with your solution. The default value if not provided is `Umb.PropertyEditorUi.TextBox`, which will use the standard Umbraco text box property editor UI. From 1dcd5c6012cae9ae06be6aa96b79cb93c3b3bb0f Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:23 +0200 Subject: [PATCH 10/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 857d15a336b..eaec511fb46 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -158,7 +158,7 @@ With Forms 14, aspects of the presentation and functionality of the custom field - A settings converter, that handles configuring the property editor and translating between the editor and persisted values. - Translations for setting labels and descriptions. -In order to create custom backoffice components for Umbraco 14, it's recommended to use a front-end build setup using Vite, TypeScript and Lit. Getting started with this is [described in the CMS documentation](https://docs.umbraco.com/umbraco-cms/tutorials/creating-your-first-extension#extension-with-vite-typescript-and-lit). +To create custom backoffice components for Umbraco 14, it's recommended to use a front-end build setup using Vite, TypeScript, and Lit. For more information, see the [Creating your first extension](https://docs.umbraco.com/umbraco-cms/tutorials/creating-your-first-extension#extension-with-vite-typescript-and-lit) article. ### Field preview From 3198b51e0aeb70afa524729bdbfbb06b1e1355c3 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:31 +0200 Subject: [PATCH 11/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index eaec511fb46..1ff224522cd 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -160,7 +160,7 @@ With Forms 14, aspects of the presentation and functionality of the custom field To create custom backoffice components for Umbraco 14, it's recommended to use a front-end build setup using Vite, TypeScript, and Lit. For more information, see the [Creating your first extension](https://docs.umbraco.com/umbraco-cms/tutorials/creating-your-first-extension#extension-with-vite-typescript-and-lit) article. -### Field preview +### Field Preview The alias of the preview to use is defined on the field type via the `PreviewView` property. From a6e91c3e8725c20a1618540974ccbfaff8ad0935 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:39 +0200 Subject: [PATCH 12/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 1ff224522cd..9f20387a6c1 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -302,7 +302,7 @@ const numberPropertyEditorManifest = { export const manifests = [numberPropertyEditorManifest]; ``` -### Setting value editor +### Setting Value Editor Field type settings also use a property editor UI for editing the values in the backoffice. The one to use is defined via the `View` property on the `Setting` attribute. From 12bd89bd4c23d3d4be1eca9ac45ca69bd3dbb3dd Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:42:47 +0200 Subject: [PATCH 13/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 9f20387a6c1..a60f9a57f04 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -376,7 +376,7 @@ const colorPropertyEditorManifest = { export const manifests = [colorPropertyEditorManifest]; ``` -### Setting value converter +### Setting Value Converter You may want to consider registering a settings value converter. This is another client-side component that is registered in a manifest. It converts between the setting value required for the editor and the value persisted with the form definition. A converter defines three methods: From 0250b694538f8c237d4d7405440e43d9f4318ab0 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:43:24 +0200 Subject: [PATCH 14/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index a60f9a57f04..47f671ff530 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -421,7 +421,7 @@ const sliderValueConverterManifest = { export const manifests = [sliderValueConverterManifest]; ``` -### Language files +### Language Files Setting labels and descriptions are translated via language files. The following example shows how this is created for the settings on our example field type: From bc07c10cf0682096dc06482b08f1366703f958ec Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:43:31 +0200 Subject: [PATCH 15/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 47f671ff530..641f7ba2683 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -476,7 +476,7 @@ const localizationManifests: Array = [ export const manifests = [...localizationManifests]; ``` -### Registering the components +### Registering the Components Finally you will need an entry point to your client-side components that will register the manifests with Umbraco's extension registry. For example: From 884088e8cff9c53f16d429d93aa3fa29f3a9224c Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:43:38 +0200 Subject: [PATCH 16/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 641f7ba2683..5bf35123574 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -478,7 +478,7 @@ export const manifests = [...localizationManifests]; ### Registering the Components -Finally you will need an entry point to your client-side components that will register the manifests with Umbraco's extension registry. For example: +Finally, you will need an entry point to your client-side components that will register the manifests with Umbraco's extension registry. For example: ```javascript import { manifests as propertyEditorManifests } from "./property-editor/manifests.js"; From 959ed655d57577e643aad62f437281f8e7596356 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:45:30 +0200 Subject: [PATCH 17/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index 5bf35123574..b247fd62544 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -92,7 +92,7 @@ public class Startup : IComposer } ``` -## Partial view +## Partial View Then we will start building the view for the default theme of the Form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.Slider.cshtml`. From 5a87bc2ff167d8d93a5d04a87f321aceb8f70beb Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:45:36 +0200 Subject: [PATCH 18/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index b247fd62544..f608fd0a11f 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -148,7 +148,7 @@ When creating a field or other provider type, you might choose to inherit from a All setting properties for the Forms provider types are marked as `virtual`, so you can override them and change the setting values: -## Umbraco backoffice components +## Umbraco Backoffice Components With Forms 14, aspects of the presentation and functionality of the custom field are handled by client-side components, registered via manifests: From c96d8c7877915b95e01fc5562256346701d722b0 Mon Sep 17 00:00:00 2001 From: Esha Noronha <82437098+eshanrnh@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:45:41 +0200 Subject: [PATCH 19/19] Update 14/umbraco-forms/developer/extending/adding-a-fieldtype.md --- 14/umbraco-forms/developer/extending/adding-a-fieldtype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md index f608fd0a11f..6ecbbdb9ce3 100644 --- a/14/umbraco-forms/developer/extending/adding-a-fieldtype.md +++ b/14/umbraco-forms/developer/extending/adding-a-fieldtype.md @@ -236,7 +236,7 @@ const sliderPreviewManifest = { export const manifests = [sliderPreviewManifest]; ``` -### Field editor +### Field Editor Umbraco Forms supports editing of the entries submitted by website visitors via the backoffice. The property editor interface to use for this is defined in the field type's `EditView` property.