/ RichTextEditor / Third-Party Integration
Search results

Third-Party Integration in Angular RichTextEditor component

21 Dec 2022 / 6 minutes to read

The Rich Text Editor can be integrated with third-party to suite the application scenario.

To get start quickly with Third-Party Integration for Angular Rich Text Editor component, refer to the video below.

CodeMirror Integration

RichTextEditor comes with a basic HTML source editor through the view-source property. CodeMirror plugin can be used to highlight the syntax of HTML. CodeMirror plugin for Rich Text Editor makes editing of HTML source code with a pleasant experience.

Import necessary CSS and JS files of CodeMirror to the HTML page.

Required JS files of code mirror.

Copied to clipboard
 <script src="scripts/CodeMirror/codemirror.js" type="text/javascript"></script>
 <script src="scripts/CodeMirror/javascript.js" type="text/javascript"></script>
 <script src="scripts/CodeMirror/css.js" type="text/javascript"></script>
 <script src="scripts/CodeMirror/htmlmixed.js" type="text/javascript"></script>

Required CSS file of code mirror

Copied to clipboard
 <link href="scripts/CodeMirror/codemirror.min.css" rel="stylesheet" />

Add a custom icon for HTML source editor in the toolbar of Rich Text Editor using the template option of ToolbarSettings, define the code mirror plugins, and then pass the Rich Text Editor content as argument in the actionComplete event.

Copied to clipboard
import { Component, ViewChild } from '@angular/core';
import { ToolbarService, LinkService, ImageService, HtmlEditorService, RichTextEditorComponent,CountService} from '@syncfusion/ej2-angular-richtexteditor';
import { createElement, addClass, removeClass, Browser } from '@syncfusion/ej2-base';

