-
Notifications
You must be signed in to change notification settings - Fork 214
Rich Text Editor
The RichTextEditor
component recently debuted in Montage 0.8. This provides a way for the user to enter text that will be transformed to HTML and can have styles applied to it.
At its most basic level, the RichTextEditor
is a wrapper around a div
element with the HTML5 contentEditable
attribute applied. There is no UI by default beyond the div
for entering text, but an API is provided to allow you to hook up controls to enable the various styling options.
Creating a rich text editor with the very basic font and text styling controls can be done trivially with no JavaScript (beyond the JSON serialization) required! Lets make a quick example that allows us to make the text bold, italic and set an underline.
First of all we need to create a toolbar with buttons for each of the options, and an area to enter the text:
<menu type="toolbar">
<div class="montage-buttonGroup font-styles">
<button data-montage-id="bold">B</button>
<button data-montage-id="italic">I</button>
<button data-montage-id="underline">U</button>
</div>
</menu>
<div data-montage-id="editor" class="montage-textarea">
<h1>Hello RichText world!</h1>
</div>
The menu
element holds the buttons we will use for styling the text. Montage doesn’t do anything special with this element; I’ve just included it for semantic value.
Inside the menu I’ve included a wrapper div
to group together the three text controls. I’ve applied a class to it so that the stylesheet can visually display the buttons as a group.
Each button and the div
element below the menu are given their own data-montage-id
so we can hook them up to the serialization.
Lets also add a data-auto-package
attribute to the Montage script element, so that we don’t need to create a package.json file. As this example has no external dependencies, it is ideal for this:
<script src="../montage/montage.js" data-auto-package></script>
Now to where the magic really happens. Lets hook up the rich text editor to our div
in our serialization code:
"editor": {
"prototype": "montage/ui/rich-text-editor/rich-text-editor.reel",
"properties": { "element": {"#": "editor" } }
}
Now you have a text area that you can type into, but you can’t apply any commands. For that we have to hook up the buttons. Lets try with the bold button first:
"bold": {
"prototype": "montage/ui/toggle-button.reel",
"properties": {
"element": {"#": "bold"},
"pressedClass": "active"
},
"bindings": { "pressed": { "<->": "@editor.bold" }
}
}
This sets the bold
element to be a toggle button, and uses the pressedClass
property to set a class of active
when the button is pressed.
When then set a two way binding between the pressed
property and the bold
boolean property of the RichTextEditor
component. When the pressed property is updated it will change the bold value between true
and false
, and thus bold or unbold the text. When some text is selected that is already bold, it will update the pressed property of the toggle to set it to true.
That is all there is to it for actions that update a boolean property such as bold, italic, and underline. Lets add the serialization for the other two buttons as well. They work in exactly the same way:
"italic": {
"prototype": "montage/ui/toggle-button.reel",
"properties": {
"element": {"#": "italic"},
"pressedClass": "active"
},
"bindings": { "pressed": { "<->": "@editor.italic" } }
},
"underline": {
"prototype": "montage/ui/toggle-button.reel",
"properties": {
"element": {"#": "underline"},
"pressedClass": "active"
},
"bindings": { "pressed": { "<->": "@editor.underline" } }
}
As you’ve just seen, setting boolean properties is a trivial affair. For properties that accept one of a number of predefined values, it becomes a little more complex, but wont be alien to those of you who know how Montage works.
I’ll show you how to set this up by creating three buttons to set the text alignment to either left, right, or center, by updating the justify
property.
First of all we need to add the buttons to allow the user to specify the alignment. For this I added an additional button group. I’ve included the Font Awesome font that was designed to be used with Twitter Bootstrap to display icons for each button:
<div class="montage-buttonGroup alignment-styles">
<button data-montage-id="left"><i class="icon-align-left" title="left align"></i></button>
<button data-montage-id="center"><i class="icon-align-center" title="center align"></i></button>
<button data-montage-id="right"><i class="icon-align-right" title="right align"></i></button>
</div>
For this example we will need to add a controller object to the serialization, which will point to our editor
object that we defined at the top of the serialization. This will give us access to this object when we need to use it in the controller object that we instantiate later in our JavaScript file.
"controller": {
"prototype": "Controller",
"properties": {
"editor": { "@": "editor"}
}
}
Now that we have a controller, we need to hook it up to the buttons that will handle the text alignment. All three serialization blocks are the same except the object name and the element they point to, so I’ll just include the serialization for the right button here. You can just repeat it for the other two buttons, editing the relevant values.
"right": {
"prototype": "montage/ui/toggle-button.reel",
"properties": {
"element": {"#": "right"},
"preventFocus": true
},
"listeners": [
{
"type": "action",
"listener": {"@": "controller"}
}
]
}
Here we’re setting up an action
listener, which will be handled via the controller object we just defined. As there is no identifier
specified in the listener, the event listener will use the default name, which in this case is handleRightListener
. This capitalizes the object name, and prefixes it with handle
, and prepends it with Action
.
We also need to include a preventFocus
property. This stops the button from taking focus away from the selected text in the rich text area. If we allowed the button to steal focus, the alignment style wouldn’t apply as it wouldn’t know what text it should apply the style to.
Now all the wiring is complete, when a user clicks the button for right alignment, it will fire the handleRightAction
event. Lets create a JavaScript file called controller.js. This is the same name as set up in the controller object.
Inside this file we first need to import Montage core, and create and export the Controller
object. We also need to define the editor
that we specified in our controller object in the serialization:
var Montage = require("montage/core/core").Montage;
exports.Controller = Montage.create(Montage, {
editor: {
value: null
}
})
Next we will define the handleRightAction
method inside the same object. This sets the editor.justify
property to right:
handleRightAction: {
value: function(event) {
this.editor.justify = "right";
this.setActive("right");
}
}
All of the other handlers can be created in the same way. The valid values for the editor.justify
property are left, right, center, and full.
As the active state for the left, right and center buttons should be mutually exclusive, I’ve also created an setActive
method. This removes focus from the previously active button and adds focus to the currently selected button instead, rather than relying on the button’s inbuilt pressedClass
property, like we did for the bold, italic and underline buttons.
Armed with this knowledge, you should be able hook up controls for the other RichTextEditor
properties yourself. Hopefully this post will inspire you to try out the rich text capabilities in Montage for yourself.