
Syncfusion Angular Blockeditor
- 211 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-blockeditor for development tasks
About
syncfusion-angular-blockeditor: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-blockeditor
Syncfusion Angular Blockeditor by the numbers
- 211 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,920 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-blockeditorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 211 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-blockeditor for development tasks
Files
Syncfusion Angular Block Editor Component
Component Overview
The Syncfusion Angular Block Editor is a powerful block-based editor component for Angular applications. It provides a document-centric editing experience similar to modern content management systems, with extensive support for formatting, customization, and advanced features.
Key Capabilities:
- Block Management - Compose documents with various block types (paragraphs, headings, lists, tables, code, images)
- Text Formatting - Bold, italic, underline, strikethrough, colors, highlights, and inline styles
- Advanced Menus - Slash commands, context menus, inline toolbars, and block action menus
- Events & Interactions - Comprehensive event handling for content changes, selection, drag-drop, paste operations
- Content Management - Import/export as JSON, HTML, or Markdown with full serialization control
- Methods - Programmatically add, remove, move, and update blocks with full API access
- Security Features - Built-in HTML sanitization and XSS prevention for safe content
- Toolbar Configuration - Customizable slash commands and block action menus with grouping
- Templates - Custom templates for blocks, placeholders, and empty states
- Globalization - Multi-language support, RTL languages, and locale-based keyboard shortcuts
- Customizable UI - Themes, CSS variables, responsive sizing, read-only mode, and placeholder text
Quick Navigation Guide
Getting Started
Start here if you're new to Block Editor or need setup instructions.
Read When:
- Setting up a new project with Block Editor
- Installing and importing dependencies
- Configuring CSS and basic properties
- Creating your first editor instance
👉 [Getting Started](./references/getting-started.md)
---
Core Concepts
Learn the fundamental architecture and data structures.
Read When:
- Understanding block types and their purposes
- Learning about BlockModel and ContentModel
- Grasping how blocks are organized and related
- Understanding different content types within blocks
👉 [Core Concepts](./references/core-concepts.md)
---
Block Management Methods
Programmatically manipulate editor content and structure.
Read When:
- Adding, removing, or moving blocks
- Updating block properties
- Querying block count and data
- Building dynamic document structures
👉 [Block Management Methods](./references/block-management-methods.md)
---
Selection and Cursor Methods
Control user selection and cursor positioning.
Read When:
- Setting cursor position programmatically
- Selecting text or blocks
- Implementing text selection features
- Managing ranges and selections
- Execute toolbar actions programmatically (executeToolbarAction)
👉 [Selection and Cursor Methods](./references/selection-and-cursor-methods.md)
---
Utility Methods
Control editor focus, toolbar state, and printing functionality.
Read When:
- Focusing or blurring the editor programmatically
- Enabling or disabling toolbar items dynamically
- Implementing conditional toolbar access
- Printing editor content
- Managing editor state based on user actions
👉 [Utility Methods](./references/utility-methods.md)
---
Configuration Properties
Configure editor styling, security settings, users and labels.
Read When:
- Applying custom CSS classes for theming
- Implementing dark mode or custom themes
- Configuring HTML encoding for security
- Setting up XSS prevention
- Integrating with CSS frameworks (Bootstrap, Tailwind, Material)
- Configuring users for multi-user editing
- Adding user avatars and identification
- Implementing @mentions functionality
- Creating label/tag systems (#tags)
- Setting up project management labels
- Tracking content by user or category
👉 [Configuration Properties](./references/configuration-properties.md)
---
Editor Menus
Configure slash commands, context menus, and toolbars.
Read When:
- Customizing the slash command menu (/)
- Adding context menu items
- Configuring block action menus
- Creating inline toolbars
- Adding custom menu items
👉 [Editor Menus](./references/editor-menus.md)
---
Events and Callbacks
Handle user interactions and content changes.
Read When:
- Implementing auto-save functionality
- Validating content changes
- Tracking user interactions
- Responding to selection changes
- Handling paste operations
👉 [Events and Callbacks](./references/events-and-callbacks.md)
---
Formatting and Styles
Apply text formatting and styling.
Read When:
- Making text bold, italic, underlined
- Applying colors and backgrounds
- Using advanced text styles (superscript, code, etc.)
- Formatting block types (headings, quotes, etc.)
- Creating styled content
👉 [Formatting and Styles](./references/formatting-and-styles.md)
---
Data Structures and Lists
Work with list blocks and table structures.
Read When:
- Creating bullet or numbered lists
- Adding checklists with checkboxes
- Building tables with rows and columns
- Creating nested indented content
- Working with toggle/collapsible blocks
👉 [Data Structures and Lists](./references/data-structures-and-lists.md)
---
Advanced Features
Explore undo/redo, drag-drop, code blocks, and templates.
Read When:
- Configuring undo/redo stack size
- Customizing drag and drop behavior
- Adding code blocks with syntax highlighting
- Creating custom template blocks
- Building complex document layouts
👉 [Advanced Features](./references/advanced-features.md)
---
Data Export and Import
Convert between formats and persist content.
Read When:
- Exporting editor content as JSON
- Converting to HTML or Markdown
- Importing from JSON or HTML files
- Parsing external HTML content
- Printing documents
- Synchronizing with servers
👉 [Data Export and Import](./references/data-export-and-import.md)
---
Keyboard Shortcuts and Globalization
Configure shortcuts and multi-language support.
Read When:
- Customizing keyboard shortcuts
- Setting up platform-specific shortcuts
- Implementing internationalization
- Supporting RTL languages
- Configuring language-specific settings
👉 [Keyboard Shortcuts and Globalization](./references/keyboard-shortcuts-and-globalization.md)
---
Security and Paste Handling
Secure the editor and manage content pasting.
Read When:
- Enabling HTML sanitization
- Preventing XSS attacks
- Configuring paste cleanup rules
- Managing allowed styles and tags
- Implementing read-only mode
- Validating user input
👉 [Security and Paste Handling](./references/security-and-paste-handling.md)
---
Appearance and Customization
Style and customize the editor appearance.
Read When:
- Setting custom width/height
- Applying themes (light, dark, high-contrast)
- Using CSS variables for styling
- Customizing fonts and typography
- Creating responsive editors
- Styling with custom CSS classes
👉 [Appearance and Customization](./references/appearance-and-customization.md)
---
Quick Start Example
1. Install Dependencies
npm install @syncfusion/ej2-angular-blockeditor
npm install @syncfusion/ej2-angular-buttons
npm install @syncfusion/ej2-base2. Create Component
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-editor',
template: `<ejs-blockeditor [height]="'500px'" />`,
standalone: true,
imports: [BlockEditorModule]
})
export class EditorComponent {}3. Import CSS
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-angular-blockeditor/styles/material3.css';
4. Add to Module/Bootstrap
// app.config.ts
import { importProvidersFrom } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
export const appConfig = {
providers: [
importProvidersFrom(BrowserModule)
]
};
// OR in app.module.ts
@NgModule({
imports: [BlockEditorModule]
})
export class AppModule {}---
Common Patterns
Pattern 1: Document Auto-Save
@Component({
selector: 'app-autosave-editor',
template: `
<div>
<span>Status: {{ saveStatus }}</span>
<ejs-blockeditor #editor (blockChanged)="onContentChange()"></ejs-blockeditor>
</div>
`,
standalone: true,
imports: [BlockEditorModule]
})
export class AutosaveEditorComponent {
@ViewChild('editor') editor!: BlockEditorComponent;
public saveStatus = 'Saved';
private saveTimeout: any;
public onContentChange(): void {
this.saveStatus = 'Unsaved';
clearTimeout(this.saveTimeout);
this.saveTimeout = setTimeout(() => {
const content = this.editor.getDataAsJson();
this.saveToServer(content);
}, 1000);
}
private saveToServer(content: any): void {
// Save to backend
this.saveStatus = 'Saved';
}
}Pattern 2: Formatted Document Export
@Component({
selector: 'app-document-exporter',
template: `
<div>
<button (click)="exportJson()">Export JSON</button>
<button (click)="exportHtml()">Export HTML</button>
<button (click)="exportMarkdown()">Export Markdown</button>
<ejs-blockeditor #editor></ejs-blockeditor>
</div>
`,
standalone: true,
imports: [BlockEditorModule]
})
export class DocumentExporterComponent {
@ViewChild('editor') editor!: BlockEditorComponent;
public exportJson(): void {
const data = this.editor.getDataAsJson();
this.downloadFile(JSON.stringify(data, null, 2), 'document.json', 'application/json');
}
public exportHtml(): void {
const html = this.editor.getDataAsHtml();
this.downloadFile(html, 'document.html', 'text/html');
}
public exportMarkdown(): void {
// Convert to markdown format
const data = this.editor.getDataAsJson();
const markdown = this.convertToMarkdown(data);
this.downloadFile(markdown, 'document.md', 'text/markdown');
}
private downloadFile(content: string, filename: string, type: string): void {
const blob = new Blob([content], { type });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}
private convertToMarkdown(blocks: any[]): string {
// Implementation details in Data Export and Import reference
return '';
}
}---
Troubleshooting Guide
Issue: Editor not displaying
Solution: Ensure CSS is imported in main.ts or global styles
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-angular-blockeditor/styles/material3.css';
Issue: Content not saving
Solution: Use getDataAsJson() method to retrieve content
const content = this.blockEditor.getDataAsJson();Issue: Events not firing
Solution: Ensure component is standalone and imports are correct
@Component({
standalone: true,
imports: [BlockEditorModule]
})---
Advanced Features
Table of Contents
- Undo/Redo Configuration
- Drag and Drop Customization
- Image Blocks
- Code Blocks with Syntax Highlighting
- Template Blocks
- Advanced Examples
Undo/Redo Configuration
The Block Editor maintains a history of actions, allowing users to undo and redo changes.
Default Behavior
By default, the editor stores up to 30 actions in the undo/redo stack.
Customizing Stack Size
Use the undoRedoStack property to control the history limit:
<ejs-blockeditor [undoRedoStack]="20" />In component:
public undoRedoStack = 20; // Limit to 20 actionsKeyboard Shortcuts
| Action | Windows | Mac |
|---|---|---|
| Undo | Ctrl + Z | ⌘ + Z |
| Redo | Ctrl + Y | ⌘ + Y |
Example Configuration
@Component({
selector: 'app-root',
template: `<ejs-blockeditor [undoRedoStack]="30" />`
})
export class AppComponent {
public undoRedoStack = 30;
}Undo/Redo Events
Track undo/redo operations with events:
public onBlockChanged(args: BlockChangedEventArgs): void {
if (args.action === 'undo') {
console.log('User pressed undo');
} else if (args.action === 'redo') {
console.log('User pressed redo');
}
}Drag and Drop Customization
Block dragging is enabled by default, allowing users to reorder content.
Enable/Disable
<ejs-blockeditor [enableDragAndDrop]="true" />Or:
public enableDragAndDrop = true;Single Block Dragging
Drag a single block by its handle:
// Hover over block → Grab drag handle → Drag to new positionMultiple Block Dragging
Select multiple blocks and drag together:
1. Select desired blocks (Ctrl+Click or Shift+Click) 2. Drag the selection to a new position 3. Release to place
Drag Events
Handle drag operations with events:
public onBlockDragStart(args: BlockDragStartEventArgs): void {
// Validate if blocks can be dragged
console.log('Dragging blocks:', args.blocks.length);
}
public onBlockDragging(args: BlockDraggingEventArgs): void {
// Update visual feedback
console.log('Target position:', args.targetPosition);
}
public onBlockDropped(args: BlockDroppedEventArgs): void {
// Confirm drop and perform actions
if (args.success) {
console.log('Dropped successfully');
this.updateReferences();
}
}Example: Prevent Dragging Certain Blocks
public onBlockDragStart(args: BlockDragStartEventArgs): void {
// Don't allow dragging headings
if (args.blocks.some(b => b.blockType === 'Heading')) {
args.canDrop = false;
}
}Image Blocks
Insert and manage images with upload, resize, and display options.
Global Image Settings
Configure global image behavior:
public imageBlockSettings = {
saveUrl: '/api/upload/image', // Server endpoint
path: '/uploads/', // Server path
maxFileSize: 10000000, // 10MB max
allowedTypes: ['.jpg', '.jpeg', '.png'], // Allowed formats
width: '100%', // Default width
height: 'auto', // Default height
enableResize: true, // Allow resizing
minWidth: '200px', // Minimum width
maxWidth: '800px' // Maximum width
};Image Block Configuration
{
blockType: 'Image',
properties: {
src: 'https://example.com/image.jpg',
width: '400px',
height: '300px',
altText: 'Image description'
}
}Image Upload Handler
@Component({
imports: [BlockEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-blockeditor [imageBlockSettings]="imageBlockSettings" (fileUploading)="onFileUploading($event)" />`
})
export class AppComponent {
public imageBlockSettings = {
saveUrl: 'https://api.example.com/upload',
path: 'https://cdn.example.com/images/'
};
public onFileUploading(args: UploadingEventArgs): void {
// Add authentication
args.currentRequest.setRequestHeader('Authorization', 'Bearer token');
}
}Image Resizing
Users can resize images by dragging corners:
- Drag corners to resize with aspect ratio maintained
- Double-click to fit to content
- Use auto-fit for responsive sizing
Code Blocks with Syntax Highlighting
Display code with language-specific syntax highlighting.
Global Code Settings
Configure available languages and default:
public codeBlockSettings: CodeBlockSettingsModel = {
defaultLanguage: 'javascript',
languages: [
{ language: 'javascript', label: 'JavaScript' },
{ language: 'typescript', label: 'TypeScript' },
{ language: 'html', label: 'HTML' },
{ language: 'css', label: 'CSS' },
{ language: 'python', label: 'Python' },
{ language: 'java', label: 'Java' },
{ language: 'csharp', label: 'C#' },
{ language: 'php', label: 'PHP' }
]
};Code Block Configuration
{
blockType: 'Code',
properties: {
language: 'typescript'
},
content: [
{
contentType: ContentType.Text,
content: 'interface User {\n name: string;\n age: number;\n}'
}
]
}Example: Multiple Code Blocks
[
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Code Examples' }
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'JavaScript:' }
]
},
{
blockType: 'Code',
properties: { language: 'javascript' },
content: [
{
contentType: ContentType.Text,
content: 'const x = 10;\nconsole.log(x);'
}
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'TypeScript:' }
]
},
{
blockType: 'Code',
properties: { language: 'typescript' },
content: [
{
contentType: ContentType.Text,
content: 'const x: number = 10;\nconsole.log(x);'
}
]
}
]Copy Code Feature
Users can copy code blocks using the built-in copy button.
Template Blocks
Create custom block types using templates for specialized content.
Basic Template
{
blockType: 'Template',
template: '<div class="custom-block">Custom Content</div>'
}Template with Dynamic Content
{
blockType: 'Template',
template: `<div class="notification-card">
<span class="icon">📢</span>
<h3>Announcement</h3>
<p>Important message</p>
</div>`
}Template as Function
{
blockType: 'Template',
template: (data: any) => {
return `<div class="dynamic-block">
<h4>${data.title}</h4>
<p>${data.content}</p>
</div>`;
},
properties: {
title: 'Dynamic Block',
content: 'This content is dynamic'
}
}Template with Styling
{
blockType: 'Template',
template: `<div class="alert alert-info">
<strong>Note:</strong> This is a template block with custom styling.
</div>`
}CSS:
.alert {
padding: 12px 16px;
border-radius: 4px;
border-left: 4px solid;
}
.alert-info {
border-left-color: #2196F3;
background-color: #E3F2FD;
color: #1976D2;
}Advanced Examples
Example 1: Complete Documentation Setup
public blocksData: BlockModel[] = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [
{ contentType: ContentType.Text, content: 'API Documentation' }
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Learn how to use our API' }
]
},
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Authentication' }
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Use Bearer token:' }
]
},
{
blockType: 'Code',
properties: { language: 'bash' },
content: [
{
contentType: ContentType.Text,
content: 'Authorization: Bearer YOUR_TOKEN'
}
]
}
];
public codeBlockSettings = {
defaultLanguage: 'javascript',
languages: [
{ language: 'bash', label: 'Bash' },
{ language: 'javascript', label: 'JavaScript' },
{ language: 'typescript', label: 'TypeScript' }
]
};
public imageBlockSettings = {
maxFileSize: 5000000,
allowedTypes: ['.jpg', '.jpeg', '.png', '.gif']
};
public undoRedoStack = 30;Example 2: Tutorial with Code and Images
public blocksData: BlockModel[] = [
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Getting Started Tutorial' }
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Follow these steps to begin' }
]
},
{
blockType: 'Image',
properties: {
src: 'https://example.com/step1.jpg',
altText: 'Step 1 illustration',
width: '100%'
}
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'View the diagram above' }
]
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'Run this code:' }
]
},
{
blockType: 'Code',
properties: { language: 'bash' },
content: [
{ contentType: ContentType.Text, content: 'npm install' }
]
}
];Example 3: Blog Post with Collapsible Sections
public blocksData: BlockModel[] = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [
{ contentType: ContentType.Text, content: 'Blog Post' }
]
},
{
id: 'intro',
blockType: 'CollapsibleHeading',
properties: {
level: 2,
isExpanded: true,
children: [
{
parentId: 'intro',
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Introduction text' }
]
}
]
}
},
{
id: 'details',
blockType: 'CollapsibleHeading',
properties: {
level: 2,
isExpanded: false,
children: [
{
parentId: 'details',
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Detailed content' }
]
}
]
}
}
];Appearance and Customization
Table of Contents
- Editor Dimensions
- CSS Classes and Styling
- Theme Customization
- Custom CSS Variables
- Font and Typography Customization
- Color Schemes
- Advanced Customization Examples
Editor Dimensions
Width and Height Configuration
Set custom dimensions for the editor:
@Component({
selector: 'app-root',
template: `
<ejs-blockeditor
[width]="editorWidth"
[height]="editorHeight"
/>
`,
standalone: true,
imports: [BlockEditorModule]
})
export class AppComponent {
public editorWidth = '100%'; // Width (string or number)
public editorHeight = '500px'; // Height (string or number)
}Responsive Sizing
@Component({
selector: 'app-responsive-editor',
template: `
<ejs-blockeditor
[width]="getWidth()"
[height]="getHeight()"
/>
`,
standalone: true,
imports: [BlockEditorModule]
})
export class ResponsiveEditorComponent {
public getWidth(): string {
const width = window.innerWidth;
if (width < 768) return '100%';
if (width < 1024) return '90%';
return '80%';
}
public getHeight(): string {
const height = window.innerHeight;
return Math.max(400, height - 150) + 'px';
}
constructor() {
window.addEventListener('resize', () => {
// Trigger resize detection
});
}
}Min/Max Constraints
public editorConfig = {
width: '100%',
height: '600px',
minHeight: '300px',
maxHeight: '1000px',
minWidth: '200px',
maxWidth: 'none'
};CSS Classes and Styling
Default Editor Classes
/* Main editor container */
.ej-blockeditor {
border: 1px solid #e0e0e0;
border-radius: 4px;
background-color: #fff;
}
/* Content area */
.ej-blockeditor .content {
padding: 16px;
min-height: 300px;
}
/* Individual block */
.ej-blockeditor .block {
margin: 8px 0;
padding: 4px 0;
position: relative;
}
/* Block placeholder */
.ej-blockeditor .block-placeholder {
color: #999;
font-style: italic;
}
/* Toolbar */
.ej-blockeditor .toolbar {
display: flex;
gap: 4px;
padding: 8px;
border-bottom: 1px solid #e0e0e0;
}
/* Toolbar buttons */
.ej-blockeditor .toolbar-button {
padding: 6px 12px;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
background-color: #fff;
}
.ej-blockeditor .toolbar-button:hover {
background-color: #f5f5f5;
}
.ej-blockeditor .toolbar-button.active {
background-color: #e3f2fd;
border-color: #2196f3;
color: #2196f3;
}Custom Styling Example
.custom-editor {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
background-color: #fafafa;
}
.custom-editor .content {
padding: 24px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.custom-editor .block {
transition: background-color 0.2s ease;
}
.custom-editor .block:hover {
background-color: #f5f5f5;
}
.custom-editor .toolbar {
background-color: #f5f5f5;
border-radius: 4px 4px 0 0;
}Theme Customization
Theme Configuration
Define a custom theme:
public themeConfig = {
primary: '#2196F3',
secondary: '#FF9800',
background: '#fff',
surface: '#f5f5f5',
text: '#333',
textSecondary: '#666',
border: '#e0e0e0',
error: '#F44336',
warning: '#FFC107',
success: '#4CAF50',
info: '#2196F3'
};
@Component({
selector: 'app-themed-editor',
template: `<ejs-blockeditor [ngClass]="'theme-' + currentTheme" />`,
styles: [`
:host ::ng-deep .theme-dark {
--primary: #90CAF9;
--secondary: #FFCC80;
--background: #121212;
--surface: #1e1e1e;
--text: #fff;
--border: #333;
}
`],
standalone: true,
imports: [BlockEditorModule]
})
export class ThemedEditorComponent {
public currentTheme = 'light';
public setTheme(theme: 'light' | 'dark' | 'high-contrast'): void {
this.currentTheme = theme;
this.applyThemeStyles(theme);
}
private applyThemeStyles(theme: string): void {
document.documentElement.setAttribute('data-theme', theme);
}
}Custom CSS Variables
Define CSS Variables
:root {
/* Color variables */
--color-primary: #2196F3;
--color-primary-light: #E3F2FD;
--color-primary-dark: #1976D2;
--color-secondary: #FF9800;
--color-secondary-light: #FFE0B2;
--color-secondary-dark: #F57C00;
--color-background: #FFFFFF;
--color-surface: #F5F5F5;
--color-surface-variant: #EEEEEE;
--color-text: #212121;
--color-text-secondary: #666666;
--color-text-disabled: #BDBDBD;
--color-border: #E0E0E0;
--color-divider: #BDBDBD;
/* Spacing variables */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
/* Typography variables */
--font-family-base: 'Segoe UI', Tahoma, Geneva, sans-serif;
--font-family-mono: 'Monaco', 'Courier New', monospace;
--font-size-sm: 12px;
--font-size-base: 14px;
--font-size-lg: 16px;
--font-size-xl: 20px;
--font-size-2xl: 24px;
--line-height-tight: 1.2;
--line-height-normal: 1.5;
--line-height-loose: 1.8;
/* Border variables */
--border-radius-sm: 3px;
--border-radius-md: 4px;
--border-radius-lg: 8px;
--border-width: 1px;
--border-width-thick: 2px;
/* Shadow variables */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12);
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.15);
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.2);
}Use CSS Variables in Editor
.ej-blockeditor {
font-family: var(--font-family-base);
background-color: var(--color-background);
color: var(--color-text);
border: var(--border-width) solid var(--color-border);
}
.ej-blockeditor .content {
padding: var(--spacing-md);
font-size: var(--font-size-base);
line-height: var(--line-height-normal);
}
.ej-blockeditor .block {
margin: var(--spacing-sm) 0;
border-radius: var(--border-radius-sm);
}
.ej-blockeditor .toolbar {
background-color: var(--color-surface);
border-bottom: var(--border-width) solid var(--color-border);
gap: var(--spacing-sm);
padding: var(--spacing-sm);
}
.ej-blockeditor .toolbar-button {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius-md);
color: var(--color-text);
background-color: var(--color-background);
border: var(--border-width) solid var(--color-border);
}
.ej-blockeditor .toolbar-button:hover {
background-color: var(--color-surface-variant);
}
.ej-blockeditor .toolbar-button.active {
color: var(--color-primary);
background-color: var(--color-primary-light);
border-color: var(--color-primary);
}Font and Typography Customization
Font Configuration
public fontSettings = {
fontFamily: 'Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 400,
letterSpacing: '0px'
};
@Component({
selector: 'app-typography-editor',
template: `<ejs-blockeditor [ngClass]="fontClass" />`,
styles: [`
.serif-font {
font-family: Georgia, serif;
}
.sans-serif-font {
font-family: 'Trebuchet MS', sans-serif;
}
.monospace-font {
font-family: 'Courier New', monospace;
}
`],
standalone: true,
imports: [BlockEditorModule]
})
export class TypographyEditorComponent {
public fontClass = 'sans-serif-font';
public setFont(font: 'serif' | 'sans-serif' | 'monospace'): void {
this.fontClass = font + '-font';
}
}Heading Styles
.ej-blockeditor h1 {
font-size: 32px;
font-weight: 700;
line-height: 1.2;
margin-top: 24px;
margin-bottom: 16px;
color: var(--color-text);
}
.ej-blockeditor h2 {
font-size: 28px;
font-weight: 700;
line-height: 1.3;
margin-top: 20px;
margin-bottom: 12px;
}
.ej-blockeditor h3 {
font-size: 24px;
font-weight: 600;
line-height: 1.4;
margin-top: 16px;
margin-bottom: 10px;
}
.ej-blockeditor h4 {
font-size: 20px;
font-weight: 600;
line-height: 1.5;
margin-top: 12px;
margin-bottom: 8px;
}
.ej-blockeditor p {
font-size: 16px;
line-height: 1.6;
margin-bottom: 12px;
}
.ej-blockeditor code {
font-family: 'Monaco', 'Courier New', monospace;
font-size: 14px;
background-color: var(--color-surface);
padding: 2px 4px;
border-radius: 3px;
}Color Schemes
Light Theme
export const LIGHT_THEME = {
primary: '#2196F3',
secondary: '#FF9800',
background: '#FFFFFF',
surface: '#F5F5F5',
text: '#212121',
textSecondary: '#666666',
border: '#E0E0E0',
error: '#F44336',
success: '#4CAF50',
warning: '#FFC107',
info: '#2196F3'
};Dark Theme
export const DARK_THEME = {
primary: '#90CAF9',
secondary: '#FFCC80',
background: '#121212',
surface: '#1E1E1E',
text: '#FFFFFF',
textSecondary: '#BDBDBD',
border: '#424242',
error: '#EF5350',
success: '#81C784',
warning: '#FFD54F',
info: '#64B5F6'
};High Contrast Theme
export const HIGH_CONTRAST_THEME = {
primary: '#0000FF',
secondary: '#FFFF00',
background: '#FFFFFF',
surface: '#F0F0F0',
text: '#000000',
textSecondary: '#333333',
border: '#000000',
error: '#FF0000',
success: '#008000',
warning: '#CCAA00',
info: '#0000FF'
};Theme Service
@Injectable({ providedIn: 'root' })
export class ThemeService {
private themes = {
light: LIGHT_THEME,
dark: DARK_THEME,
'high-contrast': HIGH_CONTRAST_THEME
};
private currentTheme$ = new BehaviorSubject<string>('light');
public setTheme(theme: string): void {
if (this.themes[theme as keyof typeof this.themes]) {
this.currentTheme$.next(theme);
this.applyTheme(this.themes[theme as keyof typeof this.themes]);
}
}
public getTheme(name: string): any {
return this.themes[name as keyof typeof this.themes];
}
private applyTheme(theme: any): void {
const root = document.documentElement;
Object.entries(theme).forEach(([key, value]) => {
root.style.setProperty(`--color-${this.kebabCase(key)}`, value as string);
});
}
private kebabCase(str: string): string {
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
}
}Advanced Customization Examples
Example 1: Custom Editor with Full Theming
@Component({
selector: 'app-themed-editor-full',
template: `
<div class="editor-wrapper" [attr.data-theme]="currentTheme">
<div class="theme-selector">
<button *ngFor="let theme of themes"
(click)="setTheme(theme)"
[class.active]="currentTheme === theme">
{{ theme | titlecase }}
</button>
</div>
<ejs-blockeditor
#blockEditor
class="custom-block-editor"
[width]="'100%'"
[height]="'600px'"
/>
</div>
`,
styles: [`
.editor-wrapper {
padding: 20px;
}
[data-theme="light"] {
--bg-primary: #fff;
--text-primary: #333;
--border-color: #ddd;
}
[data-theme="dark"] {
--bg-primary: #1e1e1e;
--text-primary: #fff;
--border-color: #444;
}
[data-theme="high-contrast"] {
--bg-primary: #fff;
--text-primary: #000;
--border-color: #000;
}
.custom-block-editor {
background-color: var(--bg-primary);
color: var(--text-primary);
border: 2px solid var(--border-color);
}
.theme-selector {
margin-bottom: 16px;
display: flex;
gap: 8px;
}
.theme-selector button {
padding: 8px 16px;
border: 1px solid var(--border-color);
background-color: var(--bg-primary);
color: var(--text-primary);
cursor: pointer;
border-radius: 4px;
transition: all 0.3s ease;
}
.theme-selector button.active {
background-color: var(--text-primary);
color: var(--bg-primary);
}
`],
standalone: true,
imports: [BlockEditorModule, CommonModule]
})
export class ThemedEditorFullComponent {
public currentTheme = 'light';
public themes = ['light', 'dark', 'high-contrast'];
constructor(private themeService: ThemeService) {}
public setTheme(theme: string): void {
this.currentTheme = theme;
this.themeService.setTheme(theme);
}
}Example 2: Responsive Custom Styling
@Component({
selector: 'app-responsive-styled',
template: `<ejs-blockeditor [ngClass]="'editor-' + screenSize" />`,
styles: [`
/* Mobile */
.editor-xs {
width: 100%;
height: 400px;
}
.editor-xs ::ng-deep .toolbar {
flex-wrap: wrap;
}
/* Tablet */
.editor-sm {
width: 90%;
height: 500px;
margin: 0 auto;
}
/* Desktop */
.editor-md,
.editor-lg {
width: 80%;
height: 600px;
margin: 0 auto;
}
/* Large Desktop */
.editor-xl {
width: 1200px;
height: 700px;
margin: 0 auto;
}
`],
standalone: true,
imports: [BlockEditorModule]
})
export class ResponsiveStyledComponent {
public screenSize: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'md';
constructor() {
this.updateScreenSize();
window.addEventListener('resize', () => this.updateScreenSize());
}
private updateScreenSize(): void {
const width = window.innerWidth;
if (width < 576) this.screenSize = 'xs';
else if (width < 768) this.screenSize = 'sm';
else if (width < 992) this.screenSize = 'md';
else if (width < 1200) this.screenSize = 'lg';
else this.screenSize = 'xl';
}
}Block Management Methods
Table of Contents
- Overview
- addBlock Method
- removeBlock Method
- moveBlock Method
- updateBlock Method
- getBlock Method
- getBlockCount Method
- Practical Examples
Overview
Block management methods allow you to programmatically manipulate the editor content. Use these methods to:
- Add new blocks dynamically
- Remove blocks from the editor
- Rearrange blocks
- Update block properties
- Query block information
- Get editor statistics
Access these methods via a template reference to the BlockEditor component.
addBlock Method
Adds a new block to the editor at a specified position.
Syntax:
addBlock(block: BlockModel, targetBlockId?: string, insertAfter?: boolean): voidParameters:
block: The BlockModel to addtargetBlockId(optional): ID of the reference block for positioninginsertAfter(optional):trueto insert after target,falseto insert before. Default: end of editor
Returns: void
Examples:
// Add block at the end
const newBlock: BlockModel = {
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'New paragraph' }
]
};
this.blockEditor.addBlock(newBlock);
// Add block after a specific block
this.blockEditor.addBlock(newBlock, 'block-2', true);
// Add block before a specific block
this.blockEditor.addBlock(newBlock, 'block-3', false);
// Add a heading block
const headingBlock: BlockModel = {
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'New Section' }
]
};
this.blockEditor.addBlock(headingBlock, 'block-1', true);Use Cases:
- Inserting content programmatically
- Adding sections to a document template
- Building dynamic forms or documents
- Appending generated content
removeBlock Method
Removes a block from the editor by its ID.
Syntax:
removeBlock(blockId: string): voidParameters:
blockId: The ID of the block to remove
Returns: void
Examples:
// Remove a block by ID
this.blockEditor.removeBlock('block-to-remove');
// Remove the first block
this.blockEditor.removeBlock('block-1');
// Remove multiple blocks (one at a time)
['block-1', 'block-2', 'block-3'].forEach(id => {
this.blockEditor.removeBlock(id);
});Use Cases:
- Deleting outdated sections
- Cleaning up template blocks
- Removing user-selected content
- Reverting document changes
moveBlock Method
Moves a block to a new position.
Syntax:
moveBlock(fromBlockId: string, toBlockId: string): voidParameters:
fromBlockId: ID of the block to movetoBlockId: ID of the target block to move to
Returns: void
Description: Moves a block from its current position to the position of the target block. The exact behavior (insert before/after) is determined by the editor's internal logic.
Examples:
// Move block to another position
this.blockEditor.moveBlock('block-3', 'block-1');
// Reorder blocks
this.blockEditor.moveBlock('block-2', 'block-4');Use Cases:
- Rearranging content programmatically
- Reordering sections
- Reorganizing document structure
- Implementing drag-and-drop reordering
updateBlock Method
Updates the properties of an existing block. Only specified properties are modified.
Syntax:
updateBlock(blockId: string, updates: Partial<BlockModel>): booleanParameters:
blockId: ID of the block to updateupdates: Object containing properties to update
Returns: true if update succeeded, false otherwise
Examples:
// Update block indentation
const success = this.blockEditor.updateBlock('block-1', {
indent: 2
});
// Update block content
this.blockEditor.updateBlock('block-2', {
content: [
{ contentType: ContentType.Text, content: 'Updated text' }
]
});
// Update checklist item status
this.blockEditor.updateBlock('checklist-1', {
properties: { isChecked: true }
});
// Add CSS class to block
this.blockEditor.updateBlock('block-3', {
cssClass: 'highlighted-section'
});
// Update heading level
this.blockEditor.updateBlock('heading-1', {
properties: { level: 3 }
});
// Update with multiple properties
const updated = this.blockEditor.updateBlock('block-4', {
indent: 1,
cssClass: 'important-section',
content: [
{ contentType: ContentType.Text, content: 'Important' }
]
});
if (updated) {
console.log('Block updated successfully');
}Use Cases:
- Modifying content programmatically
- Updating styling based on conditions
- Changing block types or properties
- Batch updating multiple blocks
getBlock Method
Retrieves a block by its ID.
Syntax:
getBlock(blockId: string): BlockModel | nullParameters:
blockId: ID of the block to retrieve
Returns: BlockModel object or null if not found
Examples:
// Get a block
const block = this.blockEditor.getBlock('block-1');
if (block) {
console.log('Block type:', block.blockType);
console.log('Block content:', block.content);
}
// Check if block exists before updating
const blockToUpdate = this.blockEditor.getBlock('block-2');
if (blockToUpdate) {
console.log('Found block:', blockToUpdate.blockType);
} else {
console.log('Block not found');
}
// Get and display block information
const block = this.blockEditor.getBlock('heading-1');
if (block && block.content) {
const text = block.content[0].content;
console.log('Heading text:', text);
}
// Check heading level
const heading = this.blockEditor.getBlock('heading-2');
if (heading && heading.properties) {
console.log('Level:', heading.properties.level);
}Use Cases:
- Reading block data for validation
- Checking block properties
- Verifying block existence
- Building block-specific logic
getBlockCount Method
Returns the total number of blocks in the editor.
Syntax:
getBlockCount(): numberParameters: None
Returns: Number of blocks (integer)
Examples:
// Get total block count
const count = this.blockEditor.getBlockCount();
console.log(`Total blocks: ${count}`);
// Check if editor is empty
if (this.blockEditor.getBlockCount() === 0) {
console.log('Editor is empty');
}
// Validate minimum content
if (this.blockEditor.getBlockCount() < 3) {
alert('Document must have at least 3 blocks');
}
// Track document complexity
const blockCount = this.blockEditor.getBlockCount();
const complexity = blockCount > 10 ? 'Complex' : 'Simple';
console.log(`Document complexity: ${complexity}`);Use Cases:
- Validating document completeness
- Showing editor statistics
- Preventing empty submissions
- Calculating document metrics
Practical Examples
Example 1: Build a Document Template
public createTemplate(): void {
// Add title
this.blockEditor.addBlock({
blockType: 'Heading',
properties: { level: 1 },
content: [
{ contentType: ContentType.Text, content: 'New Document' }
]
});
// Add introduction section
this.blockEditor.addBlock({
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Introduction' }
]
});
// Add placeholder paragraph
this.blockEditor.addBlock({
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Add your introduction here...' }
]
});
console.log(`Template created with ${this.blockEditor.getBlockCount()} blocks`);
}Example 2: Reorganize Document
public reorganizeDocument(): void {
// Get all blocks
const count = this.blockEditor.getBlockCount();
// Move block to new position
if (count > 0) {
this.blockEditor.moveBlock('block-5', 'block-1');
}
// Reorder another block
if (count > 1) {
this.blockEditor.moveBlock('block-2', 'block-3');
}
}Example 3: Update Content Based on Validation
public validateAndUpdate(): void {
// Check first block
const firstBlock = this.blockEditor.getBlock('block-1');
if (!firstBlock || !firstBlock.content) {
// Add placeholder if missing
this.blockEditor.addBlock({
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Please add content' }
]
}, 'block-1', false);
}
// Validate block count
const totalBlocks = this.blockEditor.getBlockCount();
if (totalBlocks === 0) {
console.warn('Document is empty');
}
}Example 4: Duplicate a Block
public duplicateBlock(blockId: string): void {
const originalBlock = this.blockEditor.getBlock(blockId);
if (originalBlock) {
// Create a copy
const duplicatedBlock: BlockModel = { ...originalBlock };
delete duplicatedBlock.id; // Remove ID to let editor generate new one
// Add after original
this.blockEditor.addBlock(duplicatedBlock, blockId, true);
console.log('Block duplicated');
}
}Configuration Properties
Table of Contents
Overview
The Block Editor provides configuration properties for controlling appearance, behavior, and security. These properties are set during editor initialization.
---
Styling Properties
cssClass Property
Type: string
Default Value: ""
Description: Specifies one or more CSS class names to be added to the editor's root element. This allows for custom styling and theming of the editor.
Syntax:
[cssClass]="'custom-class'"Use Cases:
- Apply custom themes
- Add specific styling for different contexts
- Implement dark mode or light mode
- Apply responsive design classes
- Add framework-specific CSS classes (Bootstrap, Tailwind, etc.)
Basic Example:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-root',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[cssClass]="'custom-editor'">
</ejs-blockeditor>
`,
styles: [`
::ng-deep .custom-editor {
border: 2px solid #4CAF50;
border-radius: 8px;
}
`]
})
export class AppComponent {}Multiple Classes:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-root',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[cssClass]="'custom-editor dark-theme bordered'">
</ejs-blockeditor>
`,
styles: [`
::ng-deep .custom-editor.dark-theme {
background-color: #1e1e1e;
color: #ffffff;
}
::ng-deep .custom-editor.bordered {
border: 1px solid #444;
padding: 16px;
}
`]
})
export class AppComponent {}Dynamic Theme Switching:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-theming',
standalone: true,
imports: [BlockEditorModule],
template: `
<button (click)="toggleTheme()">Toggle Theme</button>
<ejs-blockeditor
[cssClass]="editorClasses">
</ejs-blockeditor>
`,
styles: [`
::ng-deep .light-theme {
background-color: #ffffff;
color: #000000;
}
::ng-deep .dark-theme {
background-color: #1e1e1e;
color: #ffffff;
}
`]
})
export class ThemingComponent {
public isDarkMode = false;
public editorClasses = 'light-theme';
public toggleTheme(): void {
this.isDarkMode = !this.isDarkMode;
this.editorClasses = this.isDarkMode ? 'dark-theme' : 'light-theme';
}
}Framework Integration Examples:
// Bootstrap Integration
@Component({
template: `
<ejs-blockeditor
[cssClass]="'border border-primary rounded shadow-sm p-3'">
</ejs-blockeditor>
`
})
export class BootstrapComponent {}
// Tailwind CSS Integration
@Component({
template: `
<ejs-blockeditor
[cssClass]="'border-2 border-blue-500 rounded-lg shadow-md p-4'">
</ejs-blockeditor>
`
})
export class TailwindComponent {}
// Material Design Integration
@Component({
template: `
<ejs-blockeditor
[cssClass]="'mat-elevation-z4 mat-app-background'">
</ejs-blockeditor>
`
})
export class MaterialComponent {}Responsive Design:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-responsive',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[cssClass]="'responsive-editor'">
</ejs-blockeditor>
`,
styles: [`
::ng-deep .responsive-editor {
max-width: 100%;
margin: 0 auto;
}
@media (min-width: 768px) {
::ng-deep .responsive-editor {
max-width: 800px;
}
}
@media (max-width: 767px) {
::ng-deep .responsive-editor {
padding: 8px;
font-size: 14px;
}
}
`]
})
export class ResponsiveComponent {}Context-Based Styling:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-context-styling',
standalone: true,
imports: [BlockEditorModule],
template: `
<div class="editor-container">
<!-- Admin Editor -->
<ejs-blockeditor
[cssClass]="'admin-editor'">
</ejs-blockeditor>
<!-- User Editor -->
<ejs-blockeditor
[cssClass]="'user-editor'">
</ejs-blockeditor>
<!-- Read-Only Viewer -->
<ejs-blockeditor
[cssClass]="'viewer-mode'">
</ejs-blockeditor>
</div>
`,
styles: [`
::ng-deep .admin-editor {
border: 2px solid #4CAF50;
box-shadow: 0 4px 6px rgba(76, 175, 80, 0.2);
}
::ng-deep .user-editor {
border: 1px solid #ccc;
}
::ng-deep .viewer-mode {
background-color: #f5f5f5;
pointer-events: none;
opacity: 0.9;
}
`]
})
export class ContextStylingComponent {}---
Security Properties
enableHtmlEncode Property
Type: boolean
Default Value: true
Description: Determines whether HTML content is encoded when pasted into the editor. When enabled, HTML tags in pasted content are converted to their encoded equivalents, preventing script injection and maintaining security.
Syntax:
[enableHtmlEncode]="true"Use Cases:
- Prevent XSS (Cross-Site Scripting) attacks
- Ensure secure content handling
- Control whether pasted HTML is rendered or displayed as text
- Implement security policies
Security Enabled (Default):
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-secure',
standalone: true,
imports: [BlockEditorModule],
template: `
<!-- HTML encoding enabled for security -->
<ejs-blockeditor
[enableHtmlEncode]="true">
</ejs-blockeditor>
`
})
export class SecureEditorComponent {}
// When user pastes: <script>alert('XSS')</script>
// Result: <script>alert('XSS')</script> (encoded, safe)Security Disabled:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-html-allowed',
standalone: true,
imports: [BlockEditorModule],
template: `
<!-- HTML encoding disabled - use with caution -->
<ejs-blockeditor
[enableHtmlEncode]="false">
</ejs-blockeditor>
`
})
export class HtmlAllowedEditorComponent {}
// When user pastes: <strong>Bold text</strong>
// Result: <strong>Bold text</strong> (rendered as HTML)Dynamic Security Configuration:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-security-config',
standalone: true,
imports: [BlockEditorModule],
template: `
<div class="security-controls">
<label>
<input
type="checkbox"
[(ngModel)]="htmlEncodeEnabled"
(change)="onSecurityChange()">
Enable HTML Encoding (Security)
</label>
<p class="warning" *ngIf="!htmlEncodeEnabled">
⚠️ Warning: Disabling HTML encoding may expose your application to XSS attacks
</p>
</div>
<ejs-blockeditor
[enableHtmlEncode]="htmlEncodeEnabled">
</ejs-blockeditor>
`
})
export class SecurityConfigComponent {
public htmlEncodeEnabled = true;
public onSecurityChange(): void {
if (!this.htmlEncodeEnabled) {
const confirmed = confirm(
'Disabling HTML encoding may pose security risks. Continue?'
);
if (!confirmed) {
this.htmlEncodeEnabled = true;
}
}
}
}Role-Based Security:
import { Component, OnInit } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-role-based',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[enableHtmlEncode]="securityEnabled">
</ejs-blockeditor>
`
})
export class RoleBasedSecurityComponent implements OnInit {
public securityEnabled = true;
private userRole = 'viewer'; // 'admin', 'editor', 'viewer'
ngOnInit(): void {
// Admin users can paste HTML content
// Other users have HTML encoding enabled for security
this.securityEnabled = this.userRole !== 'admin';
}
}Security Best Practices:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-best-practices',
standalone: true,
imports: [BlockEditorModule],
template: `
<!-- Production: Always enable for user-generated content -->
<ejs-blockeditor
[enableHtmlEncode]="true"
[pasteCleanupSettings]="pasteCleanupConfig">
</ejs-blockeditor>
`
})
export class BestPracticesComponent {
public pasteCleanupConfig = {
// Additional security through paste cleanup
allowedStyles: [],
deniedTags: ['script', 'iframe', 'object', 'embed'],
keepFormat: false,
plainText: false
};
}Comparison Table:
| Scenario | enableHtmlEncode | Security Level | Use Case |
|---|---|---|---|
| Public editor (user content) | true | High | Blogs, forums, comments |
| Admin content management | false | Low | Trusted admin users only |
| Internal documentation | true | Medium-High | Company wiki, docs |
| Template editor (admin) | false | Low | Email templates, layouts |
---
Combined Configuration Example
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-configured-editor',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[cssClass]="editorClasses"
[enableHtmlEncode]="securityEnabled"
[width]="'100%'"
[height]="'500px'">
</ejs-blockeditor>
`,
styles: [`
::ng-deep .premium-editor {
border: 2px solid #gold;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
background: linear-gradient(to bottom, #ffffff, #f8f8f8);
}
::ng-deep .premium-editor.dark-mode {
background: linear-gradient(to bottom, #1e1e1e, #2d2d2d);
color: #ffffff;
border-color: #444;
}
`]
})
export class ConfiguredEditorComponent {
public editorClasses = 'premium-editor';
public securityEnabled = true;
public applyDarkMode(): void {
this.editorClasses = 'premium-editor dark-mode';
}
public applyLightMode(): void {
this.editorClasses = 'premium-editor';
}
}---
Users and Labels
users Property
Type: UserModel[]
Default Value: []
Description: Specifies an array of user models representing editor participants. Enables multiple users to work together with visual identification through avatars, colors, and user cursors.
Syntax:
[users]="usersArray"Use Cases:
- Enable multi-user editing with multiple users
- Display user avatars and cursors
- Track who is editing which block
- Provide visual identification in the editor
- Implement user mentions and tagging
UserModel Interface:
interface UserModel {
id: string; // Unique identifier for the user (required)
user: string; // Display name of the user (required)
avatarBgColor?: string; // Avatar background color (also used as cursor color)
avatarUrl?: string; // URL to user's avatar image
cssClass?: string; // Custom CSS class for styling
}Basic Users Configuration:
import { Component } from '@angular/core';
import { BlockEditorModule, UserModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-root',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[users]="users"
[blocks]="blocksData">
</ejs-blockeditor>
`
})
export class AppComponent {
public users: UserModel[] = [
{
id: 'user1',
user: 'Alice Johnson',
avatarBgColor: '#FF5722',
avatarUrl: 'https://example.com/avatars/alice.jpg'
},
{
id: 'user2',
user: 'Bob Smith',
avatarBgColor: '#2196F3'
},
{
id: 'user3',
user: 'Charlie Davis',
avatarBgColor: '#4CAF50',
cssClass: 'admin-user'
}
];
public blocksData = [
{
blockType: 'Paragraph',
content: [{ contentType: 'Text', content: 'Start editing together!' }]
}
];
}Team Structure Example:
import { Component } from '@angular/core';
import { BlockEditorModule, UserModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-team-editor',
standalone: true,
imports: [BlockEditorModule],
template: `
<div class="editor-container">
<div class="user-panel">
<h3>Active Users</h3>
<div *ngFor="let user of users" class="user-card">
<div class="avatar" [style.backgroundColor]="user.avatarBgColor">
{{ getUserInitials(user.user) }}
</div>
<span>{{ user.user }}</span>
</div>
</div>
<ejs-blockeditor
[users]="users"
[blocks]="blocksData">
</ejs-blockeditor>
</div>
`,
styles: [`
.editor-container { display: flex; gap: 20px; }
.user-panel { width: 200px; padding: 15px; background: #f5f5f5; border-radius: 8px; }
.user-card { display: flex; align-items: center; gap: 10px; padding: 8px; margin-bottom: 8px; }
.avatar {
width: 32px; height: 32px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
color: white; font-weight: bold; font-size: 12px;
}
`]
})
export class TeamEditorComponent {
public users: UserModel[] = [
{ id: 'user1', user: 'Sarah Wilson', avatarBgColor: '#E91E63' },
{ id: 'user2', user: 'Michael Chen', avatarBgColor: '#9C27B0' },
{ id: 'user3', user: 'Emma Thompson', avatarBgColor: '#FF9800' }
];
public blocksData = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [{ contentType: 'Text', content: 'Team Editor' }]
}
];
public getUserInitials(name: string): string {
return name.split(' ').map(part => part[0]).join('').toUpperCase();
}
}Dynamic User Management:
import { Component } from '@angular/core';
import { BlockEditorModule, UserModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-dynamic-users',
standalone: true,
imports: [BlockEditorModule],
template: `
<button (click)="addUser()">Add User</button>
<button (click)="removeUser('user2')">Remove User</button>
<ejs-blockeditor
[users]="users">
</ejs-blockeditor>
`
})
export class DynamicUsersComponent {
public users: UserModel[] = [
{ id: 'user1', user: 'Alice', avatarBgColor: '#FF5722' }
];
public addUser(): void {
const newUser: UserModel = {
id: `user-${Date.now()}`,
user: 'New User',
avatarBgColor: this.getRandomColor()
};
this.users = [...this.users, newUser];
}
public removeUser(userId: string): void {
this.users = this.users.filter(user => user.id !== userId);
}
private getRandomColor(): string {
const colors = ['#FF5722', '#2196F3', '#4CAF50', '#FFC107', '#9C27B0'];
return colors[Math.floor(Math.random() * colors.length)];
}
}---
labelSettings Property
Type: LabelSettingsModel
Default Value: {}
Description: Configures the label/tag system using trigger characters for quick insertion. Enables users to add labels for categorization, status tracking, and content organization.
Syntax:
[labelSettings]="labelConfig"Use Cases:
- Quick status tagging using # character
- Categorize content with labels
- Visual identification with colors
- Organize labels into groups
- Implement @ mentions for users
- Track project status and priorities
LabelSettingsModel Interface:
interface LabelSettingsModel {
triggerChar?: string; // Character that triggers label popup (default: '$')
items?: LabelItemModel[]; // Array of available labels
}
interface LabelItemModel {
id: string; // Unique identifier for the label (required)
text: string; // Display text for the label (required)
labelColor?: string; // Color of the label
iconCss?: string; // CSS class for icon
groupBy?: string; // Group header for categorization
}Basic Label Configuration:
import { Component } from '@angular/core';
import { BlockEditorModule, LabelSettingsModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-root',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[labelSettings]="labelSettings"
[blocks]="blocksData">
</ejs-blockeditor>
`
})
export class AppComponent {
public labelSettings: LabelSettingsModel = {
triggerChar: '#',
items: [
{
id: 'bug',
text: 'Bug',
labelColor: '#ff5252',
iconCss: 'icon-bug',
groupBy: 'Status'
},
{
id: 'feature',
text: 'Feature',
labelColor: '#81c784',
iconCss: 'icon-feature',
groupBy: 'Status'
},
{
id: 'task',
text: 'Task',
labelColor: '#90caf9',
iconCss: 'icon-task',
groupBy: 'Status'
}
]
};
public blocksData = [
{
blockType: 'Paragraph',
content: [{ contentType: 'Text', content: 'Type # to add labels' }]
}
];
}Project Management Labels:
import { Component } from '@angular/core';
import { BlockEditorModule, LabelSettingsModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-project-labels',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[labelSettings]="projectLabels">
</ejs-blockeditor>
`
})
export class ProjectLabelsComponent {
public projectLabels: LabelSettingsModel = {
triggerChar: '#',
items: [
// Status Labels
{ id: 'todo', text: 'To Do', labelColor: '#9E9E9E', groupBy: 'Status' },
{ id: 'in-progress', text: 'In Progress', labelColor: '#2196F3', groupBy: 'Status' },
{ id: 'review', text: 'In Review', labelColor: '#FF9800', groupBy: 'Status' },
{ id: 'done', text: 'Done', labelColor: '#4CAF50', groupBy: 'Status' },
// Priority Labels
{ id: 'critical', text: 'Critical', labelColor: '#F44336', groupBy: 'Priority' },
{ id: 'high', text: 'High', labelColor: '#FF5722', groupBy: 'Priority' },
{ id: 'medium', text: 'Medium', labelColor: '#FFC107', groupBy: 'Priority' },
{ id: 'low', text: 'Low', labelColor: '#8BC34A', groupBy: 'Priority' },
// Type Labels
{ id: 'bug', text: 'Bug', labelColor: '#E91E63', iconCss: 'icon-bug', groupBy: 'Type' },
{ id: 'feature', text: 'Feature', labelColor: '#9C27B0', iconCss: 'icon-feature', groupBy: 'Type' },
{ id: 'docs', text: 'Documentation', labelColor: '#00BCD4', iconCss: 'icon-docs', groupBy: 'Type' }
]
};
}Using @ for Mentions:
import { Component } from '@angular/core';
import { BlockEditorModule, LabelSettingsModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-mentions',
standalone: true,
imports: [BlockEditorModule],
template: `
<ejs-blockeditor
[labelSettings]="mentionSettings">
</ejs-blockeditor>
`
})
export class MentionsComponent {
public mentionSettings: LabelSettingsModel = {
triggerChar: '@',
items: [
{
id: 'user1',
text: 'Alice Johnson',
labelColor: '#FF5722',
iconCss: 'icon-user',
groupBy: 'Team Members'
},
{
id: 'user2',
text: 'Bob Smith',
labelColor: '#2196F3',
iconCss: 'icon-user',
groupBy: 'Team Members'
},
{
id: 'manager1',
text: 'Sarah Wilson',
labelColor: '#9C27B0',
iconCss: 'icon-manager',
groupBy: 'Managers'
}
]
};
}Dynamic Label Management:
import { Component } from '@angular/core';
import { BlockEditorModule, LabelSettingsModel, LabelItemModel } from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-dynamic-labels',
standalone: true,
imports: [BlockEditorModule],
template: `
<button (click)="addLabel()">Add Label</button>
<button (click)="removeLabel('bug')">Remove Bug Label</button>
<ejs-blockeditor
[labelSettings]="labelSettings">
</ejs-blockeditor>
`
})
export class DynamicLabelsComponent {
public labelSettings: LabelSettingsModel = {
triggerChar: '#',
items: [
{ id: 'bug', text: 'Bug', labelColor: '#ff5252', groupBy: 'Type' }
]
};
public addLabel(): void {
const newLabel: LabelItemModel = {
id: `label-${Date.now()}`,
text: 'New Label',
labelColor: this.getRandomColor(),
groupBy: 'Custom'
};
if (!this.labelSettings.items) {
this.labelSettings.items = [];
}
this.labelSettings.items = [...this.labelSettings.items, newLabel];
}
public removeLabel(labelId: string): void {
if (this.labelSettings.items) {
this.labelSettings.items = this.labelSettings.items.filter(
item => item.id !== labelId
);
}
}
public updateLabelColor(labelId: string, newColor: string): void {
if (this.labelSettings.items) {
const label = this.labelSettings.items.find(item => item.id === labelId);
if (label) {
label.labelColor = newColor;
}
}
}
private getRandomColor(): string {
const colors = ['#FF5722', '#2196F3', '#4CAF50', '#FFC107', '#9C27B0', '#E91E63'];
return colors[Math.floor(Math.random() * colors.length)];
}
}Complete Users and Labels Example:
import { Component } from '@angular/core';
import {
BlockEditorModule,
UserModel,
LabelSettingsModel
} from '@syncfusion/ej2-angular-blockeditor';
@Component({
selector: 'app-project-editor',
standalone: true,
imports: [BlockEditorModule],
template: `
<div class="editor-wrapper">
<h2>Project Editor</h2>
<p>Type @ to mention users or # to add labels</p>
<ejs-blockeditor
[users]="users"
[labelSettings]="labelSettings"
[blocks]="blocksData">
</ejs-blockeditor>
</div>
`
})
export class ProjectEditorComponent {
public users: UserModel[] = [
{ id: 'user1', user: 'Alice Johnson', avatarBgColor: '#FF5722' },
{ id: 'user2', user: 'Bob Smith', avatarBgColor: '#2196F3' },
{ id: 'user3', user: 'Charlie Davis', avatarBgColor: '#4CAF50' }
];
public labelSettings: LabelSettingsModel = {
triggerChar: '#',
items: [
{ id: 'urgent', text: 'Urgent', labelColor: '#F44336', groupBy: 'Priority' },
{ id: 'bug', text: 'Bug', labelColor: '#E91E63', groupBy: 'Type' },
{ id: 'todo', text: 'To Do', labelColor: '#9E9E9E', groupBy: 'Status' },
{ id: 'done', text: 'Done', labelColor: '#4CAF50', groupBy: 'Status' }
]
};
public blocksData = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [{ contentType: 'Text', content: 'Project Tasks' }]
},
{
blockType: 'Paragraph',
content: [
{ contentType: 'Text', content: 'Type @ to mention team members or # to add status labels.' }
]
}
];
}Best Practices:
For Users: 1. Use unique, stable IDs for each user 2. Provide distinct colors for easy visual identification 3. Include avatarBgColor even when using avatarUrl as fallback 4. Limit to active users only for performance 5. Update user list dynamically as users join/leave
For Labels: 1. Use concise, descriptive text (2-15 characters) 2. Use consistent colors for similar categories 3. Group related labels using groupBy property 4. Choose intuitive trigger characters (# for tags, @ for mentions) 5. Keep label count manageable (< 50 items recommended) 6. Use unique IDs that won't conflict with user data
---
Core Concepts: Blocks and Content Model
Table of Contents
- What is a Block?
- Block Types Overview
- Block Model Structure
- Content Model Structure
- Content Types
- Block Properties
What is a Block?
Blocks are the fundamental building units of the Block Editor. Each block represents a distinct piece of content that can be independently managed, formatted, and rearranged.
The entire editor content is structured as a collection of blocks, configured through the blocks property of the BlockEditor component. This block-based architecture allows users to:
- Rearrange content independently
- Format discrete sections separately
- Add or remove content units easily
- Apply different styles to different sections
Block Types Overview
The Block Editor supports 13 built-in block types:
| Block Type | Description | Editable | Use Case |
|---|---|---|---|
| Paragraph | Regular text content | Yes | Default text blocks |
| Heading1-4 | Hierarchical headings | Yes | Document structure, titles |
| BulletList | Unordered lists | Yes | Feature lists, bullet points |
| NumberedList | Ordered lists | Yes | Steps, procedures, rankings |
| Checklist | Interactive to-do items | Yes | Tasks, requirements, action items |
| Table | Tabular data with rows/columns | Yes | Data presentation, comparisons |
| Code | Formatted code with syntax highlighting | Yes | Code snippets, examples |
| Quote | Styled quotation blocks | Yes | References, important statements |
| Callout | Highlighted information blocks | Yes | Notes, warnings, important info |
| Divider | Horizontal separator line | No | Visual separation |
| Image | Embedded images | Yes | Visual content, illustrations |
| CollapsibleParagraph | Expandable paragraph | Yes | Collapsible text sections |
| CollapsibleHeading1-4 | Expandable headings | Yes | Expandable sections with titles |
Block Model Structure
A block is defined using the BlockModel interface with the following structure:
interface BlockModel {
id?: string; // Unique identifier
blockType: string; // Type from the list above
content?: ContentModel[]; // Array of content items
properties?: object; // Block-specific properties
indent?: number; // Indentation level (0+)
cssClass?: string; // Custom CSS classes
children?: BlockModel[]; // For nested blocks (Quote, Callout, Collapsible)
parentId?: string; // Parent block ID (for nested blocks)
isExpanded?: boolean; // For collapsible blocks
isChecked?: boolean; // For checklist items
template?: string | Function; // For template blocks
}Example Block Definitions
// Paragraph block
{
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'This is a paragraph'
}
]
}
// Heading with indent
{
blockType: 'Heading',
properties: { level: 2 },
indent: 1,
content: [
{
contentType: ContentType.Text,
content: 'Section Subtitle'
}
]
}
// Collapsible section with children
{
blockType: 'CollapsibleHeading',
properties: {
level: 1,
isExpanded: true,
children: [
{
blockType: 'Paragraph',
content: [{ contentType: ContentType.Text, content: 'Content inside' }]
}
]
}
}Content Model Structure
Each block contains an array of ContentModel items that define the actual content within the block:
interface ContentModel {
id?: string; // Unique identifier
contentType: string; // Type: Text, Link, Label, Mention
content?: string; // Text content
properties?: object; // Content-specific properties
}Content Properties by Type
// Text content
{
contentType: ContentType.Text,
content: 'Your text here',
properties: {
styles: {
bold: true,
italic: false,
color: '#333',
backgroundColor: '#fff'
}
}
}
// Link content
{
contentType: ContentType.Link,
content: 'Click here',
properties: {
url: 'https://example.com',
title: 'Example Site'
}
}
// Label content
{
contentType: ContentType.Label,
properties: {
labelId: 'bug' // References a label item
}
}
// Mention content
{
contentType: ContentType.Mention,
properties: {
userId: 'user123' // References a user
}
}Content Types
The Block Editor supports four inline content types:
1. Text Content
Plain or formatted text. Supports inline styles like bold, italic, underline, colors, and more.
{
contentType: ContentType.Text,
content: 'Bold and italic text',
properties: {
styles: {
bold: true,
italic: true
}
}
}2. Link Content
Hyperlinks that can be clicked to navigate or open URLs.
{
contentType: ContentType.Link,
content: 'Visit Documentation',
properties: {
url: 'https://ej2.syncfusion.com/documentation'
}
}3. Label Content
Tags or labels for categorizing or marking content. Supports grouping and custom colors.
{
contentType: ContentType.Label,
properties: {
labelId: 'high-priority' // User-defined
}
}4. Mention Content
References to users that can be used for notifications or tagging.
{
contentType: ContentType.Mention,
properties: {
userId: 'john-doe' // From users collection
}
}Block Properties
Block-specific properties are configured in the properties object:
Common Properties
| Property | Type | Example | Used In |
|---|---|---|---|
level | number | 1-4 | Heading, CollapsibleHeading |
placeholder | string | "Add item" | List blocks |
isExpanded | boolean | true/false | Collapsible blocks |
isChecked | boolean | true/false | Checklist |
language | string | 'javascript' | Code blocks |
children | BlockModel[] | [...] | Quote, Callout, Collapsible |
Example Properties Configuration
// Heading with level
{
blockType: 'Heading',
properties: {
level: 2,
placeholder: 'Enter section title'
}
}
// Code block with language
{
blockType: 'Code',
properties: {
language: 'typescript'
}
}
// Checklist item (checked)
{
blockType: 'Checklist',
properties: {
isChecked: true,
placeholder: 'Add a task'
}
}
// Collapsible section
{
blockType: 'CollapsibleParagraph',
properties: {
isExpanded: false,
children: [
{
blockType: 'Paragraph',
content: [{ contentType: ContentType.Text, content: 'Hidden content' }]
}
]
}
}Block Relationships
Indentation
The indent property controls nesting level for list-like structures:
// Nested list items
[
{
blockType: 'BulletList',
indent: 0,
content: [{ contentType: ContentType.Text, content: 'Item 1' }]
},
{
blockType: 'BulletList',
indent: 1,
content: [{ contentType: ContentType.Text, content: 'Subitem 1.1' }]
},
{
blockType: 'BulletList',
indent: 1,
content: [{ contentType: ContentType.Text, content: 'Subitem 1.2' }]
}
]Parent-Child Relationships
For nested blocks (Quote, Callout, Collapsible), the children property in properties stores child blocks:
{
id: 'quote-1',
blockType: 'Quote',
properties: {
children: [
{
parentId: 'quote-1',
blockType: 'Paragraph',
content: [{ contentType: ContentType.Text, content: 'Quote content' }]
}
]
}
}Data Export and Import
Table of Contents
Exporting Data
Export as JSON
Export the current editor content as JSON format:
@ViewChild('blockEditor', { static: false }) blockEditorObj!: BlockEditorComponent;
public exportAsJson(): void {
const jsonData = this.blockEditorObj.getDataAsJson();
console.log('Editor JSON:', jsonData);
// Download as file
this.downloadFile(jsonData, 'document.json', 'application/json');
}
private downloadFile(data: any, filename: string, type: string): void {
const dataStr = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
const dataBlob = new Blob([dataStr], { type: type });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}Export as HTML
Export content as HTML for web display:
public exportAsHtml(): void {
const htmlData = this.blockEditorObj.getDataAsHtml();
console.log('Editor HTML:', htmlData);
// Download as file
this.downloadFile(htmlData, 'document.html', 'text/html');
}Export as Markdown
Convert to markdown format:
public exportAsMarkdown(): void {
const jsonData = this.blockEditorObj.getDataAsJson();
const markdown = this.convertToMarkdown(jsonData);
this.downloadFile(markdown, 'document.md', 'text/markdown');
}
private convertToMarkdown(blocks: BlockModel[]): string {
return blocks.map(block => {
switch (block.blockType) {
case 'Heading':
const level = block.properties?.level || 1;
const heading = this.getBlockText(block);
return `${'#'.repeat(level)} ${heading}`;
case 'Paragraph':
return this.getBlockText(block);
case 'BulletList':
return `- ${this.getBlockText(block)}`;
case 'NumberedList':
return `1. ${this.getBlockText(block)}`;
case 'Code':
const lang = block.properties?.language || '';
return `${lang}\n${this.getBlockText(block)}`;
default:
return this.getBlockText(block);
}
}).join('\n\n');
}
private getBlockText(block: BlockModel): string {
return block.content?.map(c => c.content || '').join('') || '';
}Print Document
Print the editor content:
public printDocument(): void {
const printWindow = window.open('', '', 'height=600,width=800');
if (printWindow) {
const htmlContent = this.blockEditorObj.getDataAsHtml();
const styles = `
<style>
body { font-family: Arial, sans-serif; }
h1, h2, h3 { margin-top: 16px; }
code { background-color: #f5f5f5; padding: 2px 4px; }
</style>
`;
printWindow.document.write(styles + htmlContent);
printWindow.document.close();
printWindow.print();
}
}Importing Data
Import from JSON
Load JSON data into the editor:
public importFromJson(): void {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'application/json';
fileInput.onchange = (event: any) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e: any) => {
try {
const jsonData = JSON.parse(e.target.result);
this.blockEditorObj.setDataAsJson(jsonData);
} catch (error) {
console.error('Invalid JSON:', error);
}
};
reader.readAsText(file);
};
fileInput.click();
}Import from HTML
Load HTML content into the editor:
public importFromHtml(): void {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'text/html';
fileInput.onchange = (event: any) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e: any) => {
try {
const htmlContent = e.target.result;
const blocks = this.blockEditorObj.parseHtmlToBlocks(htmlContent);
this.blockEditorObj.setDataAsJson(blocks);
} catch (error) {
console.error('HTML parsing error:', error);
}
};
reader.readAsText(file);
};
fileInput.click();
}Import from Clipboard
Paste content from clipboard:
public importFromClipboard(): void {
navigator.clipboard.readText().then(text => {
try {
const jsonData = JSON.parse(text);
this.blockEditorObj.setDataAsJson(jsonData);
} catch (error) {
// Try parsing as HTML if JSON fails
const blocks = this.blockEditorObj.parseHtmlToBlocks(text);
this.blockEditorObj.setDataAsJson(blocks);
}
});
}Conversion Methods
Method: getDataAsJson
Get editor content as JSON array of blocks:
public method_getDataAsJson(): void {
const jsonData: BlockModel[] = this.blockEditorObj.getDataAsJson();
console.log('Block count:', jsonData.length);
console.log('First block type:', jsonData[0].blockType);
}Returns: BlockModel[] - Array of block objects
Method: getDataAsHtml
Get editor content as HTML string:
public method_getDataAsHtml(): void {
const htmlContent: string = this.blockEditorObj.getDataAsHtml();
console.log('HTML length:', htmlContent.length);
// Display in preview panel
const preview = document.getElementById('preview');
if (preview) {
preview.innerHTML = htmlContent;
}
}Returns: string - HTML representation
Method: parseHtmlToBlocks
Convert HTML to Block Editor format:
public method_parseHtmlToBlocks(): void {
const htmlString = `
<h2>Title</h2>
<p>Paragraph content</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
`;
const blocks: BlockModel[] = this.blockEditorObj.parseHtmlToBlocks(htmlString);
console.log('Converted blocks:', blocks);
}Converts HTML tags:
<h1>-<h6>→ Heading blocks<p>→ Paragraph blocks<ul>→ BulletList blocks<ol>→ NumberedList blocks<pre><code>→ Code blocks<table>→ Table blocks
Method: renderBlocksFromJson
Render blocks from JSON format:
public method_renderBlocksFromJson(): void {
const blocksData: BlockModel[] = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [
{ contentType: ContentType.Text, content: 'Title' }
]
},
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Content here' }
]
}
];
this.blockEditorObj.setDataAsJson(blocksData);
}Serialization Examples
Example 1: Complete Export/Import Workflow
@Component({
selector: 'app-document-manager',
template: `
<div class="controls">
<button (click)="saveDocument()">Save</button>
<button (click)="loadDocument()">Load</button>
<button (click)="exportHtml()">Export HTML</button>
</div>
<ejs-blockeditor #blockEditor></ejs-blockeditor>
`,
standalone: true,
imports: [BlockEditorModule]
})
export class DocumentManagerComponent {
@ViewChild('blockEditor') blockEditor!: BlockEditorComponent;
public saveDocument(): void {
const data = this.blockEditor.getDataAsJson();
const jsonString = JSON.stringify(data, null, 2);
// Save to localStorage for demo
localStorage.setItem('document', jsonString);
console.log('Document saved');
}
public loadDocument(): void {
const jsonString = localStorage.getItem('document');
if (jsonString) {
const data = JSON.parse(jsonString);
this.blockEditor.setDataAsJson(data);
console.log('Document loaded');
}
}
public exportHtml(): void {
const html = this.blockEditor.getDataAsHtml();
const blob = new Blob([html], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'document.html';
link.click();
}
}Example 2: Batch Operations
public processBatch(jsonArray: BlockModel[][]): void {
jsonArray.forEach((blocks, index) => {
const html = this.convertBlocksToHtml(blocks);
const filename = `document_${index + 1}.html`;
this.saveFile(html, filename);
});
}
private convertBlocksToHtml(blocks: BlockModel[]): string {
let html = '';
blocks.forEach(block => {
switch (block.blockType) {
case 'Heading':
const level = block.properties?.level || 1;
html += `<h${level}>${this.getBlockText(block)}</h${level}>`;
break;
case 'Paragraph':
html += `<p>${this.getBlockText(block)}</p>`;
break;
// ... other block types
}
});
return html;
}
private saveFile(content: string, filename: string): void {
const blob = new Blob([content], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}Practical Use Cases
Use Case 1: Document Versioning
@Component({
selector: 'app-version-control',
standalone: true,
imports: [BlockEditorModule, CommonModule]
})
export class VersionControlComponent {
@ViewChild('blockEditor') blockEditor!: BlockEditorComponent;
public versions: Array<{timestamp: Date, data: BlockModel[]}> = [];
public saveVersion(): void {
const currentData = this.blockEditor.getDataAsJson();
this.versions.push({
timestamp: new Date(),
data: JSON.parse(JSON.stringify(currentData))
});
console.log(`Version ${this.versions.length} saved`);
}
public restoreVersion(index: number): void {
const versionData = this.versions[index].data;
this.blockEditor.setDataAsJson(versionData);
console.log(`Restored version ${index + 1}`);
}
public getVersions(): Array<{timestamp: Date, data: BlockModel[]}> {
return this.versions;
}
}Use Case 2: API Synchronization
@Injectable()
export class DocumentService {
constructor(private http: HttpClient) {}
public saveToServer(blockEditor: BlockEditorComponent): Observable<any> {
const data = blockEditor.getDataAsJson();
return this.http.post('/api/documents', { blocks: data });
}
public loadFromServer(id: string): Observable<BlockModel[]> {
return this.http.get<BlockModel[]>(`/api/documents/${id}`);
}
public exportDocument(blockEditor: BlockEditorComponent, format: 'json' | 'html'): string {
if (format === 'json') {
return JSON.stringify(blockEditor.getDataAsJson(), null, 2);
} else {
return blockEditor.getDataAsHtml();
}
}
}Use Case 3: Template Generation
public createTemplateFromCurrent(): void {
const currentData = this.blockEditor.getDataAsJson();
const template = {
name: 'My Template',
created: new Date(),
blocks: currentData,
description: 'Custom template'
};
localStorage.setItem('template_1', JSON.stringify(template));
}
public applyTemplate(templateName: string): void {
const template = localStorage.getItem(templateName);
if (template) {
const parsed = JSON.parse(template);
this.blockEditor.setDataAsJson(parsed.blocks);
}
}Data Structures and Lists
Table of Contents
List Block Types
The Block Editor supports three primary list types for organizing content:
| List Type | Use Case | Interactive | Default Placeholder |
|---|---|---|---|
| BulletList | Unordered items | No | "Add item" |
| NumberedList | Ordered items | No | "Add item" |
| Checklist | Toggleable tasks | Yes | "Todo" |
Bullet Lists
Unordered lists with bullet points. Perfect for feature lists, requirements, or non-sequential items.
Basic Configuration
{
blockType: 'BulletList',
content: [
{
contentType: ContentType.Text,
content: 'First item'
}
]
}With Custom Placeholder
{
blockType: 'BulletList',
properties: { placeholder: 'Add feature' },
content: [
{
contentType: ContentType.Text,
content: 'Item with custom placeholder'
}
]
}Multiple Items
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Features:' }
]
},
{
blockType: 'BulletList',
content: [
{ contentType: ContentType.Text, content: 'Feature 1' }
]
},
{
blockType: 'BulletList',
content: [
{ contentType: ContentType.Text, content: 'Feature 2' }
]
},
{
blockType: 'BulletList',
content: [
{ contentType: ContentType.Text, content: 'Feature 3' }
]
}Nested Bullet Lists
Use indentation to create nested lists:
[
{
blockType: 'BulletList',
indent: 0,
content: [
{ contentType: ContentType.Text, content: 'Main topic' }
]
},
{
blockType: 'BulletList',
indent: 1,
content: [
{ contentType: ContentType.Text, content: 'Subtopic 1' }
]
},
{
blockType: 'BulletList',
indent: 1,
content: [
{ contentType: ContentType.Text, content: 'Subtopic 2' }
]
},
{
blockType: 'BulletList',
indent: 2,
content: [
{ contentType: ContentType.Text, content: 'Sub-subtopic' }
]
}
]Numbered Lists
Ordered lists with sequential numbering. Ideal for steps, procedures, or ranked items.
Basic Configuration
{
blockType: 'NumberedList',
content: [
{
contentType: ContentType.Text,
content: 'First step'
}
]
}Step-by-Step Example
[
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Installation Steps' }
]
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'Install Node.js' }
]
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'Install Angular CLI' }
]
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'Create new project' }
]
},
{
blockType: 'NumberedList',
content: [
{ contentType: ContentType.Text, content: 'Install dependencies' }
]
}
]Nested Numbered Lists
[
{
blockType: 'NumberedList',
indent: 0,
content: [
{ contentType: ContentType.Text, content: 'Main step' }
]
},
{
blockType: 'NumberedList',
indent: 1,
content: [
{ contentType: ContentType.Text, content: 'Sub-step a' }
]
},
{
blockType: 'NumberedList',
indent: 1,
content: [
{ contentType: ContentType.Text, content: 'Sub-step b' }
]
}
]Checklists
Interactive to-do lists with toggleable checkboxes.
Basic Configuration
{
blockType: 'Checklist',
content: [
{
contentType: ContentType.Text,
content: 'Task 1'
}
]
}With Checked State
{
blockType: 'Checklist',
properties: { isChecked: true },
content: [
{
contentType: ContentType.Text,
content: 'Completed task'
}
]
}Task List Example
[
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{ contentType: ContentType.Text, content: 'Project Tasks' }
]
},
{
blockType: 'Checklist',
properties: { isChecked: true },
content: [
{ contentType: ContentType.Text, content: 'Design mockups' }
]
},
{
blockType: 'Checklist',
properties: { isChecked: true },
content: [
{ contentType: ContentType.Text, content: 'Create API' }
]
},
{
blockType: 'Checklist',
properties: { isChecked: false },
content: [
{ contentType: ContentType.Text, content: 'Implement frontend' }
]
},
{
blockType: 'Checklist',
properties: { isChecked: false },
content: [
{ contentType: ContentType.Text, content: 'Write tests' }
]
}
]Tables
Tables display data in rows and columns with full configuration options.
Basic Table
{
blockType: 'Table',
properties: {
columns: [
{ id: 'col1', headerText: 'Name' },
{ id: 'col2', headerText: 'Value' }
],
rows: [
{
cells: [
{
columnId: 'col1',
blocks: [
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Item 1' }
]
}
]
},
{
columnId: 'col2',
blocks: [
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Value 1' }
]
}
]
}
]
}
]
}
}Table Properties
| Property | Type | Description | Default |
|---|---|---|---|
width | string | Display width | "100%" |
enableHeader | boolean | Show header row | true |
enableRowNumbers | boolean | Show row indices | true |
readOnly | boolean | Prevent edits | false |
columns | array | Column definitions | [] |
rows | array | Row data | [] |
Multi-Column Table
{
blockType: 'Table',
properties: {
columns: [
{ id: 'col1', headerText: 'Feature' },
{ id: 'col2', headerText: 'Status' },
{ id: 'col3', headerText: 'Progress' }
],
rows: [
{
cells: [
{
columnId: 'col1',
blocks: [
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Authentication' }
]
}
]
},
{
columnId: 'col2',
blocks: [
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'In Progress' }
]
}
]
},
{
columnId: 'col3',
blocks: [
{
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: '75%' }
]
}
]
}
]
}
]
}
}Nested Blocks
Certain blocks can contain nested child blocks: Quote, Callout, and Collapsible blocks.
Quote Block with Children
{
id: 'quote-1',
blockType: 'Quote',
properties: {
children: [
{
parentId: 'quote-1',
blockType: 'Heading',
properties: { level: 3 },
content: [
{ contentType: ContentType.Text, content: 'Famous Quote' }
]
},
{
parentId: 'quote-1',
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Quote text here...' }
]
}
]
}
}Callout Block with Children
{
id: 'callout-1',
blockType: 'Callout',
properties: {
children: [
{
parentId: 'callout-1',
blockType: 'Heading',
properties: { level: 3 },
content: [
{ contentType: ContentType.Text, content: 'Important Notice' }
]
},
{
parentId: 'callout-1',
blockType: 'BulletList',
content: [
{ contentType: ContentType.Text, content: 'Point 1' }
]
},
{
parentId: 'callout-1',
blockType: 'BulletList',
content: [
{ contentType: ContentType.Text, content: 'Point 2' }
]
}
]
}
}Collapsible Paragraph
{
id: 'collapsible-1',
blockType: 'CollapsibleParagraph',
properties: {
isExpanded: false,
children: [
{
parentId: 'collapsible-1',
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'Hidden content shown on expand'
}
]
}
]
}
}Collapsible Heading
{
id: 'collapsible-heading-1',
blockType: 'CollapsibleHeading',
properties: {
level: 2,
isExpanded: true,
children: [
{
parentId: 'collapsible-heading-1',
blockType: 'Paragraph',
content: [
{ contentType: ContentType.Text, content: 'Section content' }
]
}
]
}
}Examples
Example 1: Feature Comparison Table
{
blockType: 'Table',
properties: {
columns: [
{ id: 'feature', headerText: 'Feature' },
{ id: 'basic', headerText: 'Basic' },
{ id: 'pro', headerText: 'Pro' }
],
rows: [
{ cells: [
{ columnId: 'feature', blocks: [...] },
{ columnId: 'basic', blocks: [...] },
{ columnId: 'pro', blocks: [...] }
]},
// More rows...
]
}
}Example 2: Documentation Structure
[
{ blockType: 'Heading', properties: { level: 1 }, content: [...] },
{
blockType: 'CollapsibleHeading',
properties: {
level: 2,
isExpanded: true,
children: [
{ blockType: 'Paragraph', content: [...] },
{ blockType: 'NumberedList', content: [...] }
]
}
},
{
blockType: 'CollapsibleHeading',
properties: {
level: 2,
isExpanded: false,
children: [
{ blockType: 'Code', content: [...] }
]
}
}
]Example 3: Requirement Checklist
[
{ blockType: 'Heading', properties: { level: 2 }, content: [...] },
{ blockType: 'Checklist', properties: { isChecked: true }, content: [...] },
{ blockType: 'Checklist', properties: { isChecked: false }, content: [...] },
// More checklist items...
]Customizing Editor Menus
Table of Contents
Overview
The Block Editor provides four customizable menus that enable efficient content creation:
1. Slash Command Menu - Type "/" to access block creation commands 2. Context Menu - Right-click to access editing actions 3. Block Action Menu - Click menu icon next to block for block-specific actions 4. Inline Toolbar - Select text to access formatting options
Each menu can be customized with custom items, filtered commands, and event handlers.
Slash Command Menu
The Slash Command menu opens when typing "/" and provides quick access to block insertion commands.
Built-in Commands
The menu includes commands for:
- Headings: Heading 1, 2, 3, 4
- Lists: Bullet List, Numbered List, Checklist
- Blocks: Paragraph, Quote, Callout, Divider
- Content: Table, Image, Code Block, Embed
- Utility: Toggle (collapsible), Horizontal Rule
Configuration
Customize using the commandMenuSettings property:
commandMenuSettings: CommandMenuSettingsModel = {
popupWidth: '350px',
popupHeight: '400px',
commands: [
// Custom command items
{
id: 'custom-cmd',
type: BlockType.Divider,
groupBy: 'Utility',
label: 'Custom Command',
iconCss: 'e-icons e-custom-icon'
}
],
filtering: (args: CommandFilteringEventArgs) => {
// Handle filtering
},
itemSelect: (args: CommandItemSelectEventArgs) => {
// Handle selection
}
};Properties
| Property | Type | Description |
|---|---|---|
popupWidth | string | Width of the popup menu |
popupHeight | string | Height of the popup menu |
commands | array | Array of custom command items |
filtering | function | Event triggered when filtering |
itemSelect | function | Event triggered on selection |
Example: Add Custom Slash Command
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
import { BlockModel, BlockType, CommandFilteringEventArgs } from '@syncfusion/ej2-blockeditor';
@Component({
imports: [BlockEditorModule],
standalone: true,
selector: 'app-root',
template: `<ejs-blockeditor [blocks]="blocksData" [commandMenuSettings]="commandMenuSettings" />`
})
export class AppComponent {
public blocksData: BlockModel[] = [
{
blockType: 'Paragraph',
content: [{ contentType: ContentType.Text, content: 'Type "/" to open menu' }]
}
];
public commandMenuSettings = {
popupWidth: '350px',
popupHeight: '400px',
commands: [
{
id: 'custom-divider',
type: BlockType.Divider,
groupBy: 'Utility',
label: 'Horizontal Line',
iconCss: 'e-icons e-separate'
},
{
id: 'timestamp',
groupBy: 'Actions',
label: 'Insert Timestamp',
iconCss: 'e-icons e-schedule'
}
],
itemSelect: (args: CommandItemSelectEventArgs) => {
if (args.item.id === 'timestamp') {
// Handle custom command
console.log('Timestamp inserted');
}
}
};
}Context Menu
The Context menu appears on right-click and provides editing actions.
Built-in Items
- Undo/Redo: Revert or reapply actions
- Cut/Copy/Paste: Clipboard operations
- Link: Add or edit hyperlinks
- Indent/Outdent: Adjust block indentation
Configuration
Use contextMenuSettings to customize:
contextMenuSettings = {
enable: true,
showItemOnClick: true,
items: [
{
id: 'format-menu',
text: 'Format',
iconCss: 'e-icons e-format-painter',
items: [
{ id: 'bold', text: 'Bold', iconCss: 'e-icons e-bold' },
{ id: 'italic', text: 'Italic', iconCss: 'e-icons e-italic' }
]
},
{ separator: true },
{
id: 'export-menu',
text: 'Export',
items: [
{ id: 'export-json', text: 'As JSON' },
{ id: 'export-html', text: 'As HTML' }
]
}
],
opening: (args: ContextMenuOpeningEventArgs) => {
// Modify menu before opening
},
closing: (args: ContextMenuClosingEventArgs) => {
// Handle menu closing
},
itemSelect: (args: ContextMenuItemSelectEventArgs) => {
// Handle item selection
}
};Properties
| Property | Type | Description |
|---|---|---|
enable | boolean | Enable/disable context menu |
showItemOnClick | boolean | Show items on click vs hover |
items | array | Menu items with nested support |
opening | function | Fired before menu opens |
closing | function | Fired before menu closes |
itemSelect | function | Fired when item is selected |
Example: Custom Context Menu
const customContextMenu = {
enable: true,
showItemOnClick: true,
items: [
{
id: 'statistics',
text: 'Block Statistics',
iconCss: 'e-icons e-chart'
},
{ separator: true },
{
id: 'export',
text: 'Export',
items: [
{ id: 'json', text: 'JSON', iconCss: 'e-icons e-file-json' },
{ id: 'html', text: 'HTML', iconCss: 'e-icons e-file-html' }
]
}
],
itemSelect: (args: ContextMenuItemSelectEventArgs) => {
if (args.item.id === 'statistics') {
console.log('Show statistics');
}
}
};Block Action Menu
The Block Action menu appears when hovering over a block's drag handle icon.
Built-in Actions
- Duplicate: Create a copy of the block
- Delete: Remove the block
- Move Up/Down: Reorder blocks
Configuration
Use blockActionMenuSettings:
blockActionMenuSettings = {
enable: true,
popupWidth: '180px',
popupHeight: '110px',
enableTooltip: true,
items: [
{
id: 'highlight',
label: 'Highlight Block',
iconCss: 'e-icons e-highlight',
tooltip: 'Add highlight styling'
},
{
id: 'copy-content',
label: 'Copy Content',
iconCss: 'e-icons e-copy',
tooltip: 'Copy to clipboard'
},
{
id: 'info',
label: 'Block Info',
tooltip: 'Show block information'
}
],
opening: (args: BlockActionMenuOpeningEventArgs) => {
// Customize menu before opening
},
itemSelect: (args: BlockActionItemSelectEventArgs) => {
// Handle action selection
}
};Properties
| Property | Type | Description |
|---|---|---|
enable | boolean | Enable/disable menu |
popupWidth | string | Menu width |
popupHeight | string | Menu height |
enableTooltip | boolean | Show tooltips on hover |
items | array | Custom action items |
opening | function | Fired before menu opens |
itemSelect | function | Fired on action selection |
Inline Toolbar
The Inline Toolbar appears when text is selected.
Built-in Items
- Text Styles: Bold, Italic, Underline, Strikethrough
- Colors: Text color, Background color
- Case: Uppercase, Lowercase
- Advanced: Superscript, Subscript, Inline Code, Link
Configuration
Use inlineToolbarSettings:
inlineToolbarSettings = {
enable: true,
popupWidth: 'auto',
items: [
'Transform',
'Bold',
'Italic',
'Underline',
'StrikeThrough',
'Color',
'Backgroundcolor',
'InlineCode',
'Link'
],
itemClick: (args: ToolbarItemClickEventArgs) => {
// Handle toolbar action
}
};Text Transform Options
The transformSettings property customizes block transformation options:
transformSettings = {
items: [
'Paragraph',
'Heading 1',
'Heading 2',
'Heading 3',
'Heading 4',
'BulletList',
'NumberedList',
'Checklist'
],
itemSelect: (args: TransformItemSelectEventArgs) => {
// Handle transform
}
};Color Settings
Customize font and background colors:
fontColorSettings = {
mode: 'Picker',
modeSwitcher: true,
colors: ['#FF0000', '#00FF00', '#0000FF']
};
backgroundColorSettings = {
mode: 'Palette',
colors: ['#FFFF00', '#00FFFF', '#FF00FF']
};Configuration Examples
Example 1: Minimal Toolbar
inlineToolbarSettings = {
enable: true,
items: ['Bold', 'Italic', 'Underline']
};Example 2: Full-Featured Toolbar
inlineToolbarSettings = {
enable: true,
popupWidth: 'auto',
items: [
'Transform',
'Bold',
'Italic',
'Underline',
'StrikeThrough',
'Color',
'Backgroundcolor',
'InlineCode',
'Link',
'Superscript',
'Subscript'
]
};Example 3: Complete Menu Setup
@Component({
imports: [BlockEditorModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
public commandMenuSettings = {
popupWidth: '350px',
commands: [
{ id: 'divider', type: BlockType.Divider, label: 'Line' }
]
};
public contextMenuSettings = {
enable: true,
items: [
{ id: 'copy', text: 'Copy', iconCss: 'e-icons e-copy' },
{ id: 'paste', text: 'Paste', iconCss: 'e-icons e-paste' }
]
};
public inlineToolbarSettings = {
enable: true,
items: ['Bold', 'Italic', 'Underline', 'Color', 'Link']
};
public blockActionMenuSettings = {
enable: true,
items: [
{ id: 'duplicate', label: 'Duplicate' },
{ id: 'delete', label: 'Delete' }
]
};
}Formatting and Inline Styles
Table of Contents
- Inline Content Types
- Text Formatting
- Typography Blocks
- Inline Code and Links
- Colors and Styling
- Practical Examples
Inline Content Types
The Block Editor supports four primary inline content types that can be mixed within any block:
1. Text Content
Plain or formatted text with support for inline styles.
{
contentType: ContentType.Text,
content: 'Your text here',
properties: {
styles: {
bold: true,
italic: false,
color: '#333'
}
}
}2. Link Content
Clickable hyperlinks with URL and optional title.
{
contentType: ContentType.Link,
content: 'Click here',
properties: {
url: 'https://example.com',
title: 'Link Title'
}
}3. Label Content
Tags for categorization and marking content.
{
contentType: ContentType.Label,
properties: {
labelId: 'high-priority'
}
}4. Mention Content
User references for tagging and notifications.
{
contentType: ContentType.Mention,
properties: {
userId: 'john-doe'
}
}Text Formatting
Inline styles can be applied to text content for rich formatting.
Supported Styles
| Style Property | Type | Description | Default |
|---|---|---|---|
bold | boolean | Make text bold | false |
italic | boolean | Italicize text | false |
underline | boolean | Underline text | false |
strikethrough | boolean | Strike through text | false |
color | string | Text color (HEX or RGBA) | '' |
backgroundColor | string | Background color | '' |
superscript | boolean | Render as superscript | false |
subscript | boolean | Render as subscript | false |
uppercase | boolean | Convert to uppercase | false |
lowercase | boolean | Convert to lowercase | false |
inlineCode | boolean | Render as inline code | false |
Examples
// Bold text
{
contentType: ContentType.Text,
content: 'Bold text',
properties: { styles: { bold: true } }
}
// Italic and underline
{
contentType: ContentType.Text,
content: 'Formatted text',
properties: {
styles: {
italic: true,
underline: true
}
}
}
// Colored text with background
{
contentType: ContentType.Text,
content: 'Highlighted',
properties: {
styles: {
color: '#ffffff',
backgroundColor: '#ff0000'
}
}
}
// Superscript
{
contentType: ContentType.Text,
content: 'x',
properties: { styles: { superscript: true } }
}
// Inline code
{
contentType: ContentType.Text,
content: 'const x = 10;',
properties: { styles: { inlineCode: true } }
}
// Multiple styles combined
{
contentType: ContentType.Text,
content: 'Important',
properties: {
styles: {
bold: true,
italic: true,
color: '#d32f2f',
backgroundColor: '#fff9c4'
}
}
}Typography Blocks
Typography blocks provide structural organization for text content.
Paragraph Block
The default block type for regular text.
{
blockType: 'Paragraph',
properties: {
placeholder: 'Write something or "/" for commands'
},
content: [
{
contentType: ContentType.Text,
content: 'This is a paragraph'
}
]
}Heading Blocks
Hierarchical headings from level 1 to 4.
// Heading Level 1 (Title)
{
blockType: 'Heading',
properties: {
level: 1,
placeholder: 'Heading 1'
},
content: [
{ contentType: ContentType.Text, content: 'Main Title' }
]
}
// Heading Level 2 (Section)
{
blockType: 'Heading',
properties: {
level: 2,
placeholder: 'Heading 2'
},
content: [
{ contentType: ContentType.Text, content: 'Section Title' }
]
}
// Heading Level 3
{
blockType: 'Heading',
properties: {
level: 3,
placeholder: 'Heading 3'
},
content: [...]
}
// Heading Level 4
{
blockType: 'Heading',
properties: {
level: 4,
placeholder: 'Heading 4'
},
content: [...]
}Divider Block
Horizontal separator line for visual separation.
{
blockType: 'Divider'
// No content needed
}Quote Block
Styled block for quotations with nested content.
{
blockType: 'Quote',
properties: {
children: [
{
parentId: 'quote-1',
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'This is a quotation'
}
]
}
]
}
}Callout Block
Highlighted block for important information, notes, or warnings.
{
blockType: 'Callout',
properties: {
children: [
{
parentId: 'callout-1',
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'Important information'
}
]
}
]
}
}Inline Code and Links
Inline Code
Display code inline with monospace formatting.
{
contentType: ContentType.Text,
content: 'const value = 42;',
properties: {
styles: { inlineCode: true }
}
}Inline Links
Create clickable hyperlinks within text.
{
contentType: ContentType.Link,
content: 'Visit Documentation',
properties: {
url: 'https://ej2.syncfusion.com/documentation'
}
}Link in Paragraph
Mixing text and links in a single paragraph:
{
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'For more info, see the '
},
{
contentType: ContentType.Link,
content: 'documentation',
properties: { url: 'https://docs.example.com' }
},
{
contentType: ContentType.Text,
content: '.'
}
]
}Colors and Styling
Text Color
Set the color of text using HEX or RGBA format.
// HEX color
{
contentType: ContentType.Text,
content: 'Red text',
properties: {
styles: { color: '#ff0000' }
}
}
// RGBA color
{
contentType: ContentType.Text,
content: 'Semi-transparent blue',
properties: {
styles: { color: 'rgba(0, 0, 255, 0.7)' }
}
}Background Color
Highlight text with background color.
{
contentType: ContentType.Text,
content: 'Highlighted text',
properties: {
styles: { backgroundColor: '#ffff00' }
}
}Custom CSS Classes
Apply custom styling to blocks using CSS classes.
{
blockType: 'Paragraph',
cssClass: 'important-note',
content: [
{
contentType: ContentType.Text,
content: 'Important note'
}
]
}With CSS:
.important-note {
border-left: 4px solid #d32f2f;
padding-left: 12px;
background-color: #ffebee;
font-weight: 500;
}Practical Examples
Example 1: Technical Documentation
const docBlock = {
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'To initialize, use: '
},
{
contentType: ContentType.Text,
content: 'new BlockEditor(options)',
properties: { styles: { inlineCode: true } }
},
{
contentType: ContentType.Text,
content: '. For details, see the '
},
{
contentType: ContentType.Link,
content: 'API documentation',
properties: { url: 'https://api.example.com' }
}
]
};Example 2: Emphasis and Hierarchy
const emphasizedBlocks = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [
{
contentType: ContentType.Text,
content: 'Important Update'
}
]
},
{
blockType: 'Callout',
properties: {
children: [
{
parentId: 'callout-1',
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'Critical change required',
properties: {
styles: {
bold: true,
color: '#d32f2f'
}
}
}
]
}
]
}
}
];Example 3: Mixed Formatting
{
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'This is ',
properties: { styles: { bold: true } }
},
{
contentType: ContentType.Text,
content: 'a mix of ',
properties: { styles: { italic: true } }
},
{
contentType: ContentType.Text,
content: 'different ',
properties: {
styles: {
bold: true,
italic: true,
color: '#1976d2'
}
}
},
{
contentType: ContentType.Text,
content: 'styles'
}
]
}Example 4: Code Documentation
[
{
blockType: 'Heading',
properties: { level: 2 },
content: [
{
contentType: ContentType.Text,
content: 'Usage Example'
}
]
},
{
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'Initialize with '
},
{
contentType: ContentType.Text,
content: 'new Editor()',
properties: { styles: { inlineCode: true } }
}
]
},
{
blockType: 'Code',
properties: { language: 'typescript' },
content: [
{
contentType: ContentType.Text,
content: 'const editor = new BlockEditor({\n element: "#container"\n});'
}
]
}
]Getting Started with Syncfusion Angular Block Editor
Table of Contents
- Installation
- Dependencies
- Angular Environment Setup
- Creating an Angular Application
- Installing the Package
- Adding CSS Reference
- Creating Your First Editor
- Running the Application
Installation
The Syncfusion Angular Block Editor component is distributed as npm packages under the @syncfusion scope. Start by installing the package for your Angular version.
Prerequisites
- Node.js and npm installed
- Angular CLI installed globally
- Familiarity with Angular project structure
Dependencies
The Block Editor requires the following dependencies:
@syncfusion/ej2-angular-blockeditor
├── @syncfusion/ej2-angular-base
├── @syncfusion/ej2-base
├── @syncfusion/ej2-popups
├── @syncfusion/ej2-buttons
├── @syncfusion/ej2-splitbuttons
├── @syncfusion/ej2-navigations
├── @syncfusion/ej2-dropdowns
└── @syncfusion/ej2-inputsThese are automatically installed as peer dependencies when you install the main package.
Angular Environment Setup
skills\documentations\syncfusion-angular-blockeditor\references
Step 1: Install Angular CLI
If you don't have the Angular CLI installed globally, run:
npm install -g @angular/cliStep 2: Create a New Angular Project
ng new my-block-editor-app
cd my-block-editor-appChoose your preferences for routing and stylesheet format when prompted.
Creating an Angular Application
Create a new Angular project or use an existing one:
ng new my-app
cd my-appInstalling the Package
The latest Syncfusion packages use Ivy distribution and are compatible with Angular 12 and newer.
npm install @syncfusion/ej2-angular-blockeditor --saveAdding CSS Reference
Import the required CSS files in your src/styles.css:
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-angular-blockeditor/styles/material3.css';You can also use alternative themes like tailwind3.css or bootstrap5.css depending on your preference.
Creating Your First Editor
Step 1: Import the Module
In your app.component.ts:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
@Component({
imports: [BlockEditorModule],
standalone: true,
selector: 'app-root',
template: `<div id="container">
<ejs-blockeditor />
</div>`
})
export class AppComponent { }Step 2: Configure in Template
Update your component template to include the Block Editor:
import { Component } from '@angular/core';
import { BlockEditorModule } from '@syncfusion/ej2-angular-blockeditor';
import { BlockModel, ContentType } from '@syncfusion/ej2-blockeditor';
@Component({
imports: [BlockEditorModule],
standalone: true,
selector: 'app-root',
template: `<div id="container">
<ejs-blockeditor [blocks]="blocksData" />
</div>`
})
export class AppComponent {
public blocksData: BlockModel[] = [
{
blockType: 'Heading',
properties: { level: 1 },
content: [
{
contentType: ContentType.Text,
content: 'Welcome to Block Editor'
}
]
},
{
blockType: 'Paragraph',
content: [
{
contentType: ContentType.Text,
content: 'Start editing your content here...'
}
]
}
];
}Running the Application
Start your Angular development server:
ng serveNavigate to http://localhost:4200/ in your browser. You should see the Block Editor component ready for use.
Verify Installation
- Check that the editor renders without errors
- Verify you can type in the editor
- Try using "/" to open the slash command menu
- Confirm CSS styles are applied correctly
If you see styling issues, double-check that all CSS imports are present in your styles.css file and that the paths are correct for your Syncfusion version.