Custom fields allow you to render a custom UI for a field that an editor interacts within Agility CMS.
There are many different reasons why you might use a custom field. Here are a few ideas:
- Integrate with a Digital Asset Manager to allow editors to search and select external assets
- Integrate with a third-party/internal API to allow editors to search and select external entities
- Provide a customized input field such as a Block Editor, Vimeo Video, or Color Picker
- Cloudinary - Gives editors the ability to search and select resources from Cloudinary
- Widen - Gives editors the ability to search and select resources from Widen
- Vimeo - Gives editors the ability to paste a Vimeo URL and retrieve metadata for the video to incorporate in content
- Block Editor (Experimental) - Gives editors a block-styled editor for rich media stories
- Friendly URL field - Auto-generates a friendly URL value based off another text field
- Color Picker field - Allows editors to select a color from a color-picker and save the value as rgba/hex
- Selectlist from External API - A template you can start with to call an external API and allow editors to search and select external entities
- Markdown - Give editors the ability to input markdown
On load, the Content Manager will check for the existence of a a single Custom Fields JS file. If one has been set, the JS will be executed and consequently register each Custom Field declaration you have within the file. This will tell the Content Manager which custom fields are available for use, and allow you add a custom field to any content/module definition.
When a custom field is rendered within a content/module input form in the browser, your custom code is executed. A custom field typically includes your registration JS function (including the code that is executed when the field is rendered) and a reference to an HTML file representing your presentation layer.
For future backward compatibility, developers should build their Custom Fields logic encapsulated into iframes.
We recommend writing some JavaScript in the Custom Field that loads an iframe and then use iframe messages to communicate between Agility and the frontend of your integration.
An example of this can be found here.
You must have a Custom Fields Script URL set in the Content Manager. This can be found in Settings > UI Extensions.
You can load and evaluate a handful of custom fields defined in this repository by setting your Custom Fields Script URL to https://agility.github.io/CustomFields/custom-fields.js
.
This file is comprised of one or more custom field function declarations and its registration.
The Content Manager uses KnockoutJS for declaritive bindings, automatic ui refresh, dependancy tracking, and templating. While you don't need to use KnockoutJS to build a custom field, you will still likely interface with Content Manager KnockoutJS observable objects such as contentItem and fieldBinding. It is recommended to have a basic understanding of how Knockout observables work.
Boilerplate Custom Field Function Declaration:
var CustomFieldFunctionDeclaration = function () {
this.Label = "Sample Custom Field"; //[Requried] the name of the custom field as will appear in content/module def form builder
this.ReferenceName = "SampleCustomField"; //[Required] the reference name of the custom field for internal purposes
this.Render = function (options) {
/// <summary>[Optional] This function is called whenever the field is rendered in an input form - this includes after the item has been saved and the field is re-rendered.</summary>
/// <param name="options" type="Object">
/// <field name="$elem" type="jQueryElem">The .field-row jQuery Dom Element.</field>
/// <field name="contentItem" type="ContentItem Object">The entire Content Item object including Values and their KO Observable properties of all other fields on the form.</field>
/// <field name="fieldBinding" type="KO Observable">The value binding of thie Custom Field Type. Get and set this field's value by using this property - i.e. fieldBinding('new val')</field>
/// <field name="fieldSetting" type="Object">Object representing the field's settings such as 'Required', 'Hidden', 'Label', and 'Description'</field>
/// <field name="readonly" type="boolean">Represents if this field should be readonly or not.</field>
/// </param>
};
/// <field name="Template" type="String">[Optional] The absoulte URL to an HTML template that represents your custom field, or the referencename to an Inline Code file. Your ViewModel will be automatically bound to this template.</field>
this.Template = "https://domain.com/html/somefile.html";
/// <field name="DepenenciesJS"> type="Array">[Optional] The Javscript dependencies that must be loaded before your ViewModel is bound. They will be loaded in the order you specify.</field>
this.DependenciesJS = [
{ id: "somejs-reference-name", src: "https://domain.com/js/somefile.js" }, //src is an absolute URL to a JS file, or the referencename to an Inline Code file.
];
/// <field name="DepenenciesCSS" type="Array">[Optional] The CSS dependencies that must be loaded before your ViewModel is bound. They will be loaded in the order you specify.</field>
this.DependenciesCSS = [
{
id: "somecss-reference-name",
src: "https://domain.com/css/somefile.css",
}, //src is an absolute URL to a CSS file, or the referencename to an Inline Code file.
];
/// <field name="ViewModel" type="KO ViewModel">The KO ViewModel that will be binded to your HTML template</field>
this.ViewModel = function (options) {
/// <summary>[Optional] The KO ViewModel that will be binded to your HTML template.</summary>
/// <param name="options" type="Object">
/// <field name="$elem" type="jQueryElem">The .field-row jQuery Dom Element.</field>
/// <field name="contentItem" type="ContentItem Object">The entire Content Item object including Values and their KO Observable properties of all other fields on the form.</field>
/// <field name="fieldBinding" type="KO Observable">The value binding of thie Custom Field Type. Get and set this field's value by using this property.</field>
/// <field name="fieldSetting" type="Object">Object representing the field's settings such as 'Hidden', 'Label', and 'Description'</field>
/// <field name="readonly" type="boolean">Represents if this field should be readonly or not.</field>
/// </param>
this.value = options.fieldBinding; //reference the field KO observable value
this.contentID = options.contentItem.ContentID; //set the contentID of the current loaded item (NewItem = -1)
this.attrBinding = {}; //pass any custom attributes to the input field
if (options.fieldSetting.Settings.Required === "True") {
//if this field is marked as required, add a required parsley attribute
this.attrBinding["data-parsley-required"] = true;
}
};
};
Boilerplate HTML Template:
<div class="sample-field">
<input
type="text"
class="form-control"
data-bind="value: value, attr: attrBinding"
/>
</div>
Register the Custom Field:
ContentManager.Global.CustomInputFormFields.push(
new CustomFieldFunctionDeclaration()
);
-
After you've set your Custom Fields Script URL and added a custom field declaration function and registration, you will need to refresh the Content Manager to load the JS file.
-
Verify your Custom Field input field has been registered by going to Models > Content Models (or Page Modules) and create or update an existing Model or Page Module. Add a new field, and select the Custom Field tab. If successful, you should see your custom field listed under Field Type.
There are several ways to test new/changes to a custom field type.
-
For new custom fields, create a test environment for your field by creating a test content/module definition that you can use just for testing. If it works there, it will work in ANY definition.
-
For updates to existing custom fields, create a new version of the custom field type in your Custom Fields file (i.e. "Sample Field v2") and test in a test content/module definition.
-
If making updates to an existing custom field, and creating test definitions is not feasible then you can make use of a Development Mode within the Content Manager to test changes on the existing definition forms without affecting the forms for any editors.
Special care should be taken when making code changes to an existing custom field type that is being used across numerous types of content. Creating new versions of custom fields and test definitions can be cumbersome at times.
To address this, the Content Manager has a Development Mode that can be turned on and off by a developer. Turning Development Mode ON will set your browser session only to pull-in a Dev Mode Custom Fields JS File . This allows you to create a copy of your current Custom Fields JS file, specify it as your Development Mode JS file and make any required changes and test within the browser.
It is important to note that only your browser session will pull-in this Development Mode JS file. When development is complete, you can simply replace the live file with your dev file.
In your custom field code, you may want to have conditional logic when running in Development Mode. An example of this could be changing some variable values such as API endpoints, or other references for testing purposes.
Detect Development Mode:
if(ContentManager.Developer.DevMode()) {
//do something
}
- In the Content Manager go to Settings > Development Framework
- Click on Turn on Development Mode
- The application will refresh
- An pylon icon will now appear in the status bar showing that you are in Development Mdoe
- Click on the pylon in the status bar
- Confirm and click OK on the dialogue window
OR
- Go to Settings > Development Framework
- Click Turn off Development Mode