@Component({
selector: 'app-root',
template: `<ejs-richtexteditor #toolsRTE id='alltoolRTE' [toolbarSettings]='tools' [showCharCount]='true' (actionComplete)='actionCompleteHandler($event)' [maxLength]='maxLength'>
        <ng-template #valueTemplate>
          <p>The Rich Text Editor is WYSIWYG ("what you see is what you get") editor useful to create and edit content, and return the valid <a href="https://ej2.syncfusion.com/home/" target="_blank">HTML markup</a> or <a href="https://ej2.syncfusion.com/home/" target="_blank">markdown</a> of the content</p>
            <p><b>Toolbar</b></p>
            <ol>
                <li>
                    <p> Toolbar contains commands to align the text, insert link, insert image, insert list, undo/redo operations, HTML view, etc </p>
                </li>
                <li>
                    <p> Toolbar is fully customizable </p>
                </li>
            </ol>
            <p><b>Links</b></p>
            <ol>
                <li>
                    <p>You can insert a hyperlink with its corresponding dialog </p>
                </li>
                <li>
                    <p>Attach a hyperlink to the displayed text. </p>
                </li>
                <li>
                    <p> Customize the quick toolbar based on the hyperlink </p>
                </li>
            </ol>
            <p><b>Image.</b></p>
            <ol>
                <li>
                    <p> Allows you to insert images from an online source as well as the
                    local computer </p>
                </li>
                <li>
                    <p> You can upload an image </p>
                </li>
                <li>
                    <p>Provides an option to customize quick toolbar for an image </p>
                </li>
            </ol>
        </ng-template>
    </ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, CountService]
})
export class AppComponent  {
@ViewChild('toolsRTE') public rteObj: RichTextEditorComponent;
public tools: object = {
    items: ['Bold', 'Italic', 'Underline', 'StrikeThrough',
    'FontName', 'FontSize', 'FontColor', 'BackgroundColor',
    'LowerCase', 'UpperCase', '|',
    'Formats', 'Alignments', 'OrderedList', 'UnorderedList',
    'Outdent', 'Indent', '|',
    'CreateLink', 'Image', '|', 'ClearFormat', 'Print',
    'SourceCode', 'FullScreen', '|', 'Undo', 'Redo']
};
public maxLength: number = 1000;
public textArea: HTMLElement;
public myCodeMirror: any;
ngAfterViewInit(): void {
let rteObj: RichTextEditorComponent = this.rteObj;
setTimeout(() => { this.textArea = rteObj.contentModule.getEditPanel() as HTMLElement; }, 600);
}
public mirrorConversion(e?: any): void {
let id: string = this.rteObj.getID() + 'mirror-view';
let mirrorView: HTMLElement = this.rteObj.element.querySelector('#' + id) as HTMLElement;
let charCount: HTMLElement = this.rteObj.element.querySelector('.e-rte-character-count') as HTMLElement;
if (e.targetItem === 'Preview') {
    this.textArea.style.display = 'block';
    mirrorView.style.display = 'none';
    this.textArea.innerHTML = this.myCodeMirror.getValue();
    charCount.style.display = 'block';
} else {
    if (!mirrorView) {
        mirrorView = createElement('div', { className: 'e-content' });
        mirrorView.id = id;
        this.textArea.parentNode.appendChild(mirrorView);
    } else {
        mirrorView.innerHTML = '';
    }
    this.textArea.style.display = 'none';
    mirrorView.style.display = 'block';
    this.renderCodeMirror(mirrorView, this.rteObj.value);
    charCount.style.display = 'none';
}
}

public renderCodeMirror(mirrorView: HTMLElement, content: string): void {
this.myCodeMirror = CodeMirror(mirrorView, {
    value: content,
    lineNumbers: true,
    mode: 'text/html',
    lineWrapping: true,

});
}
public actionCompleteHandler(e: any): void {
if (e.targetItem && (e.targetItem === 'SourceCode' || e.targetItem === 'Preview')) {
    (this.rteObj.sourceCodeModule.getPanel() as HTMLTextAreaElement).style.display = 'none';
    this.mirrorConversion(e);
} else {
    setTimeout(() => { this.rteObj.toolbarModule.refreshToolbarOverflow(); }, 400);
}
}
}
Copied to clipboard
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RichTextEditorAllModule } from '@syncfusion/ej2-angular-richtexteditor';
import { AppComponent } from './app.component';
import { DialogModule } from '@syncfusion/ej2-angular-popups';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        RichTextEditorAllModule,
        DialogModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
Copied to clipboard
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
Copied to clipboard
<!DOCTYPE html>
<html lang="en">

<head>
            <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js"></script>
    <title>Syncfusion Angular Rich Text Editor</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript UI Controls" />
    <meta name="author" content="Syncfusion" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-richtexteditor/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-dropdowns/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-lists/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-navigations/styles/material.css" rel="stylesheet" />
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.9.0/zone.min.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.19/marked.js" type="text/javascript"></script>
    <script src="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/codemirror.js" type="text/javascript"></script>
    <script src="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/javascript.js" type="text/javascript"></script>
    <script src="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/css.js" type="text/javascript"></script>
    <script src="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/xml.js" type="text/javascript"></script>
    <script src="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/htmlmixed.js" type="text/javascript"></script>
    <link href="https://jsplayground.syncfusion.com/16.1.0.24/scripts/web/codemirror/codemirror.min.css" rel="stylesheet" />
    <script src="systemjs.config.js"></script>
    <style>
    #loader {
        color: #008cff;
        font-family:  'Helvetica Neue','calibiri';
        font-size: 16px;
        height: 40px;
        left: 45%;
        position: absolute;
        top: 45%;
        width: 30%;
    }
     .e-code-mirror::before {
        content: '\e345';
    }
    .CodeMirror-linenumber,
    .CodeMirror-gutters {
        display: none;
    }

    #special_char,
    .char_block {
        display: inline-block;
    }
    .char_block.e-active {
        /* box-shadow: inset 3px 3px 7px 0px; */
        outline: 1.5px solid;
    }

    .char_block {
        width: 30px;
        height: 30px;
        line-height: 30px;
        margin: 0 5px 5px 0;
        text-align: center;
        vertical-align: middle;
        border: 1px solid #DDDDDD;
        font-size: 20px;
        cursor: pointer;
        user-select: none;
    }

    #custom_tbar,
    #custom_tbar div{
        cursor: pointer;
    }


    #rteSection {
        height: 500px;
    }
  .e-rte-quick-popup .e-rte-quick-toolbar .e-roatate-left::before {
        content: "\e76e";
    }

    .e-rte-quick-popup .e-rte-quick-toolbar .e-roatate-right::before {
        content: "\e726";
    }

    .e-richtexteditor textarea.e-content {
      float: left;
    }
    .e-richtexteditor .e-rte-content {
      overflow: hidden;
    }
    .e-rte-content .e-content.e-pre-source{
      width: 100%;
    }

    .property-panel-content td{
        width: 50%;
    }
    .property-panel-content td div {
        padding-left: 10px;
        padding-top: 10px;
    }

    .e-icon-btn.e-active .e-md-preview::before {
        content: '\e350';
    }

	.e-icon-btn .e-md-preview.e-icons::before {
		content: '\e345';
	}
    .e-icon-btn.e-active .e-md-preview::before,
    #mdCustom .e-icon-btn.e-active .e-md-preview.e-icons::before {
		content: '\e350';
	}
	#mdCustom .e-icon-btn .e-md-preview.e-icons::before {
		content: '\e345';
    }

    #rteDialog.e-dialog .e-dlg-content {
        padding: 0px 0px 5px 16px;
    }
    #custom_tbar .e-tbar-btn-text {
        font-size: 16px;
    }
    .e-bigger #custom_tbar .e-tbar-btn-text {
        font-size: 18px;
    }

    @media (min-width: 320px) and (max-width: 480px) {
        .fabric.e-bigger #rteDialog {
            min-width: 281px;
        }

        .fabric #rteDialog {
            min-width: 241px;
        }

        .bootstrap.e-bigger #rteDialog,
        .bootstrap #rteDialog {
            min-width: 223px;
        }

        .highcontrast.e-bigger #rteDialog {
            min-width: 283px;
        }
        .highcontrast #rteDialog {
            min-width: 243px;
        }
        .material #rteDialog {
            min-width: 224px;
        }
        .material.e-bigger #rteDialog {
            min-width: 236px;
        }
    }

</style>
</head>

<body>
    <app-root>
        <div id='loader'>Loading....</div>
    </app-root>
</body>

</html>

Integrate embedly

This can be achieved by binding the actionComplete event to the toolbar items in the toolbarSettings property. In the event handler, create an element and add the appropriate class. The below script is have to add in the sample to embed the content,

Include embedly javascript.

Copied to clipboard
<script src="https://cdn.embedly.com/widgets/platform.js" charset="UTF-8"></script>

The above script is added to the page.

Copied to clipboard
import { Component, ViewChild } from '@angular/core';
import { ToolbarService, LinkService, ImageService, HtmlEditorService, RichTextEditorComponent  } from '@syncfusion/ej2-angular-richtexteditor';
@Component({
    selector: 'app-root',
    template: `<ejs-richtexteditor id='defaultRTE' #sample [toolbarSettings]='toolbarSettings' (actionComplete)='actionComplete($event)'>
    <ng-template #valueTemplate>
      <p>The Rich Text Editor triggers events based on its actions. </p>
      <p> The events can be used as an extension point to perform custom operations.</p>
      <ul>
        <li>created - Triggers when the component is rendered.</li>
        <li>change - Triggers only when RTE is blurred and changes are done to the content.</li>
        <li>focus - Triggers when RTE is focused in.</li>
        <li>blur - Triggers when RTE is focused out.</li>
        <li>actionBegin - Triggers before command execution using toolbar items or executeCommand method.</li>
        <li>actionComplete - Triggers after command execution using toolbar items or executeCommand method.</li>
        <li>destroyed – Triggers when the component is destroyed.</li>
      </ul>
    </ng-template></ejs-richtexteditor>`,
    providers: [ToolbarService, LinkService, ImageService, HtmlEditorService ]
})
export class AppComponent  {
@ViewChild('sample') public rteObj: RichTextEditorComponent;
public toolbarSettings: Object = {
    items: ['createLink']};
public actionComplete(args: any): void {
      if (<String>args.requestType === 'Links') {
        if (args.elements[0].parentNode && (<Element>args.elements[0].parentNode).tagName === 'A'){
          const emberEle: HTMLElement = document.createElement('blockquote');
          emberEle.setAttribute('class', 'embedly-card');
          emberEle.appendChild(args.elements[0].parentElement);
          emberEle.appendChild(document.createElement('p'));
          args.range.insertNode(emberEle);
        }
    }
    }
}
Copied to clipboard
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RichTextEditorAllModule } from '@syncfusion/ej2-angular-richtexteditor';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        RichTextEditorAllModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
Copied to clipboard
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
Copied to clipboard
<!DOCTYPE html>
<html lang="en">

<head>
            <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js"></script>
    <title>Syncfusion Angular Rich Text Editor</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript UI Controls" />
    <meta name="author" content="Syncfusion" />
     <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-richtexteditor/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-dropdowns/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-lists/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-navigations/styles/material.css" rel="stylesheet" />
   <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.9.0/zone.min.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="https://cdn.embedly.com/widgets/platform.js" charset="UTF-8"></script>
    <script src="systemjs.config.js"></script>
    <style>
        #loader {
            color: #008cff;
            font-family: 'Helvetica Neue', 'calibiri';
            font-size: 16px;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
    </style>
</head>

<body>
    <app-root>
        <div id='loader'>Loading....</div>
    </app-root>
</body>

</html>

At.js Integration

RichTextEditor can easily be integrated with At.js library. To display the autocomplete list, type ‘@’.

Include At.JS style.

Copied to clipboard
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/at.js/1.4.0/css/jquery.atwho.min.css">

Include At.JS javascript.

Copied to clipboard
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/at.js/1.4.0/js/jquery.atwho.min.js"></script>

Define the At.js configuration

In below configuration, email id of employees list - email id of employees from the data source.

Copied to clipboard
var config = {
    at: "@",
    data: [email id of employees list],
    displayTpl: '<li>${name} <small>${email}</small></li>',
    limit: 200
  }

Populate the employee’s email id from local or remote data and set the result to the data of At.js configuration.

Copied to clipboard
import { Component, ViewChild } from '@angular/core';
import { ToolbarService, LinkService, ImageService, HtmlEditorService, RichTextEditorComponent  } from '@syncfusion/ej2-angular-richtexteditor';

@Component({
        selector: 'app-root',
        template: `<ejs-richtexteditor id='defaultRTE' #sample [placeholder]='placeholder' (created)='oncreate($event)'></ejs-richtexteditor>`,
        providers: [ToolbarService, LinkService, ImageService, HtmlEditorService ]
})
export class AppComponent  {
  @ViewChild('sample') public rteObj: RichTextEditorComponent;
  // Build data to be used in At.JS config.
  public employeeList: { [key: string]: Object }[] = [
      { id: 'emp01', name: 'Jacob', email: 'jacob@mail.com' },
      { id: 'emp02', name: 'Isabella', email: 'isabella@mail.com' },
      { id: 'emp03', name: 'Ethan', email: 'ethan@mail.com' },
      { id: 'emp04', name: 'Emma', email: 'emma@mail.com' },
      { id: 'emp05', name: 'Michael', email: 'michael@mail.com' },
      { id: 'emp06', name: 'Olivia', email: 'olivia@mail.com' },
      { id: 'emp07', name: 'Jeniffer', email: 'jeniffer@mail.com' }
  ];

