Borak - software developer illustrational
Blog
10.12. 2019 / 00:00

javascript

typescript

library

Froala

Angular 8

Froala 3.0 extensions in Angular 8.0

There are many ways to integrate your Froala library into the frontend. The integration with Angular 4+ does not have to be awkward. It is even more straightforward with the latest release of version 3 that has introduced the non JQuery API.

Froala 3.0

Angular solution

The extension for Angular solution I am gonna introduce is about to leverage dependency injection services and one wrapper class. As I mentioned, we will work with Froala’s 3.0.0+ version.

Extending Froala

There are basically 3 ways to extend Froala with your custom payload. Each focuses on different part of the Froala’s lifecycle. Together they enable to create fully customized solution.

Types of extensions

  • Buttons
  • Icons
    • Template definition
  • Plugins

Buttons, commands and icons

It all begins with a button. Froala has a lot of build-in functionalities already implemented. If you need more, you will probably start with a button and accompanied functionality. Button in Froala is combination of the command accompanied with the icon definition.

The simplest way to create custom buttons is to register command and icon under the same name.

FroalaEditor.DefineIcon(‘doSmth‘,{});
FroalaEditor.RegisterCommand(‘doSmth‘, command);

Angular solution

Froala is initialized by calling its constructor. The constructor expects two parameters:
  • HTML element at which the Froala’s instance create the editable space.
  • Option object – to define some more detailed setup telling the instance, how the editor should behave and what features it should expose.
It’s a good idea to isolate the initialization of the Froala editor into separate class. This class, let’s call it a FroalaWrapper, can expose it’s own API to handle the initialization of the editor.
It also expose the Froala editor instance itself.

Once the editor is destroyed, the FroalaWrapper will also do the necessary cleaning jobs.

FroalaWrapper can be then consumed and handled by some Angular service, which will be available to the rest of the application. This service will expose the createEditor method, which will accept the options and will create new instance of the FroalaWrapper.

Wrapper and editorService classes

Wrapper class is more like standalone class of the app. Instance of which will be destroyed at the end of the editor’s lifecycle. It’s constructor will accept the options, that will be passed to the Froala’s constructor.

The options may look, for example, like this:

export type FroalaEditorOptions = {
element: HTMLElement; à the element which the Froala editor will work with
froalaKey: string;  à the license key for the Froala
isToolbarEnabled: boolean; à some additonal setup follows
selection: RelativeSelection;
locale: string;
 };
The FroalaWrapper class is not injectable. It is only meant to be a low-level handler of the Froala’s instance itself.

Except its constructor, it has one other important method – ‘destroy’. This method mainly handles the destruction of the Froala instance. It should handle all of our application’s features.

So far good.

To sum up. The FroalaWrapper is meant to handle the Froala’s instance API. The most important part of it is the definition of the extensions.

The editorService is injectable and passes to the FroalaWrapper methods stuff, that is present in the angular injectors. The most important part is the method for the creation of the FroalaWrapper instance.

The extensions

The extensions are meant to be self-contained classes, that will be achievable through the Angular injector.
The shape of the extensions is defined by the simple Extension interface:

export interface Extension {
initialize: () => void;
 }
As there are more extensions in the application, we will register them in the Angular module under one injection token.

export const ExtensionToken = new InjectionToken('FroalaExtensionToken');
The extensions can be then passed to the Angular module injector like this:
@NgModule({
providers: [
   { provide: ExtensionToken, useClass: OurExtension, multitrue },
  ],
 })
 export class Module {}
The trick is using the ‚multi: true‘ option that will make all the extension classes present under the Extension token as the array in the injector.

Now, there is the right time to inject all the extensions into the editor service.
export class EditorService {
public editor: FroalaEditorWrapper = null;
 
constructor(
@Inject(IFroalaExtensionToken) private froalaExtensions: IFroalaExtension[]
  ) {
this.froalaKey = config.froalaKey;
  }
}
Now we have all the extensions registered in the app’s injector. The EditorService exposes method ‚initializeExtensions‘, which simply iterate through all the injected extensions and call their intialize method.

