
Syncfusion Angular Chat Ui
- 199 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-chat-ui for development tasks
About
syncfusion-angular-chat-ui: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-chat-ui
Syncfusion Angular Chat Ui by the numbers
- 199 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,000 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-chat-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 199 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-chat-ui for development tasks
Files
Syncfusion Angular Chat UI Component
Component Overview
The Syncfusion Angular Chat UI component provides a complete, feature-rich solution for building conversational interfaces in Angular applications. It enables real-time messaging, user presence indicators, file attachments, typing indicators, and seamless integration with bot frameworks and AI services.
Key Capabilities:
- Message Management - Configure messages with text, rich templates, media, replies, pinning, and forwarding
- User System - Define current user, presence status, avatars with custom styling and mentions
- Appearance Control - Customize width, height, placeholder, CSS classes, compact mode, and suggestions
- Header & Footer - Control visibility, titles, icons, custom templates, and header toolbar with actions
- Events & Interactions - Handle message send, typing indicators, toolbar actions (copy, reply, pin, delete)
- Templates - Customize empty chat, messages, time breaks, typing indicators, and suggestion items
- File Attachments - Enable uploads with type/size restrictions, drag-and-drop, custom paths, attachment click events
- Methods - Programmatically add/update messages, scroll to bottom, scroll to specific message, focus input
- Globalization - Support multiple languages (i18n), RTL text direction, and locale-based formatting
- Advanced Features - Load on demand, state persistence, mentions, message status, time breaks, bot integrations
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and package setup (Ivy vs ngcc)
- Setup Angular environment with Angular CLI
- Basic component initialization
- Configuring messages and users
- CSS imports and theme configuration
- Running the application
Messages and Users
📄 Read: references/messages-and-users.md
- Message configuration and properties (text, id, author, timestamp)
- User models and avatars
- Defining current user with unique identifier
- Avatar URLs and custom background colors
- User presence status (online, offline, away, busy)
- Message pinning for important messages
- Enhanced MessageReplyModel with complete interface documentation
- Reply with attachments and timestamp formatting
- Dynamic reply creation patterns
- Message forwarding
- Compact mode for group conversations
- Markdown message support
Appearance and Layout
📄 Read: references/appearance-and-layout.md
- Placeholder text customization
- Width and height properties
- CSS class customization for styling
- Compact mode configuration
- Auto-scroll to bottom behavior
- Suggestions display for quick replies
Header and Footer
📄 Read: references/header-and-footer.md
- Header visibility control
- Header text (title) configuration
- Header icon customization with CSS classes
- Header toolbar with custom actions (call, video, settings, profile)
- ToolbarSettingsModel configuration with items and itemClicked event
- Footer visibility and control
- Footer template for custom layouts
Events and Interactions
📄 Read: references/events-and-interactions.md
- Component lifecycle events (created)
- Message send event handling
- User typing event and typing indicators
- Message toolbar configuration
- Complete ToolbarItemModel properties (align, cssClass, disabled, iconCss, tabIndex, template, text, tooltip, type, visible)
- Toolbar items customization (copy, reply, pin, delete, forward)
- Item click event handling
- Toolbar width configuration
- Before/after attachment events
- Header toolbar vs message toolbar differences
Templates and Content
📄 Read: references/templates-and-content.md
- Message template customization
- Empty chat template for initial state
- Time break template for date separators
- Message status and delivery icons
- Timestamp display and visibility
- Timestamp format customization (dd/MM/yyyy hh:mm a)
- Suggestion template customization with context variables (index, suggestion)
- Advanced suggestion examples (category-based, search-highlighted, icon-based)
- Typing indicator template
Attachments and File Handling
📄 Read: references/attachments-and-file-handling.md
- Enable file attachment support
- Attachment settings configuration
- Server endpoints (saveUrl, removeUrl)
- File type restrictions and filters
- File size limits (default 30MB)
- SaveFormat enum (Blob vs Base64) with advantages and use cases
- Custom storage paths with path property for CDN/cloud storage
- Drag-and-drop file upload
- Maximum file count restrictions
- File preview templates
- Attachment templates
- attachmentClick event for custom file interactions (preview, download, metadata)
- attachedFile property for pre-populating messages with files
- Upload event handling (success, failure)
Methods and Programmatic Control
📄 Read: references/methods-and-programmatic-control.md
- addMessage() - Add new messages programmatically
- updateMessage() - Edit existing messages
- scrollToBottom() - Scroll to latest messages
- scrollToMessage(messageId) - Navigate to specific message (pinned messages, search results, deep linking)
- focus() - Set focus to chat input programmatically (auto-focus, modal close, command execution)
- ViewChild component access
- Accessing chat instance for direct control
Globalization and Localization
📄 Read: references/globalization-and-localization.md
- Localization (L10n) and i18n support
- Typing indicator translations
- Multiple language support
- Right-to-Left (RTL) layout for Arabic, Hebrew, Persian
- Locale configuration
- Language-specific string customization
Advanced Features
📄 Read: references/advanced-features.md
- State persistence with enablePersistence (localStorage, cross-tab sync, browser refresh protection)
- Custom persistence implementations for sensitive data
- Load on demand for large message histories (1000+ messages)
- Mention integration with @character
- Trigger character customization
- Predefined mentions in messages
- mentionSelect event handling
- Message status tracking (sent, delivered, read)
- Time breaks between messages for date organization
Bot Integrations
📄 Read: references/bot-integrations.md
- Google Dialogflow integration for AI conversations
- Microsoft Bot Framework integration with Azure
- Direct Line API configuration
- Token server setup for security
- Backend API configuration
- Secure credential handling
- Bot response message handling
- Session management
---
Quick Start Example
Here's a minimal example to get started:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
headerText="TeamSync Professionals">
<e-messages>
<e-message text="Hi, how are you?" [author]="currentUserModel"></e-message>
<e-message text="Great! How can I help?" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`,
styles: [`
#chatui {
height: 500px;
width: 100%;
}
`]
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
}Installation:
npm install @syncfusion/ej2-angular-interactive-chatCSS Import (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-navigations/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-dropdowns/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-angular-interactive-chat/styles/material3.css';---
Key Components and Props
Core Component:
<ejs-chatui>- Main chat container[user]- Current user model (UserModel)[messages]- Message collectionheaderText- Header title[headerToolbar]- Header toolbar with custom actions[height]/[width]- Dimensions[enableAttachments]- File upload support[enablePersistence]- State persistence across sessions[enableRtl]- Right-to-left layout[attachmentSettings]- File upload configuration[messageToolbarSettings]- Message-level toolbar[suggestions]- Quick reply suggestions[suggestionTemplate]- Custom suggestion rendering
Message Element:
<e-message>- Individual messagetext- Message content[author]- User who sent message[timeStamp]- Message timestamp[timeStampFormat]- Custom timestamp format[isPinned]- Pin importance[replyTo]- Thread context (MessageReplyModel)[isForwarded]- Forwarded indicator[attachedFile]- Pre-populated file attachments (FileInfo)[status]- Message delivery status (MessageStatusModel)[mentionUsers]- Users mentioned in message
User Model Properties:
id- Unique user identifieruser- Display nameavatarUrl- Avatar imageavatarBgColor- Avatar background colorstatusIconCss- Presence status icon
---
Common Use Cases
1. Customer Support Chat
- Configure header with support team name
- Enable file attachments for screenshots/documents
- Use message status for delivery tracking
- Integrate with Dialogflow for AI-powered responses
2. Team Messaging App
- Multiple users with presence status
- Mention teammates with @mentions
- Pin important decisions/announcements
- Use timestamp format for timezone support
3. AI Assistant
- Integrate with Microsoft Bot Framework or Dialogflow
- Show typing indicator while bot responds
- Suggest quick replies with suggestions array
- Handle bot-specific message formatting
4. Load Performance
- Enable load on demand for 1000+ message conversations
- Implement auto-scroll for smooth scrolling
- Use compact mode to reduce vertical space
- Lazy load attachments and media
5. Internationalization (i18n)
- Provide localized typing indicators ("X is typing")
- Enable RTL for Arabic/Hebrew/Persian users
- Customize date/time format per locale
- Translate button labels and placeholders
---
Advanced Features
Table of Contents
State Persistence
Preserve Chat State Across Sessions
Enable enablePersistence to automatically save and restore chat state (messages, typing indicators, scroll position) across browser sessions:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enablePersistence]="true">
<e-messages>
<e-message text="How are you?" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
}What Gets Persisted:
- Component state and configuration
- Scroll position
- User preferences
Use Cases:
1. Single Page Applications (SPAs):
// Preserve state during route navigation
export class ChatPageComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public enablePersistence: boolean = true; // Maintains state when navigating away
}2. Browser Refresh Protection:
// State survives page reload
@Component({
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enablePersistence]="true">
<!-- Messages and scroll position restored after F5 refresh -->
</div>
`
})3. Multi-tab Support:
// Sync state across multiple browser tabs
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public enablePersistence: boolean = true; // Uses localStorage for cross-tab sync
}Storage Mechanism:
The component uses browser localStorage with a unique key based on the component's ID:
// Custom ID for isolated persistence
template: `
<div id="support-chat" ejs-chatui
[user]="supportUser"
[enablePersistence]="true">
<!-- Stored as 'support-chat' in localStorage -->
</div>
<div id="team-chat" ejs-chatui
[user]="teamUser"
[enablePersistence]="true">
<!-- Stored as 'team-chat' in localStorage (separate) -->
</div>
`Important Notes:
- Default:
false(persistence disabled) - Storage: Uses browser
localStorage(not suitable for sensitive data) - Capacity: Limited by browser localStorage limits (~5-10MB)
- Security: Do not persist sensitive messages - use server-side storage instead
- Clearing: Users can clear localStorage to reset persisted state
Selective Persistence (Custom Implementation):
For more control, implement custom state management:
import { Component, ViewChild, OnDestroy } from '@angular/core';
import { ChatUIComponent } from '@syncfusion/ej2-angular-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div #chatui_instance id="chatui" ejs-chatui
[user]="currentUserModel"
[enablePersistence]="false"
[messages]="messages">
</div>
`
})
export class AppComponent implements OnDestroy {
@ViewChild('chatui_instance', { static: false })
public chatUIInstance!: ChatUIComponent;
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public messages: MessageModel[] = [];
// Custom persistence logic
saveState() {
const state = {
messages: this.messages,
timestamp: new Date().toISOString()
};
sessionStorage.setItem('chat-state', JSON.stringify(state));
}
loadState() {
const saved = sessionStorage.getItem('chat-state');
if (saved) {
const state = JSON.parse(saved);
this.messages = state.messages;
}
}
ngOnDestroy() {
this.saveState(); // Save before component unmounts
}
}When NOT to Use enablePersistence:
- ❌ Chat contains sensitive/private information
- ❌ Messages should be loaded fresh from server each time
- ❌ You need server-side message history synchronization
- ❌ Compliance requirements prohibit client-side storage
Best Practice: For production applications with message history, use server-side storage and load messages via API. Use enablePersistence only for non-sensitive UI state like scroll position and preferences.
Load on Demand
Improve Performance for Large Conversations
Use load-on-demand for chats with 1000+ messages to optimize performance:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat';
import { Component, OnInit } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[loadOnDemand]="true"
[messages]="messages">
<e-messages>
<!-- Messages loaded on scroll -->
</e-messages>
</div>
`
})
export class AppComponent implements OnInit {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public messages: MessageModel[] = [];
public ngOnInit(): void {
// Load initial batch of recent messages
for (let i = 1; i <= 50; i++) {
this.messages.push({
text: `Message ${i}`,
author: i % 2 === 0 ? this.michaleUserModel : this.currentUserModel
});
}
}
// Load more messages when user scrolls up
public loadMoreMessages = () => {
for (let i = this.messages.length + 1; i <= this.messages.length + 50; i++) {
this.messages.unshift({
text: `Message ${i}`,
author: i % 2 === 0 ? this.michaleUserModel : this.currentUserModel
});
}
};
}Behavior:
- Only initial messages render in DOM
- When user scrolls to top, more messages load automatically
- Reduces memory usage and improves initial render time
Mention Integration
Configure Mention Users
Enable users to mention teammates with @mentions:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[mentionUsers]="mentionUsers">
<e-messages>
<e-message
text="Want to get coffee tomorrow?"
[author]="currentUserModel">
</e-message>
<e-message
text="Sure! What time?"
[author]="michaleUserModel">
</e-message>
<e-message
text="@Michale How about 10 AM?"
[author]="currentUserModel"
[mentionUsers]="[michaleUserModel]">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public reeναUserModel: UserModel = { user: 'Reena', id: 'user3' };
public mentionUsers: UserModel[] = [
this.michaleUserModel,
this.reeναUserModel
];
}Customize Mention Trigger Character
Change the mention character from @ to something else:
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[mentionUsers]="mentionUsers"
mentionTriggerChar="/">
<!-- Use / instead of @ to mention -->
</div>
`
// Example: "/Michale How about 10 AM?" instead of "@Michale"Predefine Mentions in Messages
Use placeholders to map mentions to users:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[mentionUsers]="mentionUsers">
<e-messages>
<!-- Placeholder {0} maps to first user in mentionUsers array -->
<e-message
text="Hi {0}, are we on track for the deadline?"
[author]="currentUserModel"
[mentionUsers]="[michaleUserModel]">
</e-message>
<!-- Placeholder {1} maps to second user -->
<e-message
text="Yes {0}, the design phase is complete."
[author]="michaleUserModel"
[mentionUsers]="[currentUserModel]">
</e-message>
<e-message
text="I'll review it and send feedback by today."
[author]="currentUserModel">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public mentionUsers: UserModel[] = [];
}Handle Mention Selection
Use mentionSelect event when user selects a mention:
import { MentionSelectEventArgs } from '@syncfusion/ej2-angular-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[mentionUsers]="mentionUsers"
(mentionSelect)="onMentionSelect($event)">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public mentionUsers: UserModel[] = [this.michaleUserModel];
public onMentionSelect = (args: MentionSelectEventArgs) => {
console.log('User mentioned:', args.mentionUser);
// Send notification to mentioned user
this.notifyUser(args.mentionUser);
};
private notifyUser = (user: UserModel) => {
console.log(`Notifying ${user.user} of mention`);
};
}Message Status Tracking
Delivery Status Indicators
Track message delivery states:
interface MessageStatus {
'sent'; // Message sent to server
'delivered'; // Message received by recipient
'read'; // Message read by recipient
'failed'; // Message delivery failed
}Display Message Status
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui [user]="currentUserModel">
<e-messages>
<e-message
text="Message 1: Sending..."
[author]="currentUserModel"
[status]="{ iconCss: 'e-icon-clock', text: 'Sending' }">
</e-message>
<e-message
text="Message 2: Sent"
[author]="currentUserModel"
[status]="{ iconCss: 'e-icon-check', text: 'Sent' }">
</e-message>
<e-message
text="Message 3: Delivered"
[author]="currentUserModel"
[status]="{ iconCss: 'e-icon-check-double', text: 'Delivered' }">
</e-message>
<e-message
text="Message 4: Read"
[author]="currentUserModel"
[status]="{ iconCss: 'e-icon-check-double', text: 'Read', tooltip: 'Read at 3:45 PM' }">
</e-message>
<e-message
text="Message 5: Failed"
[author]="currentUserModel"
[status]="{ iconCss: 'e-icon-close', text: 'Failed' }">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
}Update Status on Delivery
public async sendMessage(text: string) {
const messageId = this.generateId();
// Add message with "sending" status
const message: MessageModel = {
messageId,
text,
author: this.currentUserModel,
status: { iconCss: 'e-icon-clock', text: 'Sending' }
};
this.chatUIInstance.addMessage(message);
try {
// Send to server
const response = await fetch('/api/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text })
});
if (response.ok) {
// Update to "sent" status
this.chatUIInstance.updateMessage(messageId, {
status: { iconCss: 'e-icon-check', text: 'Sent' }
});
// Simulate delivery after 1 second
setTimeout(() => {
this.chatUIInstance.updateMessage(messageId, {
status: { iconCss: 'e-icon-check-double', text: 'Delivered' }
});
}, 1000);
}
} catch (error) {
// Update to "failed" status
this.chatUIInstance.updateMessage(messageId, {
status: { iconCss: 'e-icon-close', text: 'Failed' }
});
}
}
private generateId = (): string => {
return 'msg_' + Date.now() + '_' + Math.random();
};Time Breaks
Display Date Separators
Use time breaks to organize messages by date:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[showTimeBreak]="true"
headerText="TeamSync Professionals">
<e-messages>
<!-- Messages from today -->
<e-message
text="How are you?"
[author]="currentUserModel"
[timeStamp]="today">
</e-message>
<e-message
text="Good! How are you?"
[author]="michaleUserModel"
[timeStamp]="today">
</e-message>
<!-- Time break appears automatically for different date -->
<!-- Messages from yesterday -->
<e-message
text="Let's meet tomorrow"
[author]="currentUserModel"
[timeStamp]="yesterday">
</e-message>
<e-message
text="Sure! See you then."
[author]="michaleUserModel"
[timeStamp]="yesterday">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public today = new Date();
public yesterday = new Date(new Date().setDate(new Date().getDate() - 1));
}Behavior:
- Automatically inserts separator when message date changes
- Shows "Today", "Yesterday", or specific date
- Improves conversation readability
Customize Time Break Template
Refer to Templates and Content for custom time break templates.
Best Practices
1. Mention Performance
// ✅ Good - limit mention suggestions
mentionUsers: UserModel[] = this.teamMembers.slice(0, 20);
// ❌ Bad - too many suggestions
mentionUsers: UserModel[] = allCompanyUsers; // 10,000+ users2. Load on Demand Strategy
// ✅ Good - load older messages intelligently
loadMoreMessages = () => {
// Fetch 50 messages before current first message
// Prepend to array
// Continue chat
};
// ❌ Bad - load all messages at once
ngOnInit = () => {
this.loadAllMessages(); // Huge performance hit
};3. Status Updates
// ✅ Good - update status asynchronously
this.updateMessageStatus(messageId, 'sent');
setTimeout(() => {
this.updateMessageStatus(messageId, 'delivered');
}, 2000);
// ❌ Bad - rapid synchronous updates
this.updateMessageStatus(messageId, 'sent');
this.updateMessageStatus(messageId, 'delivered');
this.updateMessageStatus(messageId, 'read');---
Appearance and Layout Configuration
Table of Contents
- Placeholder Customization
- Width and Height Properties
- CSS Class Customization
- Compact Mode
- Auto-Scroll Configuration
- Suggestions Display
Placeholder Customization
Default Placeholder
The input field shows a default placeholder "Type your message…". Customize it with the placeholder property:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[placeholder]="placeholder">
<e-messages>
<e-message text="Hi Michale, are we on track for the deadline?" [author]="currentUserModel"></e-message>
<e-message text="Yes, the design phase is complete." [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public placeholder: string = 'Start typing...';
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
}Common Placeholder Examples
// Support chat
placeholder: 'Describe your issue...'
// Team messaging
placeholder: '@mention someone or type your message'
// AI assistant
placeholder: 'Ask me anything...'
// Customer service
placeholder: 'How can we help? Type your message here.'Width and Height Properties
Setting Width
Control chat component width with the width property:
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
width="450px">
<!-- Messages -->
</div>
`
// Or as percentage
width="100%"
width="80%"Setting Height
Control chat component height with the height property:
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
height="380px">
<!-- Messages -->
</div>
`
// Or with viewport units
height="80vh"
height="100%"Responsive Layout
Combine width/height with CSS media queries:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
width="100%"
height="500px">
<!-- Messages -->
</div>
`,
styles: [`
@media (max-width: 768px) {
#chatui {
height: 300px !important;
}
}
`]
})
export class AppComponent { }CSS Class Customization
Apply Custom CSS Class
Use the cssClass property to apply custom styling:
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
cssClass="custom-container">
<!-- Messages -->
</div>
`,
styles: [`
.custom-container {
border: 2px solid #007bff;
border-radius: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.custom-container .e-input-group {
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
`]Multiple CSS Classes
Combine multiple classes with spaces:
cssClass="custom-container dark-theme elevated"Predefined CSS Selectors
Style chat elements with Syncfusion classes:
/* Message area */
.e-chat-messages { }
/* Message bubble */
.e-message { }
/* Current user message (right side) */
.e-message.e-message-right { }
/* Other user message (left side) */
.e-message.e-message-left { }
/* Input field */
.e-input-group { }
/* Send button */
.e-btn-primary { }
/* Avatar */
.e-avatar { }
/* Header */
.e-header { }
/* Footer */
.e-footer { }Custom Styling Example
styles: [`
/* Dark theme */
.dark-theme {
background: #1e1e1e;
color: #fff;
}
.dark-theme .e-message {
background: #333;
color: #fff;
}
.dark-theme .e-input-group {
background: #2d2d2d;
border: 1px solid #444;
}
/* Elevated shadow effect */
.elevated {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}
/* Rounded corners */
.elevated {
border-radius: 12px;
overflow: hidden;
}
`]Compact Mode
Enable Compact Mode
Use enableCompactMode to align all messages to the left:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableCompactMode]="true"
headerText="TeamSync Professionals">
<e-messages>
<e-message text="How are you?" [author]="currentUserModel"></e-message>
<e-message text="Good! How are you?" [author]="michaleUserModel"></e-message>
<e-message text="I'm doing well, Thanks for asking!" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
}Default: false - Messages alternate left/right based on sender When true: All messages left-aligned (compact layout)
Use Cases for Compact Mode
- Group conversations - Multiple speakers, left-aligned reads better
- Mobile devices - Space-constrained interfaces benefit from single-column layout
- Documentation chats - Uniform column width improves readability
- Accessibility - Simplified layout reduces cognitive load
Auto-Scroll Configuration
Enable Auto-Scroll
Use autoScrollToBottom to automatically scroll when new messages arrive:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[autoScrollToBottom]="true"
headerText="TeamSync Professionals">
<e-messages>
<e-message text="How are you?" [author]="currentUserModel"></e-message>
<e-message text="Good! How are you?" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
}Behavior:
- Default (false) - Manual scrolling required, FAB (Floating Action Button) appears
- When true - Auto-scrolls to bottom for sent messages and when scroll is at bottom
Scrolling Behavior Explained
Auto-Scroll Active (true):
✓ User sends message → Auto-scrolls to bottom
✓ Other user sends message while scroll is at bottom → Auto-scrolls
✗ User scrolls up to view history → Stops auto-scrolling (manual control)
✓ User scrolls back to bottom → Resumes auto-scrollSuggestions Display
Add Quick Reply Suggestions
Use the suggestions property to show quick-reply buttons:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[suggestions]="suggestions"
headerText="TeamSync Professionals">
<e-messages>
<e-message text="How are you?" [author]="currentUserModel"></e-message>
<e-message text="Good! How are you?" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public suggestions: any[] = [
{ text: 'Thanks' },
{ text: 'Welcome' },
{ text: 'No problem' },
{ text: 'Perfect!' }
];
}Use Cases for Suggestions
// Customer support
suggestions: [
{ text: 'Yes, that helped' },
{ text: 'No, still need help' },
{ text: 'Can I escalate?' }
]
// Feedback collection
suggestions: [
{ text: 'Very satisfied' },
{ text: 'Satisfied' },
{ text: 'Neutral' },
{ text: 'Unsatisfied' }
]
// Quick actions
suggestions: [
{ text: 'Show billing' },
{ text: 'Track order' },
{ text: 'Contact support' }
]Layout Combinations
Example: Mobile-Optimized Chat
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
width="100%"
height="75vh"
[enableCompactMode]="true"
[autoScrollToBottom]="true"
[placeholder]="'Type message...'"
cssClass="mobile-chat">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`,
styles: [`
.mobile-chat {
max-width: 600px;
margin: 0 auto;
border-radius: 12px;
overflow: hidden;
}
`]
})
export class AppComponent { }Example: Desktop Full-Width Chat
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
width="100%"
height="100vh"
[enableCompactMode]="false"
[autoScrollToBottom]="false"
[suggestions]="suggestions"
cssClass="desktop-chat">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`,
styles: [`
.desktop-chat {
height: calc(100vh - 60px);
}
`]
})
export class AppComponent { }---
Attachments and File Handling
Table of Contents
- Enable File Attachments
- Attachment Settings
- File Type Restrictions
- File Size Configuration
- Save Format Options
- Drag and Drop
- Upload Events
- Preview and Attachment Templates
Enable File Attachments
Basic File Attachment Setup
Enable file attachment support:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, FileAttachmentSettingsModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
<e-messages>
<e-message text="Welcome to Chat!" [author]="botUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public botUserModel: UserModel = { user: 'Bot', id: 'bot' };
public enableAttachments: boolean = true;
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove'
};
}Key Requirements:
enableAttachments: true- Enables file pickersaveUrl- Server endpoint to handle file uploadsremoveUrl- Server endpoint to handle file deletion
Attachment Settings
Core Settings Configuration
public attachmentSettings: FileAttachmentSettingsModel = {
// Upload endpoints
saveUrl: 'https://your-domain.com/api/files/upload',
removeUrl: 'https://your-domain.com/api/files/delete',
// File restrictions
allowedFileTypes: '.pdf,.doc,.docx,.xls,.xlsx,.jpg,.png,.gif',
// Size limits
maxFileSize: 5 * 1024 * 1024, // 5MB
maximumCount: 5, // Max 5 files per message
// Upload format
saveFormat: 'Base64' // or 'Blob'
};File Type Restrictions
Allowed File Types
Restrict uploads to specific file extensions:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
// Allow documents only
allowedFileTypes: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
};
// Images only
allowedFileTypes: '.jpg,.jpeg,.png,.gif,.bmp,.webp'
// Audio files
allowedFileTypes: '.mp3,.wav,.flac,.m4a'
// Videos
allowedFileTypes: '.mp4,.webm,.mov,.avi'
// All files (no restriction)
allowedFileTypes: ''MIME Type Restrictions
Use MIME types for more specific control:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
// MIME types (browser-level, not enforced server-side)
allowedFileTypes: 'image/jpeg,image/png,application/pdf'
};File Size Configuration
Maximum File Size
Set maximum file size in bytes:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
// 5MB limit
maxFileSize: 5 * 1024 * 1024,
// 10MB limit
maxFileSize: 10 * 1024 * 1024,
// 1MB limit
maxFileSize: 1024 * 1024
};Default: 30MB (30,000,000 bytes)
File Size Validation Example
public onBeforeAttachmentUpload = (args: UploadingEventArgs) => {
const file = args.filesData[0];
// Check file size
if (file.size > 5 * 1024 * 1024) {
args.cancel = true;
alert('File size exceeds 5MB limit');
}
// Check file type
const allowedTypes = ['pdf', 'doc', 'docx'];
const extension = file.name.split('.').pop()?.toLowerCase();
if (!allowedTypes.includes(extension!)) {
args.cancel = true;
alert('File type not allowed');
}
};Save Format Options
SaveFormat Enum
The saveFormat property accepts two enum values that determine how file data is serialized:
import { SaveFormat } from '@syncfusion/ej2-interactive-chat';
enum SaveFormat {
Blob = 'Blob', // Binary file format (default)
Base64 = 'Base64' // Base64-encoded string format
}Blob Format (Default)
Default format for efficient binary handling:
import { SaveFormat } from '@syncfusion/ej2-interactive-chat';
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
saveFormat: SaveFormat.Blob // or simply 'Blob'
};Advantages:
- ✅ Efficient for large files (no Base64 overhead)
- ✅ Native browser support for FormData uploads
- ✅ Better performance and memory usage
- ✅ Suitable for streaming and chunked uploads
Use cases:
- Direct binary upload to cloud storage (AWS S3, Azure Blob)
- File previews with
URL.createObjectURL() - Local storage with IndexedDB
- RESTful API multipart/form-data uploads
Base64 Format
Encode files as Base64 strings:
import { SaveFormat } from '@syncfusion/ej2-interactive-chat';
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
saveFormat: SaveFormat.Base64 // or simply 'Base64'
};Advantages:
- ✅ Embeddable in JSON payloads
- ✅ Easy to store in relational databases (TEXT column)
- ✅ Direct display in <img> src attribute
- ✅ Compatible with older APIs expecting string data
Disadvantages:
- ⚠️ File size increases by ~33% due to encoding
- ⚠️ Higher memory consumption
- ⚠️ Slower for large files
Use cases:
- Embedding images in JSON API responses
- Storing small files in SQL databases
- Displaying images without server storage:
<img src="data:image/png;base64,..."> - GraphQL APIs (typically use Base64 for binary data)
Choosing Between Blob and Base64:
// ✅ Use Blob for:
// - Files > 1MB
// - Production file upload systems
// - Cloud storage integration
// - Performance-critical applications
saveFormat: 'Blob'
// ✅ Use Base64 for:
// - Small images (< 100KB, like avatars)
// - JSON-only APIs
// - Database-embedded files
// - Backward compatibility
saveFormat: 'Base64'Example: Base64 for Small Avatars, Blob for Documents
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings"
(beforeAttachmentUpload)="onBeforeUpload($event)">
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
saveFormat: 'Blob', // Default to Blob
maxFileSize: 10485760 // 10MB
};
// Dynamic format selection based on file type/size
onBeforeUpload(args: any) {
const file = args.filesData[0];
const isSmallImage = file.type.startsWith('image/') && file.size < 102400; // < 100KB
if (isSmallImage) {
// Use Base64 for small images (avatars, icons)
this.attachmentSettings.saveFormat = SaveFormat.Base64;
} else {
// Use Blob for documents and large files
this.attachmentSettings.saveFormat = SaveFormat.Blob;
}
}
}Drag and Drop
Enable Drag-and-Drop
Allow users to drag files to the chat:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
<e-messages>
<!-- Drag files here to upload -->
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
enableDragAndDrop: true // Enable drag-and-drop
};
}Default: true - Drag-and-drop enabled by default
Attachment Click Event
Handle Attachment Clicks
Use the attachmentClick event to handle clicks on attachment items:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, FileAttachmentSettingsModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
<e-messages>
<e-message
text="Check out this document"
[author]="michaleUserModel"
[attachedFile]="documentFile">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
attachmentClick: this.onAttachmentClick
};
public documentFile: any = {
name: 'project-plan.pdf',
size: 2048000,
type: 'application/pdf',
url: 'https://example.com/files/project-plan.pdf'
};
private onAttachmentClick = (args: any) => {
const { file, isBeforeSend } = args;
console.log('Attachment clicked:', file.name);
console.log('Before send:', isBeforeSend);
if (isBeforeSend) {
// Attachment clicked before sending (in footer preview)
this.showAttachmentPreview(file);
} else {
// Attachment clicked in sent message
this.downloadOrPreviewAttachment(file);
}
};
private showAttachmentPreview = (file: any) => {
// Show preview modal for attachment about to be sent
console.log('Showing preview for:', file.name);
// Implement preview logic
};
private downloadOrPreviewAttachment = (file: any) => {
// Download or preview sent attachment
if (file.type.includes('image')) {
this.previewImage(file);
} else if (file.type.includes('pdf')) {
this.previewPDF(file);
} else {
this.downloadFile(file);
}
};
private previewImage = (file: any) => {
window.open(file.url, '_blank');
};
private previewPDF = (file: any) => {
window.open(file.url, '_blank');
};
private downloadFile = (file: any) => {
const link = document.createElement('a');
link.href = file.url;
link.download = file.name;
link.click();
};
}Attachment Click Use Cases
1. Image Preview Modal
private onAttachmentClick = (args: any) => {
const { file } = args;
if (file.type.startsWith('image/')) {
this.openImagePreviewModal(file);
}
};
private openImagePreviewModal = (file: any) => {
this.selectedImage = file.url;
this.showImageModal = true;
};2. Download with Progress Tracking
private onAttachmentClick = async (args: any) => {
const { file } = args;
this.downloadProgress = 0;
this.isDownloading = true;
try {
await this.downloadWithProgress(file.url, file.name);
} catch (error) {
console.error('Download failed:', error);
alert('Download failed');
} finally {
this.isDownloading = false;
}
};3. File Metadata Display
private onAttachmentClick = (args: any) => {
const { file } = args;
this.showFileMetadata({
name: file.name,
size: this.formatFileSize(file.size),
type: file.type,
uploadedBy: file.uploadedBy,
uploadDate: file.uploadDate
});
};Pre-Populated Message Attachments
Display Messages with Existing Attachments
Use the attachedFile property to show messages with pre-existing file attachments:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
<e-messages>
<e-message
text="Here's the project proposal"
[author]="michaleUserModel"
[attachedFile]="proposalFile">
</e-message>
<e-message
text="And here are the design mockups"
[author]="michaleUserModel"
[attachedFile]="mockupFiles">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public attachmentSettings: any = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove'
};
// Single file attachment
public proposalFile: any = {
name: 'project-proposal.pdf',
size: 2048000,
type: 'application/pdf',
url: 'https://example.com/files/proposal.pdf'
};
// Multiple file attachments
public mockupFiles: any[] = [
{
name: 'homepage-mockup.png',
size: 1024000,
type: 'image/png',
url: 'https://example.com/files/homepage.png'
},
{
name: 'dashboard-mockup.png',
size: 1536000,
type: 'image/png',
url: 'https://example.com/files/dashboard.png'
}
];
}Load Conversation History with Attachments
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChatUIComponent } from '@syncfusion/ej2-angular-interactive-chat';
import { MessageModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
#chatui_instance
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
</div>
`
})
export class AppComponent implements OnInit {
@ViewChild('chatui_instance', { static: false })
public chatUIInstance!: ChatUIComponent;
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public attachmentSettings: any = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove'
};
async ngOnInit() {
// Load message history from server
await this.loadMessageHistory();
}
private loadMessageHistory = async () => {
try {
const response = await fetch('https://your-api.com/messages/history');
const historyData = await response.json();
// Add each message with attachments
historyData.messages.forEach((msg: any) => {
const message: MessageModel = {
text: msg.text,
author: msg.author,
timeStamp: new Date(msg.timestamp),
attachedFile: msg.attachments // Include file attachments
};
this.chatUIInstance.addMessage(message);
});
} catch (error) {
console.error('Failed to load history:', error);
}
};
}FileInfo Interface Structure
interface FileInfo {
name: string; // File name
size: number; // File size in bytes
type: string; // MIME type (e.g., 'image/png', 'application/pdf')
url?: string; // URL to access the file
rawFile?: File; // Raw File object (for uploads)
statusCode?: number; // Upload status code
}Example with complete FileInfo:
public attachedFile: FileInfo = {
name: 'annual-report.pdf',
size: 5242880, // 5MB in bytes
type: 'application/pdf',
url: 'https://cdn.example.com/files/annual-report.pdf',
statusCode: 200
};Path Property Configuration
Configure Custom Storage Path
Use the path property to specify a custom storage path for attachments:
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
path: 'https://cdn.example.com/chat-attachments/' // Custom CDN path
};Priority: If both path and saveFormat are configured, the path property takes priority.
Use Cases for Path Property
1. CDN Storage
// Store and serve files from CDN
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
path: 'https://cdn.yourdomain.com/uploads/'
};2. User-Specific Paths
// Organize files by user
const userId = this.currentUserModel.id;
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
path: `https://storage.example.com/users/${userId}/attachments/`
};3. Date-Based Organization
// Organize by date
const today = new Date();
const datePath = `${today.getFullYear()}/${today.getMonth() + 1}`;
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
path: `https://storage.example.com/chat/${datePath}/`
};Upload Events
Before Upload Event
Fired before file upload begins:
import { UploadingEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings"
(beforeAttachmentUpload)="onBeforeAttachmentUpload($event)">
<e-messages></e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove'
};
public onBeforeAttachmentUpload = (args: UploadingEventArgs) => {
const file = args.filesData[0];
console.log('Uploading:', file.name);
// Validate file
if (!this.isValidFile(file)) {
args.cancel = true;
alert('Invalid file');
}
};
private isValidFile = (file: any): boolean => {
// Custom validation logic
return file.size < 5 * 1024 * 1024;
};
}Upload Success Event
Fired when upload completes successfully:
import { SuccessEventArgs } from '@syncfusion/ej2-angular-inputs';
public onAttachmentUploadSuccess = (args: SuccessEventArgs) => {
console.log('Upload successful:', args);
// Handle successful upload
this.showNotification('File uploaded successfully');
};Upload Failure Event
Fired when upload fails:
import { FailureEventArgs } from '@syncfusion/ej2-angular-inputs';
public onAttachmentUploadFailure = (args: FailureEventArgs) => {
console.error('Upload failed:', args.error);
this.showNotification('Upload failed: ' + args.error);
};Attachment Removed Event
Fired when user removes an attachment:
import { RemovingEventArgs } from '@syncfusion/ej2-angular-inputs';
public onAttachmentRemoved = (args: RemovingEventArgs) => {
console.log('Removed:', args);
this.showNotification('File removed');
};Preview and Attachment Templates
File Preview Template
Customize file preview before upload:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
previewTemplate: (file: any) => {
return `
<div class="file-preview">
<span class="file-icon">📄</span>
<span class="file-name">${file.name}</span>
<span class="file-size">${(file.size / 1024).toFixed(2)} KB</span>
</div>
`;
}
};Attachment Display Template
Customize how attachments appear in messages:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
attachmentTemplate: (attachment: any) => {
const isImage = /image/.test(attachment.type);
return `
<div class="attachment">
${isImage ? `<img src="${attachment.url}" alt="image">` :
`<a href="${attachment.url}" target="_blank">${attachment.name}</a>`}
</div>
`;
}
};Maximum File Count
Restrict Number of Files
Limit simultaneous file uploads:
attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/upload',
removeUrl: 'https://your-api.com/remove',
maximumCount: 5 // Max 5 files at once
};Default: 10 files
Complete Attachment Example
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings"
(beforeAttachmentUpload)="onBeforeAttachmentUpload($event)"
(attachmentUploadSuccess)="onAttachmentUploadSuccess($event)"
(attachmentUploadFailure)="onAttachmentUploadFailure($event)"
(attachmentRemoved)="onAttachmentRemoved($event)">
<e-messages></e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://your-api.com/api/files/upload',
removeUrl: 'https://your-api.com/api/files/delete',
allowedFileTypes: '.pdf,.doc,.docx,.jpg,.png',
maxFileSize: 5 * 1024 * 1024, // 5MB
maximumCount: 3,
saveFormat: 'Base64',
enableDragAndDrop: true
};
public onBeforeAttachmentUpload = (args: any) => {
console.log('Starting upload:', args.filesData[0].name);
};
public onAttachmentUploadSuccess = (args: any) => {
console.log('Upload success:', args);
};
public onAttachmentUploadFailure = (args: any) => {
console.error('Upload failed:', args.error);
};
public onAttachmentRemoved = (args: any) => {
console.log('Attachment removed:', args);
};
}---
Bot Integrations
Table of Contents
- Security Requirements
- Google Dialogflow Integration
- Microsoft Bot Framework Integration
- Direct Line Configuration
- Backend Setup
⚠️ Security Requirements
Before Production Deployment
CRITICAL: The code examples in this guide use http://localhost:5000 for local development. For production environments, you MUST follow these security practices:
1. Use HTTPS in Production
// ❌ Development Only
const backendUrl = 'http://localhost:5000/api/message';
// ✅ Production - Always use HTTPS
const backendUrl = 'https://your-secure-domain.com/api/message';2. Never Commit Secrets to Version Control
Create .gitignore in your project root:
# Environment variables
.env
.env.local
.env.*.local
# Service account credentials
service-acct.json
credentials.json
*-key.json
token-server/.env
# Node modules
node_modules/3. Environment Variables Setup
For Token Server (.env):
# ❌ NEVER include quotes or actual secrets in .env
DIRECT_LINE_SECRET=your_actual_secret_from_azure
# ✅ Load from secure source (Azure Key Vault, AWS Secrets Manager, etc.)
DIALOGFLOW_PROJECT_ID=your_project_id
DIALOGFLOW_CREDENTIALS_PATH=/secure/path/to/service-acct.jsonLoad credentials securely in Node.js:
// ✅ Secure approach
const serviceAccount = JSON.parse(
fs.readFileSync(process.env.DIALOGFLOW_CREDENTIALS_PATH, 'utf8')
);
// ❌ Avoid this - hardcoding paths
const serviceAccount = require('./service-acct.json');4. CORS Configuration for Production
// ❌ Development only
app.use(cors());
// ✅ Production - restrict origins
app.use(cors({
origin: 'https://your-frontend-domain.com',
credentials: true,
optionsSuccessStatus: 200
}));5. Store Credentials Securely
| Environment | Solution |
|---|---|
| Azure | Azure Key Vault |
| AWS | AWS Secrets Manager |
| Google Cloud | Google Cloud Secret Manager |
| Local Development | .env file (in .gitignore) |
| Docker | Docker Secrets or environment variables |
---
Google Dialogflow Integration
Prerequisites
Before integrating Dialogflow, ensure:
1. Google Account with access to Google Cloud Console 2. Dialogflow Service Account with JSON credentials 3. Node.js Backend for secure API communication 4. Syncfusion Chat UI properly installed
Install Backend Dependencies
npm install express body-parser dialogflow corsSet Up Dialogflow Agent
1. Create a new agent in Dialogflow Console 2. Set agent name (e.g., "MyChatBot") 3. Add intents with training phrases and responses 4. Create service account in Google Cloud Console with "Dialogflow API Client" role 5. Download JSON key file
Configure Node.js Backend
Create backend/index.js:
const express = require('express');
const { SessionsClient } = require('dialogflow');
const bodyParser = require('body-parser');
const cors = require('cors');
const serviceAccount = require('./service-acct.json');
const app = express();
app.use(cors());
app.use(bodyParser.json());
const projectId = serviceAccount.project_id;
const sessionClient = new SessionsClient({ credentials: serviceAccount });
app.post('/api/message', async (req, res) => {
const message = req.body.text;
const sessionId = req.body.sessionId || 'default-session';
const sessionPath = `projects/${projectId}/agent/sessions/${sessionId}`;
const request = {
session: sessionPath,
queryInput: {
text: {
text: message,
languageCode: 'en-US',
},
},
};
try {
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
res.json({ reply: result.fulfillmentText });
} catch (err) {
console.error('Dialogflow error:', err);
res.status(500).json({ reply: "Error connecting to Dialogflow." });
}
});
app.listen(5000, () => console.log('Backend running on http://localhost:5000'));
// ⚠️ For production, use HTTPS and configure environment variablesConfigure Chat UI Component
import { Component } from '@angular/core';
import { ChatUIModule, MessageModel, UserModel } from '@syncfusion/ej2-angular-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chat-container" ejs-chatui
id="chat"
[user]="currentUserModel"
[messages]="messages"
(messageSend)="messageSend($event)"
headerText="Dialogflow Bot"
headerIconCss="chat-bot">
</div>
`,
styles: [`
.chat-bot {
background-image: url('//ej2.syncfusion.com/demos/src/chat-ui/images/bot.png');
background-color: unset;
}
`]
})
export class AppComponent {
public messages: MessageModel[] = [];
public currentUserModel: UserModel = { id: 'user1', user: 'You' };
public botUserModel: UserModel = {
id: 'bot',
user: 'Bot',
avatarUrl: 'https://ej2.syncfusion.com/demos/src/chat-ui/images/bot.png'
};
public async messageSend(args: any): Promise<void> {
args.cancel = true; // Prevent default send
// 1. Add user's message
const userMessage: MessageModel = {
text: args.message.text,
author: this.currentUserModel
};
this.messages = [...this.messages, userMessage];
// 2. Call Dialogflow backend
try {
// ⚠️ Replace with environment variable in production
const backendUrl = 'http://localhost:5000/api/message';
const response = await fetch(backendUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: args.message.text,
sessionId: this.currentUserModel.id
})
});
const data = await response.json();
// 3. Add bot response
const botReply: MessageModel = {
text: data.reply,
author: this.botUserModel
};
this.messages = [...this.messages, botReply];
} catch {
const errorMsg: MessageModel = {
text: "Sorry, I couldn't contact the server.",
author: this.botUserModel
};
this.messages = [...this.messages, errorMsg];
}
}
}Microsoft Bot Framework Integration
Prerequisites
1. Microsoft Azure Account 2. Azure Bot Service created and deployed 3. Direct Line Channel enabled 4. Syncfusion Chat UI installed
Enable Direct Line Channel
1. Go to Azure Portal 2. Navigate to your bot resource 3. Click "Channels" 4. Enable "Direct Line" 5. Copy the secret key (store securely)
Install Frontend Dependencies
npm install botframework-directlinejs axiosSet Up Token Server
Create token-server/.env:
DIRECT_LINE_SECRET= "YOUR_SECRET_KEY_HERE"Create token-server/index.js:
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const app = express();
app.use(cors());
const directLineSecret = process.env.DIRECT_LINE_SECRET;
app.post('/directline/token', async (req, res) => {
try {
const response = await axios.post(
'https://directline.botframework.com/v3/directline/tokens/generate',
{},
{
headers: {
'Authorization': `Bearer ${directLineSecret}`
}
}
);
res.json({ token: response.data.token });
} catch (err) {
console.error('Error generating token:', err);
res.status(500).json({ error: 'Failed to generate Direct Line token.' });
}
});
app.listen(5000, () => console.log('Token server on http://localhost:5000'));
// ⚠️ For production, deploy with HTTPS and secure credential storageDirect Line Configuration
Configure Chat UI Component
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ChatUIModule, MessageModel, UserModel } from '@syncfusion/ej2-angular-interactive-chat';
import { DirectLine } from 'botframework-directlinejs';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { Subscription } from 'rxjs';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'app-root',
imports: [ChatUIModule, HttpClientModule],
standalone: true,
template: `
<div id="chat-container" ejs-chatui
[user]="currentUserModel"
(messageSend)="messageSend($event)"
headerText="Microsoft Bot">
<e-messages>
<e-message
*ngFor="let msg of messages"
[text]="msg.text"
[author]="msg.author">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent implements OnInit, OnDestroy {
public currentUserModel: UserModel = { id: 'user1', user: 'You' };
public botUserModel: UserModel = { id: 'bot', user: 'Bot' };
public messages: MessageModel[] = [];
private directLine!: DirectLine;
private activitySubscription!: Subscription;
constructor(private http: HttpClient) {}
async ngOnInit(): Promise<void> {
try {
// 1. Get Direct Line token from backend
// ⚠️ Replace with environment variable in production
const tokenServerUrl = 'http://localhost:5000/directline/token';
const response = await firstValueFrom(
this.http.post<{ token: string }>(tokenServerUrl, {})
);
const { token } = response!;
// 2. Create Direct Line connection
this.directLine = new DirectLine({ token });
// 3. Subscribe to incoming messages
this.activitySubscription = this.directLine.activity$
.filter(activity =>
activity.type === 'message' &&
activity.from.id !== this.currentUserModel.id
)
.subscribe(message => {
const botReply: MessageModel = {
text: message.text,
author: this.botUserModel
};
this.messages = [...this.messages, botReply];
});
} catch (error) {
console.error('Connection failed:', error);
}
}
ngOnDestroy(): void {
if (this.directLine) {
this.directLine.end();
}
if (this.activitySubscription) {
this.activitySubscription.unsubscribe();
}
}
public messageSend(args: any): void {
args.cancel = true;
if (!this.directLine) {
console.error('Direct Line not connected');
return;
}
// Add user message to UI
const userMessage: MessageModel = {
text: args.message.text,
author: this.currentUserModel
};
this.messages = [...this.messages, userMessage];
// Send to bot via Direct Line
this.directLine.postActivity({
from: { id: this.currentUserModel.id, name: this.currentUserModel.user },
type: 'message',
text: args.message.text
}).subscribe(
id => console.log('Message sent:', id),
error => console.error('Send failed:', error)
);
}
}Backend Setup
Security Best Practices
❌ Never do this:
// DON'T expose secrets in frontend code
const secret = 'direct-line-secret-12345';✅ Use token server:
// Backend only - secure
const secret = process.env.DIRECT_LINE_SECRET;Session Management
// Generate unique session ID per user
private generateSessionId = (): string => {
return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
};
// Store session for user
private sessionMap = new Map<string, string>();
public getOrCreateSession = (userId: string): string => {
if (!this.sessionMap.has(userId)) {
this.sessionMap.set(userId, this.generateSessionId());
}
return this.sessionMap.get(userId)!;
};Troubleshooting
Dialogflow Issues
❌ "Permission Denied"
✅ Verify service account has "Dialogflow API Client" role
❌ "CORS Error"
✅ Check CORS configuration in backend/index.js
❌ "No Response"
✅ Test intent in Dialogflow Console simulator
❌ "Quota Exceeded"
✅ Check API quotas in Google Cloud ConsoleMicrosoft Bot Issues
❌ "Token Server Error (500)"
✅ Ensure DIRECT_LINE_SECRET is correct in .env
❌ "Bot is Not Responding"
✅ Test bot in Azure Portal's "Test in Web Chat"
❌ "Connection Fails"
✅ Verify token server is running
✅ Check frontend Host URL matches CORS configComplete Integration Example
import { Component, OnInit } from '@angular/core';
import { ChatUIModule, MessageModel, UserModel } from '@syncfusion/ej2-angular-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-bot-chat',
template: `
<div class="bot-container">
<h2>Chat with Our Bot</h2>
<div id="chat" ejs-chatui
[user]="currentUser"
[messages]="chatMessages"
(messageSend)="handleMessage($event)"
headerText="Support Bot"
[enableAttachments]="true"
[attachmentSettings]="attachmentSettings">
</div>
</div>
`,
styles: [`
.bot-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
`]
})
export class BotChatComponent implements OnInit {
public currentUser: UserModel = { id: 'user123', user: 'You' };
public botUser: UserModel = { id: 'bot', user: 'Support Bot' };
public chatMessages: MessageModel[] = [];
public attachmentSettings: any = {
saveUrl: 'https://your-backend.com/api/upload',
removeUrl: 'https://your-backend.com/api/remove'
};
async ngOnInit() {
// Initialize bot connection
await this.initializeBot();
}
private async initializeBot(): Promise<void> {
// Call backend to initialize bot session
console.log('Bot initialized');
}
public async handleMessage(args: any): Promise<void> {
args.cancel = true;
// Add user message
this.chatMessages.push({
text: args.message.text,
author: this.currentUser
});
// Get bot response
const botResponse = await this.getBotResponse(args.message.text);
this.chatMessages.push({
text: botResponse,
author: this.botUser
});
}
private async getBotResponse(userMessage: string): Promise<string> {
try {
const response = await fetch('https://your-backend.com/api/bot/reply', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userMessage })
});
const data = await response.json();
return data.reply;
} catch (error) {
return 'Sorry, I encountered an error.';
}
}
}---
Events and Interactions
Table of Contents
- Component Lifecycle Events
- Message Send Event
- User Typing Event
- Message Toolbar Configuration
- Toolbar Item Click Events
Component Lifecycle Events
Created Event
The created event fires when the Chat UI component is fully rendered and ready for interaction:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
(created)="onCreated()">
<e-messages>
<e-message text="Welcome to Chat!" [author]="botUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public botUserModel: UserModel = { user: 'Bot', id: 'bot' };
public onCreated = () => {
console.log('Chat UI component initialized');
// Load initial data, set up subscriptions, etc.
};
}Use Cases for Created Event
public onCreated = () => {
// Fetch message history from server
this.loadMessageHistory();
// Initialize WebSocket connection
this.connectWebSocket();
// Set up typing indicator subscription
this.subscribeToTypingIndicators();
// Restore conversation state
this.restorePreviousChat();
};Message Send Event
Handle Message Submission
The messageSend event fires before a message is sent:
import { ChatUIModule, MessageSendEventArgs } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
(messageSend)="onMessageSend($event)">
<e-messages>
<e-message *ngFor="let msg of messages"
[text]="msg.text"
[author]="msg.author"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public botUserModel: UserModel = { user: 'Bot', id: 'bot' };
public messages: MessageModel[] = [];
public onMessageSend = async (args: MessageSendEventArgs) => {
const messageText = args.message.text;
// Add user's message immediately
this.messages.push({
text: messageText,
author: this.currentUserModel
});
// Send to backend
try {
const response = await fetch('/api/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: messageText })
});
const data = await response.json();
// Add bot response
this.messages.push({
text: data.reply,
author: this.botUserModel
});
} catch (error) {
console.error('Message send failed:', error);
}
};
}Message Send Event Args
interface MessageSendEventArgs {
message: MessageModel; // The message being sent
cancel: boolean; // Set to true to prevent sending
}Prevent Default Send
Use args.cancel = true to override default behavior:
public onMessageSend = (args: MessageSendEventArgs) => {
// Prevent default send, handle manually
args.cancel = true;
// Validate message content
if (args.message.text.trim() === '') {
console.warn('Cannot send empty message');
return;
}
// Custom validation
if (args.message.text.length > 500) {
console.warn('Message exceeds 500 characters');
return;
}
// Custom send logic
console.log('Sending custom message:', args.message);
};User Typing Event
Handle Typing Indicator
The userTyping event fires as the user types in the message input:
import { ChatUIModule, TypingEventArgs } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
(userTyping)="onTyping($event)">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
private typingTimeout: any;
public onTyping = (args: TypingEventArgs) => {
// Clear previous timeout
clearTimeout(this.typingTimeout);
// Broadcast typing status to other users
this.broadcastTypingStatus(true);
// Stop broadcasting after 2 seconds of inactivity
this.typingTimeout = setTimeout(() => {
this.broadcastTypingStatus(false);
}, 2000);
};
private broadcastTypingStatus = (isTyping: boolean) => {
// Send to server/WebSocket
console.log('User is typing:', isTyping);
};
}Display Typing Indicators
Use the typingUsers property to show who's typing:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[typingUsers]="typingUsers">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public typingUsers: UserModel[] = [];
// Simulate receiving typing notification
simulateUserTyping() {
this.typingUsers = [this.michaleUserModel];
// Clear after 3 seconds
setTimeout(() => {
this.typingUsers = [];
}, 3000);
}
}Message Toolbar Configuration
Default Toolbar Items
By default, the message toolbar includes:
- Copy
- Reply
- Pin
- Delete
Customize Toolbar
Configure toolbar with messageToolbarSettings:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings">
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
<e-message messageId="2" text="Good! How are you?" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public messageToolbarSettings: any = {
width: '100%',
items: [
{ icon: 'e-icon-forward', tooltip: 'Forward' },
{ icon: 'e-icon-reply', tooltip: 'Reply' },
{ icon: 'e-icon-copy', tooltip: 'Copy' },
{ icon: 'e-icon-trash', tooltip: 'Delete' },
{ icon: 'e-icon-pin', tooltip: 'Pin' }
]
};
}Toolbar Item Widths
Control toolbar width with the width property:
messageToolbarSettings: any = {
width: '50%', // 50% of message width
width: '200px', // Fixed pixel width
width: '100%' // Full width (default)
};ToolbarItemModel Properties
Complete Toolbar Item Configuration
The ToolbarItemModel interface provides 10 properties for comprehensive toolbar customization. This applies to both messageToolbarSettings and headerToolbar:
interface ToolbarItemModel {
align?: ItemAlign; // Toolbar item alignment
cssClass?: string; // Custom CSS class
disabled?: boolean; // Disable interaction
iconCss?: string; // Icon class
tabIndex?: number; // Tab order
template?: string | object; // Custom template
text?: string; // Display text
tooltip?: string; // Hover tooltip
type?: ItemType; // Item type
visible?: boolean; // Visibility control
}Align Property
Control toolbar item positioning with the align property:
import { ItemAlign } from '@syncfusion/ej2-navigations';
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', align: 'Left' },
{ iconCss: 'e-icon-reply', tooltip: 'Reply', align: 'Left' },
{ iconCss: 'e-icon-delete', tooltip: 'Delete', align: 'Right' },
{ iconCss: 'e-icon-more', tooltip: 'More', align: 'Right' }
]
};ItemAlign Values:
'Left'- Align to left side'Right'- Align to right side'Center'- Align to center
Disabled Property
Conditionally disable toolbar items:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings">
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public canDelete: boolean = false;
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', disabled: false },
{ iconCss: 'e-icon-reply', tooltip: 'Reply', disabled: false },
{
iconCss: 'e-icon-delete',
tooltip: 'Delete',
disabled: !this.canDelete // Disabled based on permissions
}
]
};
// Enable delete after authentication
enableDelete() {
this.canDelete = true;
this.messageToolbarSettings.items[2].disabled = false;
}
}TabIndex Property
Control keyboard navigation order:
public headerToolbar: any = {
items: [
{ iconCss: 'e-icons e-video', tooltip: 'Video Call', tabIndex: 1 },
{ iconCss: 'e-icons e-phone', tooltip: 'Voice Call', tabIndex: 2 },
{ iconCss: 'e-icons e-settings', tooltip: 'Settings', tabIndex: 3 }
]
};Tab Navigation:
- Positive values (1, 2, 3...) define custom tab order
0follows DOM order- Negative values remove from tab navigation
Template Property
Use custom templates for toolbar items:
import { NgTemplateOutlet } from '@angular/common';
@Component({
imports: [ChatUIModule, NgTemplateOutlet],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings">
<ng-template #customToolbarItem>
<div class="custom-toolbar-btn">
<span class="icon">⭐</span>
<span class="label">Star</span>
</div>
</ng-template>
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`,
styles: [`
.custom-toolbar-btn {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
}
.custom-toolbar-btn:hover {
background: #f0f0f0;
}
`]
})
export class AppComponent {
@ViewChild('customToolbarItem', { static: true })
public customToolbarItem: any;
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy' },
{ template: this.customToolbarItem, tooltip: 'Add to Favorites' },
{ iconCss: 'e-icon-delete', tooltip: 'Delete' }
]
};
}Type Property
Specify toolbar item types:
import { ItemType } from '@syncfusion/ej2-navigations';
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', type: 'Button' },
{ type: 'Separator' }, // Visual divider
{ iconCss: 'e-icon-reply', tooltip: 'Reply', type: 'Button' },
{ type: 'Separator' },
{ iconCss: 'e-icon-delete', tooltip: 'Delete', type: 'Button' }
]
};ItemType Values:
'Button'- Interactive button (default)'Separator'- Visual divider line
Visible Property
Conditionally show/hide toolbar items:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings">
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public isAdmin: boolean = false;
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', visible: true },
{ iconCss: 'e-icon-reply', tooltip: 'Reply', visible: true },
{ iconCss: 'e-icon-pin', tooltip: 'Pin', visible: true },
{
iconCss: 'e-icon-delete',
tooltip: 'Delete',
visible: this.isAdmin // Only admins can delete
}
]
};
// Update visibility dynamically
updateToolbarVisibility(role: string) {
this.isAdmin = role === 'admin';
this.messageToolbarSettings.items[3].visible = this.isAdmin;
}
}CssClass Property
Apply custom styling to toolbar items:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings">
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
</e-messages>
</div>
`,
styles: [`
.copy-btn {
color: #007bff;
}
.copy-btn:hover {
background: #e7f3ff;
}
.delete-btn {
color: #dc3545;
}
.delete-btn:hover {
background: #ffe0e0;
}
.star-btn {
color: #ffc107;
}
`]
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public messageToolbarSettings: any = {
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', cssClass: 'copy-btn' },
{ iconCss: 'e-icon-star', tooltip: 'Star', cssClass: 'star-btn' },
{ iconCss: 'e-icon-delete', tooltip: 'Delete', cssClass: 'delete-btn' }
]
};
}Complete ToolbarItemModel Example
Example using all properties together:
public messageToolbarSettings: any = {
items: [
{
iconCss: 'e-icon-copy',
text: 'Copy',
tooltip: 'Copy message to clipboard',
cssClass: 'copy-toolbar-btn',
disabled: false,
visible: true,
align: 'Left',
tabIndex: 1,
type: 'Button'
},
{
type: 'Separator',
visible: true
},
{
iconCss: 'e-icon-reply',
text: 'Reply',
tooltip: 'Reply to this message',
cssClass: 'reply-toolbar-btn',
disabled: false,
visible: true,
align: 'Left',
tabIndex: 2,
type: 'Button'
},
{
iconCss: 'e-icon-delete',
text: 'Delete',
tooltip: 'Delete this message',
cssClass: 'delete-toolbar-btn',
disabled: this.cannotDelete,
visible: this.isAuthorized,
align: 'Right',
tabIndex: 3,
type: 'Button'
}
]
};Differences: headerToolbar vs messageToolbarSettings
Both use ToolbarItemModel, but differ in context:
| Property | headerToolbar | messageToolbarSettings |
|---|---|---|
| Location | Chat header (top) | Per message (on hover) |
| Scope | Global chat actions | Message-specific actions |
| Common Actions | Video call, settings, profile | Copy, reply, pin, delete |
| Width Property | Not applicable | Configurable (50%, 100%, 200px) |
| Event | itemClicked | itemClicked (same handler pattern) |
Example: Both toolbars in one component
public headerToolbar: ToolbarSettingsModel = {
items: [
{ iconCss: 'e-icons e-video', tooltip: 'Video Call', align: 'Right' },
{ iconCss: 'e-icons e-settings', tooltip: 'Settings', align: 'Right' }
]
};
public messageToolbarSettings: any = {
width: '100%',
items: [
{ iconCss: 'e-icon-copy', tooltip: 'Copy', align: 'Left' },
{ iconCss: 'e-icon-reply', tooltip: 'Reply', align: 'Left' },
{ iconCss: 'e-icon-delete', tooltip: 'Delete', align: 'Right' }
]
};Toolbar Item Click Events
Handle Toolbar Actions
Use itemClicked event to handle toolbar item clicks:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component, ViewChild } from '@angular/core';
import { ChatUIComponent } from '@syncfusion/ej2-angular-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
#chatui_instance
[user]="currentUserModel"
[messageToolbarSettings]="messageToolbarSettings"
(itemClicked)="itemClicked($event)">
<e-messages>
<e-message messageId="1" text="How are you?" [author]="currentUserModel"></e-message>
<e-message messageId="2" text="Good! How are you?" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
@ViewChild('chatui_instance', { static: false })
public chatUIInstance!: ChatUIComponent;
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale Suyama', id: 'user2' };
public messageToolbarSettings: any = {
items: [
{ icon: 'e-icon-forward', tooltip: 'Forward' },
{ icon: 'e-icon-reply', tooltip: 'Reply' },
{ icon: 'e-icon-copy', tooltip: 'Copy' },
{ icon: 'e-icon-trash', tooltip: 'Delete' },
{ icon: 'e-icon-pin', tooltip: 'Pin' }
]
};
public itemClicked = (args: any) => {
const { item, message } = args;
switch (item.icon) {
case 'e-icon-forward':
console.log('Forward message:', message);
break;
case 'e-icon-reply':
console.log('Reply to message:', message);
break;
case 'e-icon-copy':
this.copyToClipboard(message.text);
break;
case 'e-icon-trash':
console.log('Delete message:', message);
break;
case 'e-icon-pin':
this.togglePin(message);
break;
}
};
private copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
console.log('Copied to clipboard:', text);
};
private togglePin = (message: any) => {
message.isPinned = !message.isPinned;
console.log('Message pinned:', message.isPinned);
};
}Common Toolbar Actions
// Forward to another user/chat
case 'e-icon-forward':
this.forwardMessage(message);
break;
// Reply with threading
case 'e-icon-reply':
this.replyToMessage(message);
break;
// Copy message text
case 'e-icon-copy':
navigator.clipboard.writeText(message.text);
break;
// Delete/remove message
case 'e-icon-trash':
this.deleteMessage(message);
break;
// Pin important message
case 'e-icon-pin':
this.pinMessage(message);
break;
// Edit message
case 'e-icon-edit':
this.editMessage(message);
break;Attachment Events
Before Upload Event
The beforeAttachmentUpload event fires before file upload begins:
public onBeforeAttachmentUpload = (args: UploadingEventArgs) => {
// Validate file size
if (args.filesData[0].size > 5 * 1024 * 1024) {
args.cancel = true;
console.error('File too large (max 5MB)');
}
};Upload Success Event
public onAttachmentUploadSuccess = (args: SuccessEventArgs) => {
console.log('File uploaded successfully:', args);
};Upload Failure Event
public onAttachmentUploadFailure = (args: FailureEventArgs) => {
console.error('Upload failed:', args.error);
};---
Getting Started with Syncfusion Angular Chat UI
Table of Contents
- Installation
- Angular Environment Setup
- Basic Component Initialization
- Configuring Messages and Users
- CSS Imports and Themes
- Running the Application
Installation
Package Availability
Syncfusion Angular Chat UI is available as an npm scoped package @syncfusion. Get all Angular Syncfusion packages from npm.
Ivy Library Distribution Package
For Angular 12 and above, use the Ivy distribution package (>=20.2.36), which supports modern Angular rendering:
npm install @syncfusion/ej2-angular-interactive-chat --saveAngular Environment Setup
Install Angular CLI
If you haven't already, install Angular CLI globally:
npm install -g @angular/cliCreate a New Angular Application
Use Angular CLI to scaffold a new Angular project:
ng new my-chat-app
cd my-chat-appThis creates a project with standalone components support (Angular 14+).
Basic Component Initialization
Add Chat UI to Component
Modify your component file (app.component.ts) to include the Chat UI:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `<div id="chatui" ejs-chatui [user]="currentUserModel"></div>`,
styles: [`
#chatui {
height: 500px;
width: 100%;
}
`]
})
export class AppComponent {
public currentUserModel: UserModel = {
user: 'Albert',
id: 'user1'
};
}Key Points:
- Import
ChatUIModulein theimportsarray - Use standalone component syntax with
standalone: true - Use
<div ejs-chatui>to render the component - Bind
[user]property with current user model
Configuring Messages and Users
Define Users
Create user models for all chat participants:
public currentUserModel: UserModel = {
user: 'Albert',
id: 'user1'
};
public michaleUserModel: UserModel = {
user: 'Michale Suyama',
id: 'user2'
};Add Static Messages
Use the <e-message> element to define static messages:
template: `
<div id="chatui" ejs-chatui [user]="currentUserModel">
<e-messages>
<e-message
text="Hi Michale, are we on track for the deadline?"
[author]="currentUserModel">
</e-message>
<e-message
text="Yes, the design phase is complete."
[author]="michaleUserModel">
</e-message>
<e-message
text="I'll review it and send feedback by today."
[author]="currentUserModel">
</e-message>
</e-messages>
</div>
`Dynamic Messages with TypeScript
For dynamic messages, use component properties and *ngFor:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat';
import { CommonModule } from '@angular/common';
@Component({
imports: [ChatUIModule, CommonModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui [user]="currentUserModel">
<e-messages>
<e-message
*ngFor="let msg of messages"
[text]="msg.text"
[author]="msg.author">
</e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public messages: MessageModel[] = [
{ text: 'Hello!', author: this.currentUserModel },
{ text: 'Hi there!', author: this.michaleUserModel }
];
}CSS Imports and Themes
Add CSS to Global Styles
Import Syncfusion CSS files in 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-navigations/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-dropdowns/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-angular-interactive-chat/styles/material3.css';Available Themes
Syncfusion supports multiple themes. Replace material3.css with:
material.css- Material Designbootstrap.css- Bootstrap themetailwind.css- Tailwind CSS themebootstrap4.css- Bootstrap 4 themehighcontrast.css- High contrast for accessibility
Example for Bootstrap theme:
@import "../node_modules/@syncfusion/ej2-base/styles/bootstrap.css";
@import '../node_modules/@syncfusion/ej2-inputs/styles/bootstrap.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/bootstrap.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/bootstrap.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/bootstrap.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/bootstrap.css';
@import '../node_modules/@syncfusion/ej2-angular-interactive-chat/styles/bootstrap.css';Component-Scoped Styles
For component-specific styling, add styles in the component decorator:
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `<div id="chatui" ejs-chatui [user]="currentUserModel"></div>`,
styles: [`
#chatui {
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
`]
})
export class AppComponent { }Running the Application
Development Server
Start the Angular development server:
ng serveOr with automatic port selection:
ng serve --openNavigate to http://localhost:4200/ in your browser.
Build for Production
ng build --configuration productionOutput is generated in the dist/ folder.
Minimal Working Example
Here's a complete example to verify installation:
import { Component } from '@angular/core';
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div style="padding: 20px;">
<h1>Chat UI Component</h1>
<div id="chatui" ejs-chatui [user]="currentUserModel" headerText="Chat">
<e-messages>
<e-message text="Hello! Welcome to Chat UI" [author]="botUserModel"></e-message>
</e-messages>
</div>
</div>
`,
styles: [`
#chatui {
height: 400px;
width: 100%;
max-width: 600px;
border: 1px solid #ddd;
border-radius: 4px;
}
`]
})
export class AppComponent {
public currentUserModel: UserModel = {
user: 'You',
id: 'user1'
};
public botUserModel: UserModel = {
user: 'Bot',
id: 'bot'
};
}Troubleshooting
Issue: Module not found errors
- Verify
@syncfusion/ej2-angular-interactive-chatis installed:npm list - Clear node_modules and reinstall:
rm -rf node_modules && npm install
Issue: Styles not applied
- Check CSS imports in
styles.css - Ensure all required theme files are imported
- Verify theme files exist in
node_modules
Issue: Component not rendering
- Confirm
ChatUIModuleis imported in component - Check component selector is added to template
- Use browser console to check for errors
Issue: Type errors with TypeScript
- Import types from
@syncfusion/ej2-interactive-chat - Example:
import { UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat'
---
Globalization and Localization
Table of Contents
- Localization (i18n)
- Typing Indicator Translations
- Right-to-Left (RTL) Support
- Multiple Language Support
Localization (i18n)
Configure Language Support
The Chat UI component supports localization for typing indicators and other UI text. Set language using the locale property:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { L10n } from '@syncfusion/ej2-base';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[locale]="locale"
[typingUsers]="typingUsers">
<e-messages>
<!-- Messages -->
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public locale: string = 'en'; // English locale
public typingUsers: UserModel[] = [this.michaleUserModel];
}Typing Indicator Translations
Localization Keys
The Chat UI supports these typing indicator keys:
oneUserTyping - Single user typing
twoUserTyping - Two users typing
threeUserTyping - Three users typing
multipleUsersTyping - More than three users typingConfigure German Translations
import { L10n } from '@syncfusion/ej2-base';
export class AppComponent implements OnInit {
public locale: string = 'de';
public ngOnInit(): void {
L10n.load({
'de': {
"chat-ui": {
"oneUserTyping": "{0} tippt",
"twoUserTyping": "{0} und {1} tippen",
"threeUserTyping": "{0}, {1} und {2} andere tippen gerade",
"multipleUsersTyping": "{0}, {1} und {2} andere tippen gerade"
}
}
});
}
}Common Language Translations
Spanish:
L10n.load({
'es': {
"chat-ui": {
"oneUserTyping": "{0} está escribiendo",
"twoUserTyping": "{0} y {1} están escribiendo",
"threeUserTyping": "{0}, {1} y {2} otros están escribiendo",
"multipleUsersTyping": "{0}, {1} y {2} otros están escribiendo"
}
}
});French:
L10n.load({
'fr': {
"chat-ui": {
"oneUserTyping": "{0} tape",
"twoUserTyping": "{0} et {1} tapent",
"threeUserTyping": "{0}, {1} et {2} autres tapent",
"multipleUsersTyping": "{0}, {1} et {2} autres tapent"
}
}
});Japanese:
L10n.load({
'ja': {
"chat-ui": {
"oneUserTyping": "{0}が入力中です",
"twoUserTyping": "{0}と{1}が入力中です",
"threeUserTyping": "{0}、{1}、および{2}人の他のユーザーが入力中です",
"multipleUsersTyping": "{0}、{1}、および{2}人の他のユーザーが入力中です"
}
}
});Localization with User Count
The placeholders work as follows:
{0}- First user name{1}- Second user name{2}- Count of additional users
// Example: "Albert, Sarah, and 3 others are typing"
// oneUserTyping: "Albert is typing"
// twoUserTyping: "Albert and Sarah are typing"
// threeUserTyping: "Albert, Sarah, and 1 other are typing"
// multipleUsersTyping: "Albert, Sarah, and 3 others are typing"Right-to-Left (RTL) Support
Enable RTL Layout
Use the enableRtl property for right-to-left languages:
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[enableRtl]="true">
<e-messages>
<e-message text="مرحبا! كيف حالك؟" [author]="currentUserModel"></e-message>
<e-message text="بخير! وأنت؟" [author]="michaleUserModel"></e-message>
</e-messages>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'ألبرت', id: 'user1' };
public michaleUserModel: UserModel = { user: 'ميشيل', id: 'user2' };
}Supported RTL Languages
Arabic (ar)
Hebrew (he)
Persian/Farsi (fa)
Urdu (ur)RTL with CSS
Alternatively, use CSS to apply RTL:
/* Enable RTL globally */
html {
direction: rtl;
}
/* RTL for chat component only */
#chatui {
direction: rtl;
}Multiple Language Support
Dynamic Language Switching
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { L10n } from '@syncfusion/ej2-base';
import { Component } from '@angular/core';
@Component({
imports: [ChatUIModule],
standalone: true,
selector: 'app-root',
template: `
<div>
<div style="margin-bottom: 10px;">
<button (click)="changeLanguage('en')">English</button>
<button (click)="changeLanguage('de')">Deutsch</button>
<button (click)="changeLanguage('es')">Español</button>
<button (click)="changeLanguage('ar')">العربية</button>
</div>
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[locale]="currentLocale"
[enableRtl]="currentLocale === 'ar'">
<e-messages>
<e-message text="Hello!" [author]="currentUserModel"></e-message>
</e-messages>
</div>
</div>
`
})
export class AppComponent {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public currentLocale: string = 'en';
constructor() {
this.loadTranslations();
}
private loadTranslations = () => {
L10n.load({
'en': {
"chat-ui": {
"oneUserTyping": "{0} is typing",
"twoUserTyping": "{0} and {1} are typing",
"threeUserTyping": "{0}, {1}, and {2} other are typing",
"multipleUsersTyping": "{0}, {1}, and {2} others are typing"
}
},
'de': {
"chat-ui": {
"oneUserTyping": "{0} tippt",
"twoUserTyping": "{0} und {1} tippen",
"threeUserTyping": "{0}, {1} und {2} andere tippen",
"multipleUsersTyping": "{0}, {1} und {2} andere tippen"
}
},
'es': {
"chat-ui": {
"oneUserTyping": "{0} está escribiendo",
"twoUserTyping": "{0} y {1} están escribiendo",
"threeUserTyping": "{0}, {1} y {2} otros están escribiendo",
"multipleUsersTyping": "{0}, {1} y {2} otros están escribiendo"
}
},
'ar': {
"chat-ui": {
"oneUserTyping": "{0} يكتب",
"twoUserTyping": "{0} و {1} يكتبان",
"threeUserTyping": "{0} و {1} و {2} آخرين يكتبون",
"multipleUsersTyping": "{0} و {1} و {2} آخرين يكتبون"
}
}
});
};
public changeLanguage = (locale: string) => {
this.currentLocale = locale;
};
}Locale Persistence
Store user language preference:
public changeLanguage = (locale: string) => {
this.currentLocale = locale;
// Save to localStorage
localStorage.setItem('preferredLanguage', locale);
};
public loadUserPreference = () => {
const savedLocale = localStorage.getItem('preferredLanguage');
if (savedLocale) {
this.currentLocale = savedLocale;
}
};Complete Globalization Example
import { ChatUIModule } from '@syncfusion/ej2-angular-interactive-chat';
import { UserModel } from '@syncfusion/ej2-interactive-chat';
import { L10n } from '@syncfusion/ej2-base';
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ChatUIModule, CommonModule],
standalone: true,
selector: 'app-root',
template: `
<div style="max-width: 600px; margin: 0 auto;">
<div style="margin-bottom: 15px; display: flex; gap: 10px;">
<button *ngFor="let lang of languages"
(click)="changeLanguage(lang.code)"
[style.background]="currentLocale === lang.code ? '#007bff' : '#ccc'"
[style.color]="currentLocale === lang.code ? 'white' : 'black'">
{{lang.name}}
</button>
</div>
<div id="chatui" ejs-chatui
[user]="currentUserModel"
[locale]="currentLocale"
[enableRtl]="isRTL()"
[typingUsers]="typingUsers"
headerText="Chat App">
<e-messages>
<e-message text="Hello! How are you?" [author]="currentUserModel"></e-message>
<e-message text="Great! Thanks for asking." [author]="michaleUserModel"></e-message>
</e-messages>
</div>
</div>
`,
styles: [`
button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
`]
})
export class AppComponent implements OnInit {
public currentUserModel: UserModel = { user: 'Albert', id: 'user1' };
public michaleUserModel: UserModel = { user: 'Michale', id: 'user2' };
public currentLocale: string = 'en';
public typingUsers: UserModel[] = [this.michaleUserModel];
public languages = [
{ code: 'en', name: 'English' },
{ code: 'de', name: 'Deutsch' },
{ code: 'es', name: 'Español' },
{ code: 'fr', name: 'Français' },
{ code: 'ar', name: 'العربية' }
];
public ngOnInit(): void {
this.loadTranslations();
this.loadUserPreference();
}
private loadTranslations = () => {
L10n.load({
'en': {
"chat-ui": {
"oneUserTyping": "{0} is typing",
"twoUserTyping": "{0} and {1} are typing",
"threeUserTyping": "{0}, {1}, and {2} other are typing",
"multipleUsersTyping": "{0}, {1}, and {2} others are typing"
}
},
'de': {
"chat-ui": {
"oneUserTyping": "{0} tippt",
"twoUserTyping": "{0} und {1} tippen",
"threeUserTyping": "{0}, {1} und {2} andere tippen",
"multipleUsersTyping": "{0}, {1} und {2} andere tippen"
}
},
'es': {
"chat-ui": {
"oneUserTyping": "{0} está escribiendo",
"twoUserTyping": "{0} y {1} están escribiendo",
"threeUserTyping": "{0}, {1} y {2} otros están escribiendo",
"multipleUsersTyping": "{0}, {1} y {2} otros están escribiendo"
}
},
'fr': {
"chat-ui": {
"oneUserTyping": "{0} tape",
"twoUserTyping": "{0} et {1} tapent",
"threeUserTyping": "{0}, {1} et {2} autres tapent",
"multipleUsersTyping": "{0}, {1} et {2} autres tapent"
}
},
'ar': {
"chat-ui": {
"oneUserTyping": "{0} يكتب",
"twoUserTyping": "{0} و {1} يكتبان",
"threeUserTyping": "{0} و {1} و {2} آخرين يكتبون",
"multipleUsersTyping": "{0} و {1} و {2} آخرين يكتبون"
}
}
});
};
public changeLanguage = (locale: string) => {
this.currentLocale = locale;
localStorage.setItem('preferredLanguage', locale);
};
private loadUserPreference = () => {
const savedLocale = localStorage.getItem('preferredLanguage');
if (savedLocale && this.languages.find(l => l.code === savedLocale)) {
this.currentLocale = savedLocale;
}
};
public isRTL = (): boolean => {
return this.currentLocale === 'ar';
};
}---