Markdown in the Angular Rich Text Editor component
16 Jan 202524 minutes to read
When you format the word in Markdown format, you should add Markdown syntax to the word to indicate the words and phrases that look different from each other.
The Rich Text Editor supports Markdown editing when the ‘editorMode’ is set to markdown. You can apply formatting to text using both keyboard interactions and toolbar actions.
To enable the quick Markdown editing feature, inject MarkdownEditorService in the provider section of AppComponent.
Refer to the video below for guidance on building a Markdown editor with the Angular Rich Text Editor.
Markdown Basic Formatting
The Angular Markdown editor supports various commands to format markdown content. Below are the supported commands and their usage:
Commands | Syntax | Description | |
---|---|---|---|
Bold | Sample content for ** bold text** . |
For bold, add ** or __ to front and back of the text. |
For order list, precede each line with a number. |
Italic | Sample content for * Italic text* . |
For Italic, add * or _ to front and back of the text. |
|
Bold and Italics | Sample content for *** bold and Italic text*** . |
For bold and Italics, add *** to the front and back of the text. |
|
Heading 1 |
# Heading 1 content |
For heading 1, add # to start of the line. |
|
Heading 2 |
## Heading 2 content |
For heading 2, add ## to start of the line. |
|
Heading 3 |
### Heading 3 content |
For heading 3, add ### to start of the line. |
|
Heading 4 |
#### Heading 4 content |
For heading 4, add #### to start of the line. |
|
Heading 5 |
##### Heading 5 content |
For heading 5, add ##### to start of the line. |
|
Heading 6 |
###### Heading 6 content |
For heading 6, add ###### to start of the line. |
|
Line Break | First line <br> Second line |
For line break, press enter two times (or) add <br> in between the first and the second line. |
|
Blockquotes |
> Blockquotes text |
For blockquotes, add > to start of the line. |
|
Strike Through | Sample content for ~~ strike through text~~ . |
For strike through, add ~~ to front and back of the text. |
|
Code (Single line) | `Single line code` | For single line code, add ` to front and back of the text. | |
Code block (Multi Line) | ``` Multi line code text Multi line code text ``` |
For multiple line code, add ``` in the new line before and after the content. | |
Subscript |
<sub> Subscript text</sub>
|
For subscript, add <sub> to the front and </sub> to the back of the text. |
|
Superscript |
<sup> Superscript text</sup>
|
For superscript, add <sup> to the front and </sup> to the back of the text. |
|
Ordered List |
1. First1. Second |
For ordered list, preceding one or more lines of text with 1.
|
|
Unordered List |
* First* second |
For unordered list, preceding one or more lines of text with * . |
|
Links |
Link text without title text[ Link text ]( URL) Link text with title text [ Link text ]( URL , “title text”)
|
Create an inline link by wrapping link text in brackets [ ] , and then wrapping the URL as first parameter and title as second parameter in the parentheses () .Note: The title text is optional, if needed it can be given manually. |
|
Table | | Heading 1 | Heading 2 |<br>|---------|---------|<br>| Col A1 | Col A2 |<br>| Col B1 | Col B2 | |
Create a table using the pipes and underscores as given in the syntax to create 2 x 2 table. | |
Horizontal Line |
*** (three asterix in new line)(or) ___ (three underscores in new line) |
For horizontal line, add *** or ___ to the start of the new line. |
|
Image |
![alt text]( URL path)
|
Create an image by wrapping the image source in parentheses () . |
|
Image with alternate text |
![alt text]( URL path)
|
Create an image with alternate text by wrapping an alternative text in brackets [] , and then link of the image source in parentheses () .Note: When inserting the image using toolbar, the alternate text cannot be provided that needs to be given manually. |
|
Escape tick marks supported | Sample text content with ** bold and ** not bold** text can be in the same line.**
|
In the syntax, the whole content is made as bold where the content not bold can be made as normal text by adding the bold syntax to the start and end of the respective text. Likewise you can do the same for various inline commands. |
|
Escape Character |
\( any syntax)
|
Escape any markdown syntax by prefix \ to the syntax.Example: \** Bold text**
|
|
HTML Entities | Copyright: © - © Trade mark: ™ - ™ Registered: ® - ® Ampersand: & - & Less than: < - < Greater than: > - >
|
For HTML entities, add & and ; to the front and back of the respective entities. |
The above listed commands are the only ones supported in the Syncfusion Markdown editor. For other commands, use HTML tags within the Markdown editor. Additionally, footnotes, definitions, math, and checklist Markdown syntax are not supported.
Insert table
Add the CreateTable
item to the toolbar items to use the table tool.
To insert a table in the Markdown editor, click the insert table
icon in the toolbar. By default, the inserted Markdown table includes 2 rows and 2 columns, along with a heading.
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorModule, ToolbarSettingsModel, RichTextEditorComponent, NotifyArgs, ContentRender, ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService } from '@syncfusion/ej2-angular-richtexteditor';
import { createElement, isNullOrUndefined, enableRipple } from '@syncfusion/ej2-base';
import * as Marked from 'marked';
enableRipple(true);
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id='mdPreview' #mdPreview [toolbarSettings]='tools' [editorMode]='mode' (created)='onCreate()'
(actionComplete)="actionComplete($event)" [value]='value'>
</ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService]
})
export class AppComponent {
@ViewChild('mdPreview')
public editorObj?: RichTextEditorComponent;
public textArea?: HTMLTextAreaElement;
public mdsource?: HTMLElement;
public mdSplit?: HTMLElement;
public htmlPreview?: HTMLElement;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'StrikeThrough', '|', 'Formats', 'OrderedList',
'UnorderedList', '|', 'CreateLink', 'Image', 'CreateTable', '|',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, {
tooltipText: 'Split Editor',
template: '<button id="MD_Preview" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-view-side e-icons"></span></button>'
}, 'FullScreen', '|', 'Undo', 'Redo']
};
public mode: string = 'Markdown';
public value: string = "In Rich Text Editor, you click the toolbar buttons to format the words and the changes are visible immediately. Markdown is not like that. When you format the word in Markdown format, you need to add Markdown syntax to the word to indicate which words and phrases should look different from each other. Rich Text Editor supports markdown editing when the editorMode set as **markdown** and using both *keyboard interaction* and *toolbar action*, you can apply the formatting to text. You can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/). The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content.";
public onCreate(): void {
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
document.head.appendChild(script);
this.textArea = (this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', () => {
this.markDownConversion();
});
this.mdsource = document.getElementById('preview-code') as HTMLElement;
this.mdsource!.addEventListener('click', (e: MouseEvent) => {
this.fullPreview({ mode: true, type: 'preview' });
if ((e.target as HTMLElement).parentElement!.classList.contains('e-active')) {
this.editorObj!.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.add('e-overlay');
} else {
this.editorObj!.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.remove('e-overlay');
}
});
this.mdSplit = document.getElementById('MD_Preview') as HTMLElement;
this.mdSplit!.addEventListener('click', (e: MouseEvent) => {
if (this.editorObj!.element.classList.contains('e-rte-full-screen')) {
this.fullPreview({ mode: true, type: '' });
}
this.mdsource!.classList.remove('e-active');
this.editorObj!.showFullScreen();
});
}
public async actionComplete(e: NotifyArgs): Promise<void> {
if (e.target === 'Maximize' && isNullOrUndefined(e.args)) {
this.fullPreview({ mode: true, type: '' });
} else if (!this.mdSplit!.parentElement!.classList.contains('e-overlay')) {
if (e.target === 'Minimize') {
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
if (this.htmlPreview) { this.htmlPreview.style.display = 'none'; }
this.mdSplit!.classList.remove('e-active');
this.mdsource!.classList.remove('e-active');
}
await this.markDownConversion();
}
}
public async markDownConversion(): Promise<void> {
if (this.mdSplit!.classList.contains('e-active')) {
let id: string = this.editorObj!.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
public async fullPreview(e: { [key: string]: string | boolean }): Promise<void> {
let id: string = this.editorObj!.getID() + 'html-preview';
this.htmlPreview = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
if ((this.mdsource!.classList.contains('e-active') || this.mdSplit!.classList.contains('e-active')) && e['mode']) {
this.mdsource!.classList.remove('e-active');
this.mdSplit!.classList.remove('e-active');
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
this.htmlPreview.style.display = 'none';
} else {
this.mdsource!.classList.add('e-active');
this.mdSplit!.classList.add('e-active');
if (!this.htmlPreview) {
this.htmlPreview = createElement('div', { className: 'e-content' });
this.htmlPreview.id = id;
this.textArea!.parentNode!.appendChild(this.htmlPreview);
}
if (e['type'] === 'preview') {
this.textArea!.style.display = 'none';
this.htmlPreview.classList.add('e-pre-source');
} else {
this.htmlPreview.classList.remove('e-pre-source');
this.textArea!.style.width = '50%';
}
this.htmlPreview.style.display = 'block';
this.htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Insert image
Add the Image
item to the toolbar to enable the image tool.
To insert an image in the Markdown editor, follow these steps:
- Click the
Insert Image
icon in the toolbar. - Browse and select an image from your local machine by clicking the browse button, or enter an image link from an online source.
- Click the Insert button in the image dialog.
The selected image will be added to the editor area.
Please refer to the sample and code snippets below to add the image in the Markdown editor.
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorModule, ToolbarSettingsModel, NotifyArgs, RichTextEditorComponent, ContentRender, ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService } from '@syncfusion/ej2-angular-richtexteditor';
import { createElement, isNullOrUndefined, enableRipple } from '@syncfusion/ej2-base';
import * as Marked from 'marked';
enableRipple(true);
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id='mdPreview' #mdPreview [toolbarSettings]='tools' [editorMode]='mode' (created)='onCreate()'
(actionComplete)="actionComplete($event)" [value]='value'>
</ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService]
})
export class AppComponent {
@ViewChild('mdPreview')
public editorObj?: RichTextEditorComponent;
public textArea?: HTMLTextAreaElement;
public mdsource?: HTMLElement;
public mdSplit?: HTMLElement;
public htmlPreview?: HTMLElement;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'StrikeThrough', '|', 'Formats', 'OrderedList',
'UnorderedList', '|', 'Image', '|',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, {
tooltipText: 'Split Editor',
template: '<button id="MD_Preview" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-view-side e-icons"></span></button>'
}, 'FullScreen', '|', 'Undo', 'Redo']
};
public mode: string = 'Markdown';
public value: string = "In Rich Text Editor, you click the toolbar buttons to format the words and the changes are visible immediately. Markdown is not like that. When you format the word in Markdown format, you need to add Markdown syntax to the word to indicate which words and phrases should look different from each other. Rich Text Editor supports markdown editing when the editorMode set as **markdown** and using both *keyboard interaction* and *toolbar action*, you can apply the formatting to text. You can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/). The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content.";
public onCreate(): void {
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
document.head.appendChild(script);
this.textArea = (this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', () => {
this.markDownConversion();
});
this.mdsource = document.getElementById('preview-code') as HTMLElement;
this.mdsource!.addEventListener('click', (e: MouseEvent) => {
this.fullPreview({ mode: true, type: 'preview' });
if ((e.target as HTMLElement).parentElement!.classList.contains('e-active')) {
this.editorObj!.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.add('e-overlay');
} else {
this.editorObj!.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.remove('e-overlay');
}
});
this.mdSplit = document.getElementById('MD_Preview') as HTMLElement;
this.mdSplit!.addEventListener('click', (e: MouseEvent) => {
if (this.editorObj!.element.classList.contains('e-rte-full-screen')) {
this.fullPreview({ mode: true, type: '' });
}
this.mdsource!.classList.remove('e-active');
this.editorObj!.showFullScreen();
});
}
public async actionComplete(e: NotifyArgs): Promise<void> {
if (e.target === 'Maximize' && isNullOrUndefined(e.args)) {
this.fullPreview({ mode: true, type: '' });
} else if (!this.mdSplit!.parentElement!.classList.contains('e-overlay')) {
if (e.target === 'Minimize') {
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
if (this.htmlPreview) { this.htmlPreview.style.display = 'none'; }
this.mdSplit!.classList.remove('e-active');
this.mdsource!.classList.remove('e-active');
}
await this.markDownConversion();
}
}
public async markDownConversion(): Promise<void> {
if (this.mdSplit!.classList.contains('e-active')) {
let id: string = this.editorObj!.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
public async fullPreview(e: { [key: string]: string | boolean }): Promise<void> {
let id: string = this.editorObj!.getID() + 'html-preview';
this.htmlPreview = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
if ((this.mdsource!.classList.contains('e-active') || this.mdSplit!.classList.contains('e-active')) && e['mode']) {
this.mdsource!.classList.remove('e-active');
this.mdSplit!.classList.remove('e-active');
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
this.htmlPreview.style.display = 'none';
} else {
this.mdsource!.classList.add('e-active');
this.mdSplit!.classList.add('e-active');
if (!this.htmlPreview) {
this.htmlPreview = createElement('div', { className: 'e-content' });
this.htmlPreview.id = id;
this.textArea!.parentNode!.appendChild(this.htmlPreview);
}
if (e['type'] === 'preview') {
this.textArea!.style.display = 'none';
this.htmlPreview.classList.add('e-pre-source');
} else {
this.htmlPreview.classList.remove('e-pre-source');
this.textArea!.style.width = '50%';
}
this.htmlPreview.style.display = 'block';
this.htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Insert link
To use a link, add the CreateLink
item to the toolbar items.
To create a link for a text or an image in the Markdown editor, follow these steps:
- Click the
Insert
icon in the link dialog. - Enter the link and other relevant information.
- Click the Insert button to add the link to the editor area.
The link will be added to the editor area.
Please refer to the sample and code snippets below to add the link in the Markdown editor.
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorAllModule, ToolbarSettingsModel, NotifyArgs, RichTextEditorComponent, ContentRender, ToolbarService, LinkService, ImageService, MarkdownEditorService } from '@syncfusion/ej2-angular-richtexteditor';
import { createElement, isNullOrUndefined, enableRipple } from '@syncfusion/ej2-base';
import * as Marked from 'marked';
enableRipple(true);
@Component({
imports: [RichTextEditorAllModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id='mdPreview' #mdPreview [toolbarSettings]='tools' [editorMode]='mode' (created)='onCreate()'
(actionComplete)="actionComplete($event)" [value]='value'>
</ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService]
})
export class AppComponent {
@ViewChild('mdPreview')
public editorObj?: RichTextEditorComponent;
public textArea?: HTMLTextAreaElement;
public mdsource?: HTMLElement;
public mdSplit?: HTMLElement;
public htmlPreview?: HTMLElement;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'StrikeThrough', '|', 'Formats', 'OrderedList',
'UnorderedList', '|', 'CreateLink', '|',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, {
tooltipText: 'Split Editor',
template: '<button id="MD_Preview" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-view-side e-icons"></span></button>'
}, 'FullScreen', '|', 'Undo', 'Redo']
};
public mode: string = 'Markdown';
public value: string = "In Rich Text Editor, you click the toolbar buttons to format the words and the changes are visible immediately. Markdown is not like that. When you format the word in Markdown format, you need to add Markdown syntax to the word to indicate which words and phrases should look different from each other. Rich Text Editor supports markdown editing when the editorMode set as **markdown** and using both *keyboard interaction* and *toolbar action*, you can apply the formatting to text. You can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/). The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content.";
public onCreate(): void {
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
document.head.appendChild(script);
this.textArea = (this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', () => {
this.markDownConversion();
});
this.mdsource = document.getElementById('preview-code') as HTMLElement;
this.mdsource!.addEventListener('click', (e: MouseEvent) => {
this.fullPreview({ mode: true, type: 'preview' });
if ((e.target as HTMLElement).parentElement!.classList.contains('e-active')) {
this.editorObj!.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.add('e-overlay');
} else {
this.editorObj!.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image', 'CreateTable']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.remove('e-overlay');
}
});
this.mdSplit = document.getElementById('MD_Preview') as HTMLElement;
this.mdSplit!.addEventListener('click', (e: MouseEvent) => {
if (this.editorObj!.element.classList.contains('e-rte-full-screen')) {
this.fullPreview({ mode: true, type: '' });
}
this.mdsource!.classList.remove('e-active');
this.editorObj!.showFullScreen();
});
}
public async actionComplete(e: NotifyArgs): Promise<void> {
if (e.target === 'Maximize' && isNullOrUndefined(e.args)) {
this.fullPreview({ mode: true, type: '' });
} else if (!this.mdSplit!.parentElement!.classList.contains('e-overlay')) {
if (e.target === 'Minimize') {
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
if (this.htmlPreview) { this.htmlPreview.style.display = 'none'; }
this.mdSplit!.classList.remove('e-active');
this.mdsource!.classList.remove('e-active');
}
await this.markDownConversion();
}
}
public async markDownConversion(): Promise<void> {
if (this.mdSplit!.classList.contains('e-active')) {
let id: string = this.editorObj!.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
public async fullPreview(e: { [key: string]: string | boolean }): Promise<void> {
let id: string = this.editorObj!.getID() + 'html-preview';
this.htmlPreview = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
if ((this.mdsource!.classList.contains('e-active') || this.mdSplit!.classList.contains('e-active')) && e['mode']) {
this.mdsource!.classList.remove('e-active');
this.mdSplit!.classList.remove('e-active');
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
this.htmlPreview.style.display = 'none';
} else {
this.mdsource!.classList.add('e-active');
this.mdSplit!.classList.add('e-active');
if (!this.htmlPreview) {
this.htmlPreview = createElement('div', { className: 'e-content' });
this.htmlPreview.id = id;
this.textArea!.parentNode!.appendChild(this.htmlPreview);
}
if (e['type'] === 'preview') {
this.textArea!.style.display = 'none';
this.htmlPreview.classList.add('e-pre-source');
} else {
this.htmlPreview.classList.remove('e-pre-source');
this.textArea!.style.width = '50%';
}
this.htmlPreview.style.display = 'block';
this.htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Custom format
The Rich Text Editor allows you to customize the markdown syntax by overriding its default syntax. Configure the customized markdown syntax using the formatter
property.
This example demonstrates how to customize tags in Markdown formatting.
For example, use ‘+’ for unordered lists, ‘1., 2., 3.’ for ordered lists, ‘__ ‘for bold text, and ‘_’ for italic text.
import { enableRipple, createElement } from '@syncfusion/ej2-base';
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorModule, ToolbarSettingsModel, ContentRender, RichTextEditorComponent, MarkdownFormatter, ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService } from '@syncfusion/ej2-angular-richtexteditor';
import * as Marked from 'marked';
enableRipple(true);
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id='mdCustom' #mdCustom [toolbarSettings]='tools' [editorMode]='mode' [formatter]='formatter' (created)='onCreate()' [value]='value'>
</ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService]
})
export class AppComponent {
@ViewChild('mdCustom')
public editorObj?: RichTextEditorComponent;
public textArea?: HTMLTextAreaElement;
public mdsource?: HTMLElement;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', '|',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-icons e-md-preview"></span></button>'
}, 'Undo', 'Redo']
};
public mode: string = 'Markdown';
public formatter: MarkdownFormatter = new MarkdownFormatter({
listTags: { 'OL': '1., 2., 3.', 'UL': '+ ' },
formatTags: {
'Blockquote': '> '
},
selectionTags: { 'Bold': '__', 'Italic': '_' }
});
public value: string = "The sample is configured with customized markdown syntax using the __formatter__ property. Type the content and click the toolbar item to view customized markdown syntax. For unordered list, you need to add a plus sign before the word (e.g., + list1). Or to make a phrase bold, you need to add two underscores before and after the phrase (e.g., __this text is bold__).";
public onCreate(): void {
this.textArea = (this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', () => {
this.markDownConversion();
});
this.mdsource = document.getElementById('preview-code') as HTMLElement;
this.mdsource ?.addEventListener('click', (e: MouseEvent) => {
this.fullPreview();
});
}
public async markDownConversion(): Promise<void> {
if (this.mdsource ?.classList.contains('e-active')) {
let id: string = this.editorObj ?.getID() + 'html-view';
let htmlPreview: Element = this.editorObj!.element.querySelector('#' + id) as Element;
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
public async fullPreview(): Promise<void> {
let id: string = this.editorObj!.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
if (this.mdsource!.classList.contains('e-active')) {
this.mdsource!.classList.remove('e-active');
this.textArea!.style.display = 'block';
htmlPreview.style.display = 'none';
} else {
this.mdsource!.classList.add('e-active');
if (!htmlPreview) {
htmlPreview = createElement('div', { className: 'e-content e-pre-source' });
htmlPreview.id = id;
this.textArea!.parentNode!.appendChild(htmlPreview);
}
this.textArea!.style.display = 'none';
htmlPreview.style.display = 'block';
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
this.mdsource!.parentElement!.title = 'Code View';
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Markdown to HTML
The Rich Text Editor provides an instant preview of Markdown changes. Type or edit the text and apply formatting to view the Markdown preview.
This example demonstrates how to preview Markdown changes in the Rich Text Editor. The third-party library Marked
is used to convert Markdown into HTML content.
import { enableRipple } from '@syncfusion/ej2-base';
import { Component, ViewChild } from '@angular/core';
import { RichTextEditorModule, ToolbarSettingsModel, NotifyArgs, RichTextEditorComponent, ContentRender, ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService } from '@syncfusion/ej2-angular-richtexteditor';
import { createElement, isNullOrUndefined } from '@syncfusion/ej2-base';
import * as Marked from 'marked';
enableRipple(true);
@Component({
imports: [RichTextEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-richtexteditor id='mdPreview' #mdPreview [toolbarSettings]='tools' [editorMode]='mode' (created)='onCreate()' (actionComplete)="actionComplete($event)" [value]='value'>
</ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, MarkdownEditorService, TableService]
})
export class AppComponent {
@ViewChild('mdPreview')
public editorObj?: RichTextEditorComponent;
public textArea?: HTMLTextAreaElement;
public mdsource?: HTMLElement;
public mdSplit?: HTMLElement;
public htmlPreview?: HTMLElement;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'StrikeThrough', '|', 'Formats', 'OrderedList', 'UnorderedList', '|', 'CreateLink', 'Image', '|',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, {
tooltipText: 'Split Editor',
template: '<button id="MD_Preview" class="e-tbar-btn e-control e-btn e-icon-btn">' +
'<span class="e-btn-icon e-view-side e-icons"></span></button>'
}, 'FullScreen', '|', 'Undo', 'Redo']
};
public mode: string = 'Markdown';
public value: string = "In Rich Text Editor, you click the toolbar buttons to format the words and the changes are visible immediately. Markdown is not like that. When you format the word in Markdown format, you need to add Markdown syntax to the word to indicate which words and phrases should look different from each other. Rich Text Editor supports markdown editing when the editorMode set as **markdown** and using both *keyboard interaction* and *toolbar action*, you can apply the formatting to text. You can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/). The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content.";
public onCreate(): void {
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
document.head.appendChild(script);
this.textArea = (this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', () => {
this.markDownConversion();
});
this.mdsource = document.getElementById('preview-code') as HTMLElement;
this.mdsource!.addEventListener('click', (e: MouseEvent) => {
this.fullPreview({ mode: true, type: 'preview' });
if ((e.target as HTMLElement).parentElement!.classList.contains('e-active')) {
this.editorObj!.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image']);
(e.target as HTMLElement)!.parentElement!.parentElement!.nextElementSibling!.classList.add('e-overlay');
} else {
this.editorObj!.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'CreateLink', 'Image']);
(e.target as HTMLElement).parentElement!.parentElement!.nextElementSibling!.classList.remove('e-overlay');
}
});
this.mdSplit = document.getElementById('MD_Preview') as HTMLElement;
this.mdSplit!.addEventListener('click', (e: MouseEvent) => {
if (this.editorObj!.element.classList.contains('e-rte-full-screen')) {
this.fullPreview({ mode: true, type: '' });
}
this.mdsource!.classList.remove('e-active');
this.editorObj!.showFullScreen();
});
}
public async actionComplete(e: NotifyArgs): Promise<void> {
if (e.target === 'Maximize' && isNullOrUndefined(e.args)) {
this.fullPreview({ mode: true, type: '' });
} else if (!this.mdSplit!.parentElement!.classList.contains('e-overlay')) {
if (e.target === 'Minimize') {
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
if (this.htmlPreview) { this.htmlPreview.style.display = 'none'; }
this.mdSplit!.classList.remove('e-active');
this.mdsource!.classList.remove('e-active');
}
await this.markDownConversion();
}
}
public async markDownConversion(): Promise<void> {
if (this.mdSplit!.classList.contains('e-active')) {
let id: string = this.editorObj!.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
public async fullPreview(e: { [key: string]: string | boolean }): Promise<void> {
let id: string = this.editorObj!.getID() + 'html-preview';
this.htmlPreview = this.editorObj!.element.querySelector('#' + id) as HTMLElement;
if ((this.mdsource!.classList.contains('e-active') || this.mdSplit!.classList.contains('e-active')) && e['mode']) {
this.mdsource!.classList.remove('e-active');
this.mdSplit!.classList.remove('e-active');
this.textArea!.style.display = 'block';
this.textArea!.style.width = '100%';
this.htmlPreview.style.display = 'none';
} else {
this.mdsource!.classList.add('e-active');
this.mdSplit!.classList.add('e-active');
if (!this.htmlPreview) {
this.htmlPreview = createElement('div', { className: 'e-content' });
this.htmlPreview.id = id;
this.textArea!.parentNode!.appendChild(this.htmlPreview);
}
if (e['type'] === 'preview') {
this.textArea!.style.display = 'none';
this.htmlPreview.classList.add('e-pre-source');
} else {
this.htmlPreview.classList.remove('e-pre-source');
this.textArea!.style.width = '50%';
}
this.htmlPreview.style.display = 'block';
this.htmlPreview.innerHTML = await Marked.parse(((this.editorObj!.contentModule as ContentRender).getEditPanel() as HTMLTextAreaElement).value);
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));