  public config: Object = {
      at: "@",
       callbacks: {
        beforeReposition: function (offset) {
            offset.left = this.rect().left - (leftBarWdith + leftPadding);
        }
      },
      data: this.employeeList,
      displayTpl: '<li>${name} <small>${email}</small></li>',
      limit: 200
  };
public placeholder: String = "Type @ to get the e-mail list";
public leftBarWdith : number = window.parent.document.getElementById('doc-left-toc').offsetWidth;
public leftPadding : number = +getComputedStyle(window.parent.document.getElementById('md-cnt')).paddingRight.match(/\d/g).join('');

public oncreate(e: any): void {
    const textArea: HTMLElement = this.rteObj.contentModule.getEditPanel() as HTMLElement;
    $(textArea).atwho(this.config);
    $(textArea).on('keydown', function(e: any) {
    if (e.keyCode === 13 && $(textArea).atwho('isSelecting')) {
        return false;
      }
    });
    }
}
Copied to clipboard
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RichTextEditorAllModule } from '@syncfusion/ej2-angular-richtexteditor';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        RichTextEditorAllModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
Copied to clipboard
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
Copied to clipboard
<!DOCTYPE html>
<html lang="en">

<head>
            <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js"></script>
    <title>Syncfusion Angular Rich Text Editor</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript UI Controls" />
    <meta name="author" content="Syncfusion" />
     <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-richtexteditor/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-dropdowns/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-lists/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/20.4.38/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/at.js/1.4.0/css/jquery.atwho.min.css">

    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.9.0/zone.min.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="//cdn.syncfusion.com/js/assets/external/jquery-1.11.3.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/at.js/1.4.0/js/jquery.atwho.min.js"></script>
    <script src="systemjs.config.js"></script>
    <style>
        #loader {
            color: #008cff;
            font-family: 'Helvetica Neue', 'calibiri';
            font-size: 16px;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
    </style>
</head>

<body>
    <app-root>
        <div id='loader'>Loading....</div>
    </app-root>
</body>

</html>