Skip to content

Customising The Toolbar

Joel Parke edited this page Jul 14, 2016 · 24 revisions

There are several parts to customising the toolbar;

  • changing the classes/styling
  • working with the plugin interface for individual toolbar buttons (referred to as tools from here on)
  • working with multiple toolbars and editors

Styling the Default Toolbar

The toolbar is an implementation of Bootstraps Button Toolbar. There are 4 HTML attributes you can set on the text-angular tag to change the defaults, here is shown what the defaults are:

<div text-angular ng-model="htmlVariable" ta-toolbar-class="btn-toolbar" ta-toolbar-group-class="btn-group" ta-toolbar-button-class="btn btn-default" ta-toolbar-active-button-class="active"></div>

Converts to:

<div text-angular ...>
  <div class="ta-toolbar btn-toolbar">
    <div class="btn-group">
      <button type="button" class="btn btn-default">...</button>
      <button type="button" class="btn btn-default active">...</button>
    </div>
  </div>
  ...
</div>

Defining the toolbar

The toolbar is comprised of several groups of buttons, these are defined by another HTML tag. This tag, unlike the previous ones, is a compiled tag so you can use a javascript variable or the input as in the example:

<text-angular ta-toolbar="[['h1','h2','h3'],['bold','italics']]"></text-angular>

This would output the following toolbar (simplified from full version):

...
<div class="ta-toolbar btn-toolbar">
  <div class="btn-group">
    <button type="button" class="btn btn-default ng-scope" unselectable="on">H1</button>
    <button type="button" class="btn btn-default ng-scope" unselectable="on">H2</button>
    <button type="button" class="btn btn-default ng-scope" unselectable="on">H3</button>
  </div>
  <div class="btn-group">
    <button type="button" class="btn btn-default ng-scope" unselectable="on"><i class="fa fa-bold"></i></button>
    <button type="button" class="btn btn-default ng-scope" unselectable="on"><i class="fa fa-italic"></i></button>
  </div>
</div>
...

The default available buttons are:

  • h1
  • h2
  • h3
  • h4
  • h5
  • h6
  • p
  • pre
  • quote
  • bold
  • italics
  • underline
  • strikeThrough
  • ul
  • ol
  • undo
  • redo
  • clear
  • justifyLeft
  • justifyCenter
  • justifyRight
  • justifyFull
  • indent
  • outdent
  • html
  • insertImage
  • insertLink
  • insertVideo
  • wordcount
  • charcount

The string values put into the ta-toolbar arrays are keys for tools that have been defined as shown in the next section.

Creating and Editing Tools

All of the plugin tools are stored on the textAngularManager and should be setup with a decorator. To explain the parts of a tool I am going to show a quick tutorial on how to add a button to style the current selection red. (The wrapSelection function as used below internally calls the document.execCommand(command, false, opt)). This assumes that somewhere the following css exists .red{ color: red; }

First the javascript to create the Tool Plugin:

module.config(function($provide){
	$provide.decorator('taOptions', ['taRegisterTool', '$delegate', function(taRegisterTool, taOptions){
		// $delegate is the taOptions we are decorating
		// register the tool with textAngular
		taRegisterTool('colourRed', {
			iconclass: "fa fa-square red",
			action: function(){
				this.$editor().wrapSelection('forecolor', 'red');
			}
		});
		// add the button to the default toolbar definition
		taOptions.toolbar[1].push('colourRed');
		return taOptions;
	}]);
});

Injection: textAngular has several factories that you may wish to access in your decorator. Those are injected in the angular way by adding them to the javascript code. For example (I list many of them here for clarity only):

