New Dise One & CX Portal release! Click this link to read the release notes: Dise One 4.2 & CX Portal V39

Custom Editors

Prev Next

The basics

Usually, the set of Template-fields are defined in the template.json and the CMS provides a default Editor for adjusting the Message values. This has its limitations, especially if a more complex data-model is required.

As an alternative, a Template can contain a “Custom Editor“ which will be used instead. It can:

  • Supply a JSON-serializable object to the CMS that is used as the Message “payload“.

  • Get the current Message (when editing an existing instance).

  • Call for a Media Selector for choosing CMS Media.

  • Have two-way communication with the Template being previewed.

  • “Listen in“ on the events sent from the Preview-Template.

The Editor is simply an HTML-file and specified at the root of the template.json manifest. When a Custom Editor is provided, any “fields”-Array in the manifest will be ignored.

  "customEditor": "editor/editor.html"

The “editor“ subfolder is not a requirement, but the “zipping script“ provided by Dise will include it in the zip-file if it exists. Note that any subfolder named “src“ and files with names ending in “.map“ or starting with a period will be excluded, so if the editor requires compilation it is wise to keep the source-files in the “src“-folder.

EditorAPI

To simplify implementation of Custom Editors, a helper-class is provided in the TemplateFramework. This can be found in the “EditorAPI/index.ts“ file of both the “esm“ and “framework“ (classic) versions.

Simple use-case

In plain JavaScript with the ESM-Framework:

import * as Dise from "...<Your relative path>.../TemplateFramework/esm/EditorAPI/index.js";

const editorAPI = new Dise.EditorAPI();
let data = {};

editorAPI.onData = (cmsData) => {
  // Absorb the data from the CMS.
  // This is called as soon as the EditorAPI
  // has connected, before the template is started.
  data = cmsData;
};

// Whenever the editor-data is modified, update the CMS-state
// which will also restart/update the preview-template.
editorAPI.setData(data);

Selecting Media from the CMS

Call the (async) method selectContent(). An array of Media objects will be returned, possibly empty.

interface Media {
    url: string;
    contentId: string;
    name?: string;
}

async function selectMedia(): Promise<Media | void> {
  try {
    const medias = await editorAPI.selectContent();
    return medias?.[0];
  }
  catch (err) {
    // It is CMS-specific what is considered an error
  }
}

selectContent() takes an optional object as an argument. This is sent to the CMS in the request and is CMS-specific. For example, the media-type filter or default folder may be customizable. Please check other documentation or contact Dise for a specification)

Advanced

As an alternative to setting the event-callbacks on the EditorAPI object itself, an option is to supply an object/class-instance with the implementation. This is similar to how a TemplateController is constructed with a View and allows for better separation of concerns and easier state-management:

const editorAPI = new Dise.EditorAPI({
  onData: (cmsData: any) => {
    // ...
  },
  onLoad: () => {
    // ...
  }
});

The full list of possible event-callbacks is:

interface IEditorEvents {
  // Set this to true to get verbose console-logging of event-traffic:
  enableEventLogging?: boolean;
  // Called on Editor-page Load completed, but before the CMS is connected.
  onLoad?(): void;
  // Initial Message-data from CMS:
  onData?(data: object): void;

  // Template events:

  // The Template has loaded
  onReady?(data?: any): void;
  // Player-metadata sent to the Template
  onMetadata?(metadata: PlayerMetadata): void;
  // The response from the Template on Preload complete
  onPreloadComplete?(preloadCompleteResponse: OnPreloadReadyReturn): void;
  onTemplateEnded?(): void;
  onTemplateError?(err?: Error): void;
  // A message from the Preview-Template to the Editor
  onTemplateMessage?(msg: any): void;
}

Communication between Editor and Preview-Template

The EditorAPI and Template Controller/View-implementations have the same functionality, but with slightly different naming:

In Editor:

  • To send: editorAPI.templateMessage(data: any)

  • To receive, implement: onTemplateMessage(data: any)

In TemplateView:

  • To send: controller.editorMessage(data: any)

  • To receive, implement: onEditorMessage(data: any)

Caveats & Limitations

Some care has to be taken when implementing a template-editor pair.

Template behavior in Edit-Preview

In normal cases, with the default editor, the CMS will fill the message-object with default values before preloading the Template. With a Custom Editor, on the other hand, the CMS has no concept of “fields“ and will send an empty object as the message when preload is called on a new message-instance.

As a consequence, the Template must be able to handle this case when it is hosted in Edit-mode. See the Tenants Sample-Template for an example.

Calling setData before onData is received

If the editor calls with data before the CMS has had a chance to send the current message, a conflict may occur.

At time of writing, however, the CMS does not send onData at all unless an existing message is being edited. This will be remedied in an upcoming release.

The Tenants Sample-Editor handles this with a 500ms timeout as a workaround (see the Service implementation).

Development

As with Template-development with PlayerSim, a hosting-page is provided for testing and debugging Editors. This can be found as “editorhost.html“ on the root of the SampleTemplates repository.

The host functions similarly to the CMS. It uses IFrames for the Custom Editor page and the PlayerSim and uses its own “EditorSim“ implementation. It also displays the current message as a JSON-dump:

To set this up for testing:

  1. Update/pull the SampleTemplates repository.

  2. Make sure the PlayerSimulator and TemplateFramework submodules are up-to-date on the master/main branches.

  3. npm install

  4. build (“npm run build-Tenants” if you want to test with that).

The editorhost simply opens the PlayerSim-page (normally Index.html) in an iframe.

The correct template will have to be selected in Index.html as it is not supplied by the editor-host.

Certain settings are configurable in editorhost.html in case your setup is different:

        // Edit these two to select your editor and initial data:
        // (set mockDataUrl to something nullish to start from scratch)
        const editorUrl = 'Samples/Tenants/editor/editor.html';
        let mockDataUrl = 'Samples/Tenants/MockData/Content.json';

        function start() {
            editorSim = new EditorSim({
                editorIframe: document.getElementById('Editor'),
                templateIframe: document.getElementById('Preview'),
                playerSimUrl: 'Index.html',
                editorUrl: editorUrl,
                mockDataUrl: mockDataUrl
            }, (data) =>
                document.getElementById('JSON').textContent = JSON.stringify(data, null, '  '));
        }