ddraper

Creating the Markdown with Preview Editor Widget

Blog Post created by ddraper on Oct 5, 2016

Introduction

In my last blog post I showed how easy it is to add new items into the Aikau Document Library "Create Content..." menu. In this particularly example I was showing how to create markdown content but in order to do this I actually needed to create a new widget. It's been suggested in the past that I should try to provide more examples of how to create custom widgets so I figured that it would be as good a time as any...

 

Aikau already had widgets for both editing content with syntax highlighting and previewing markdown but these didn't yet exist as a single entity. The great thing about Aikau is that it incredibly easy to combine existing widgets together to make a new widget that can be referenced as an individual entity. We learned our lessons from the original YUI2 widgets that it is important to define fine-grained widgets that can be easily composed rather than creating large-grained coarse widgets that become increasingly hard to customize over time. The Dojo _WidgetBase module that Aikau builds upon also provides excellent inheritance capabilities that we are able to leverage.

 

So our objective was to create a new widget that allowed editing of markdown and showed a preview of the changes as they happened. As this widget is to be used for creating content submitted through a form then it needed to also inherit from BaseFormControl.  This meant that obvious candidate to extend would be the existing CodeMirrorEditor form control as it had the correct ancestry and could be configured to show markdown syntax highlighting.

 

Creating the Widget

The initial code to extend the widget looks like this:

 

define(["dojo/_base/declare",
        "alfresco/forms/controls/CodeMirrorEditor"],
        function(declare, CodeMirrorEditor) {
   return declare([CodeMirrorEditor], {
   });
});

 

This essentially defines a new widget that is exactly the same as the CodeMirrorEditor, now we can start to change a few things. First we know that we want to use markdown highlighting - this is done by setting the editMode:

 

define(["dojo/_base/declare",
        "alfresco/forms/controls/CodeMirrorEditor"],
        function(declare, CodeMirrorEditor) {
   return declare([CodeMirrorEditor], {
      editMode: "markdown"
   });
});

 

We also know that we want to change how the form control itself is constructed. We do this by extending (through a call to this.inherited(arguments) the createFormControl function:

 

define(["dojo/_base/declare",
        "alfresco/forms/controls/CodeMirrorEditor"],
        function(declare, CodeMirrorEditor) {
   return declare([CodeMirrorEditor], {
      editMode: "markdown",

      createFormControl: function alfresco_forms_controls_MarkdownWithPreviewEditor__createFormControl(config, domNode) {
         var widget = this.inherited(arguments);
         return widget;
      }
   });
});

 

We want to also create the Markdown widget. This requires a new DOM element created, and a new Markdown widget created that binds to it:

 

createFormControl: function alfresco_forms_controls_MarkdownWithPreviewEditor__createFormControl(config, domNode) {
   var widget = this.inherited(arguments);
   this.previewNode = domConstruct.create("div", { 
      style: "height:" + this.height + "px;width:" + this.width + "px;",
      className: "alfresco-forms-controls-MarkdownWithPreviewEditor__previewNode"
   }, this._controlNode);
   var preview = this.createWidget({
      name: "alfresco/html/Markdown",
      config: {
         markdown: this.initialValue
      }
   });
   preview.placeAt(this.previewNode);
   return widget;
},

 

In order for the Markdown widget to show the changes as they happen it is necessary to define a subscription topic that it can uses to listen for changes. By calling the generateUuid function we can create a unique topic to prevent any "cross talk" between widgets.

 

this.generatePreviewTopic = this.generateUuid();

this.previewNode = domConstruct.create("div", { 
   style: "height:" + this.height + "px;width:" + this.width + "px;",
   className: "alfresco-forms-controls-MarkdownWithPreviewEditor__previewNode"
}, this._controlNode);

var preview = this.createWidget({
   name: "alfresco/html/Markdown",
   config: {
      markdown: this.initialValue,
      subscriptionTopics: [this.generatePreviewTopic]
   }
});
preview.placeAt(this.previewNode);

 

Now we need to ensure that when the user makes edits they are published on the generatePreviewTopic that the Markdown widget will be subscribed to. This means extending the onEditorChange function to publish the new value each time it is updated.

 

onEditorChange: function alfresco_forms_controls_MarkdownWithPreviewEditor__onEditorChange(editor, changeObject) {
   this.inherited(arguments);
   this.alfPublish(this.generatePreviewTopic, {
      markdown: this.lastValue
   });
}

 

Now we have an extended version of the CodeMirrorEditor that is configured for markdown editing, with an additional Markdown preview widget that is updated on changes.

 

Summary

Although this doesn't show every change (these can be reviewed in the associate pull request) I've tried to capture the essential elements of how two separate widgets were pulled together to make a new one. The steps I took to create this were obvious to me because of my familiarity with Aikau - but if you need help on getting started creating a new widget then just create a question in the discussion forums and I'll be happy to provide you with suggestions on how to get started.

Outcomes