module.config(function($provide){
	$provide.decorator('taOptions', ['taRegisterTool', 'taSelection', 'taBrowserTag', 'taTranslations', 
                                     'taToolFunctions', '$delegate', 
                                     function(taRegisterTool, taSelection, taBrowserTag, taTranslations, 
                                              taToolFunctions, taOptions){
		// $delegate is the taOptions we are decorating
		// register the tool with textAngular
   ...
   ...
   ...

Note: For a full list of commands used in wrapSelection (passes through to execCommand) and their support across browsers refer to http://quirksmode.org/dom/execCommand.html an alternative reference point is MDN. As follows is an explanation of the individual keys of the tool plugin object: (Note that this will always refer to the scope of the tool)

First the parameters that may be passed in:

  • action: [function(deferred, restoreSelection)] a function that is executed on clicking on the button - this will allways be executed using ng-click and will overwrite any ng-click value in the display attribute. The function is passed a deferred object ($q.defer()), if this is wanted to be used return false; from the action and manually call deferred.resolve(); elsewhere to notify the editor that the action has finished. restoreSelection is only defined if the rangy library is included and it can be called as restoreSelection() to restore the users selection in the WYSIWYG editor.
  • display: [string]? Optional, an HTML element to be displayed as the buton. The scope of the button is the tool definition object with some additional functions. If set this will cause buttontext and iconclass to be ignored.
  • buttontext: [string]? if this is defined it will replace the contents of the button element.
  • class: [string]? if this is defined it will replace the CSS classes of the button element.
  • iconclass: [string]? if this is defined an icon (<i>) will be appended to the button element with this string as it's class
  • activestate: [function(commonElement)]? this function is called on every caret movement, if it returns true then the class taOptions.classes.toolbarButtonActive will be applied to the display element, else the class will be removed. If the rangy library is loaded commonElement will be an angular.element object that contains ALL of the selection.
  • disabled: [function()]? if this function returns true then the tool will have the class taOptions.classes.disabled applied to it, else it will be removed.

The functions available on the Tool scope are (ie this. in the action, activestate or disabled functions):

  • name: [string] the name of the tool, this is the first parameter passed into taRegisterTool
  • isDisabled: [function()] returns true if the tool is disabled, false if it isn't
  • displayActiveToolClass: [function(boolean)] returns true if the tool is 'active' in the currently focussed toolbar

Some more details on the use of the action and activeState functions.

action: This function is called whenever the button is clicked. In this case we are using textAngulars wrapSelection function which internally calls `document.execCommand('formatBlock', false, '') in our example and then updates the model.

activeState: This is called whenever the editor is updated and/or the text cursor is moved (keyup event), the active variable on the scope will be set to the result of this function. In this example it is difficult to detect if the cursor is in a block of red styled text so we return false.

Finally to add this new plugin tool to the example toolbar in Defining the Toolbar we do the following:

<text-angular ta-toolbar="[['h1','h2','h3'],['bold','italics','colourRed']]"></text-angular>

Multiple Toolbars

In textAngular 1.2.0 we have added the functionality to allow a link an editor to any number of toolbars, also a toolbar can be linked to multiple editors. The styling attributes available for the text-angular-toolbar directive are: ta-toolbar, ta-toolbar-class, ta-toolbar-group-class, ta-toolbar-button-class, ta-toolbar-button-active-class, ta-focussed-class. All of these can be set on any individual toolbar or set on the text-angular directive to be placed on the automatically generated toolbar if the ta-target-toolbars value isn't set.

NOTE: Using ta-target-toolbars="''" or ta-toolbar="''" will load up an editor with NO toolbar, you must input an empty string as leaving it blank is the same as omitting it due to the way angularjs evaluates attributes.

Below are two examples of how to do this:

Using multiple toolbars for one editor

<text-angular-toolbar name="toolbar1"></text-angular-toolbar>
<text-angular-toolbar name="toolbar2"></text-angular-toolbar>
<text-angular name="htmlcontent" ng-model="data.htmlcontent" ta-target-toolbars='toolbar1,toolbar2'></text-angular>

Using multiple editors for one toolbar

<text-angular-toolbar name="toolbar"></text-angular-toolbar>
<text-angular name="htmlcontent1" ng-model="data.htmlcontent1" ta-target-toolbars='toolbar'></text-angular>
<text-angular name="htmlcontent2" ng-model="data.htmlcontent2" ta-target-toolbars='toolbar'></text-angular>