private initializeExtensions () {
this.froalaExtensions.forEach((extension: IFroalaExtension) => {
   extension.initialize(this);
  });
 }
There is one more method, activateEditor, which works with the FroalaWrapper class and calls the createEditorExtraPlugins method among others.
activateEditor(options: EditorOptions) {
this initializeExtensions (); ==> here define the extensions
 
if (this.editor) {
throw new Error('Editor has been already activated');
  }
 
 
this.editor = new FroalaEditorWrapper({
disableLinkPopupsForClasses: options.disableLinkPopupsForClasses,
element: options.element,
froalaKeythis.froalaKey,
isToolbarEnabled: options.isToolbarEnabled,
selection: options.selection,
localethis.locale,
  });
 }
So far, our edtiorService look like this:
export class EditorService {
public editor: FroalaEditorWrapper = null;
 
constructor(
@Inject(FroalaExtensionToken) private froalaExtensions: FroalaExtension[]
  ) {
this.froalaKey = config.froalaKey;
  }
private initializeExtensions() {
this.froalaExtensions.forEach((extension: Extension) => {
   extension.initialize(this);
  });
 }
activateEditor(options: EditorOptions) {
this.createEditorExtraPlugins(); --> here define the extensions
 
if (this.editor) {
throw new Error('Editor has been already activated');
  }
 
 
this.editor = new FroalaEditorWrapper({
disableLinkPopupsForClasses: options.disableLinkPopupsForClasses,
element: options.element,
froalaKeythis.froalaKey,
isToolbarEnabled: options.isToolbarEnabled,
selection: options.selection,
localethis.locale,
  });
 }
}

 

Button and Plugin extensions

The extension will implement the ‚Extension‘ interface, which postulates, that it should define an ‚implement‘ method. This method will call functions that define either the Button or Plugin extension on the Froala instance.

Button definition function

This function takes the parameters defined by the ‚FroalaButtonSetup‘ interface. As metioned, the button is defined as the combination of the command definition and the icon definiton. So the FroalaButtonSetup looks like this:
export interface FroalaButtonSetup {
icon: FroalaIcon;
name: string;
command: FroalaButtonCommand;
 }
export interface FroalaIcon {
NAME: string;
SVG_KEY?: string;
template?: string;
 }
export interface FroalaButtonCommand {
icon?: string;
title: string;
focus: boolean;
undo: boolean;
refreshAfterCallback: boolean;
callback: () => void;
 }
Now, that we have all we need for the definition of button, we can define the ‚defineButton‘ function:

import { FroalaFramework } from './froala-editor-wrapper';

export function defineButton(setup: FroalaButtonSetup): void {
FroalaFramework.DefineIcon(setup.name, setup.icon);
FroalaFramework.RegisterCommand(setup.name, setup.command);
 }
The job is to simply define command and icon trough the Froala’s static methods.
Let’s look at the FroalaWrapper instance (the instance serving as the wrapper around the Froala instance):

export const FroalaFramework = Froala;
 
 export class FroalaEditorWrapper {
public froalaEditor: Froala;
 
constructor(options: FroalaEditorOptions) {
         this.froalaEditor = new Froala(this.parentElement,
{
         key: options.froalaKey,
          multiLinefalse,
          enter: FroalaFramework.ENTER_BR,
          placeholderText'',
          language: options.locale,
});
  }
}
In the FroalaWrapper’s module we firstly export froala class. Down there is the FroalaWrapper class by itself. The instance of the Froala editor is present as the ‚froalaEditor‘ property.

Conclusion

We have created the infrastructure to handle the Froala instance and defined the way to add custom extension to the Froala editor instance.

More by Borak

To maximalize your user experience during visit to my page, I use cookies.More info
I understand

#BORAKlive

This page is subjected to the Creative Common Licence. Always cite the Author - Do not use the page's content on commercial basis. Comply with the licence 3.0 Czech Republic.
go to top