Angular Chat - Implement an AI Chat Clear Button
AI agent chats often include a button that allows users to start a new conversation. This tutorial shows how to add and configure a Clear Chat button in your app. You can find the complete example code for this tutorial in the following GitHub repository:
Configure a Chat Reset Function
To clear messages in DevExtreme Chat, follow these steps:
- Get the component DataSource instance and call store.push() to clear messages in the UI.
- Empty the dxChat's data source array.
- If you use a separate array to store AI messages, empty this array.
function clearChat(chatInstance) {
const removals = chatInstance.getDataSource().items().map((item) => ({ type: 'remove', key: item.id }));
chatInstance.getDataSource().store().push(removals);
chatMessages.length = 0;
aiMessages.length = 0;
// Clear typingUsers indicators and alerts if any are displayed
chatInstance.option({ alerts: [], typingUsers: [] });
}This tutorial uses the same approach as the following technical demo to store messages:
AI and Chatbot Integration Demo
Call clearChat() in the onClick handler of a DevExtreme Button to clear messages. You can initialize this button in the disabled state and enable the button in Chat's onMessageEntered handler:
function handleClearChatButtonClick(e) {
e.component.option('disabled', true);
clearChat(chatInstance);
}
function handleMessageEntered() {
clearChatButtonInstance.option('disabled', false);
}If a user clicks the Clear Chat button while an AI request is in progress, you must abort the request to ensure the button works as expected. This tutorial passes an AbortSignal from an AbortController to AzureOpenAI and calls abort() in the button's onClick handler:
let controller = new AbortController();
async function getAIResponse() {
const params = {
messages,
model,
// ...
};
const signalObj = {
signal: controller.signal,
};
const response = await chatService.chat.completions.create(params, signalObj);
return response.choices[0]?.message?.content;
}
function handleClearChatButtonClick() {
// ...
controller.abort();
}Assign a new AbortController to the controller variable in onMessageEntered to allow subsequent requests:
function handleMessageEntered() {
// ...
controller = new AbortController();
}Configure the UI
Your Clear Chat button configuration depends on how you use the Chat component in your app. This tutorial includes steps for three usage scenarios.
Standalone
This section shows how to configure a Clear Chat button with a standalone Chat component.
Define DevExtreme Chat and Toolbar components in a container, and configure the Clear Chat button in the Toolbar as follows:
jQuery
$('#dx-ai-chat').dxChat({
// ...
});
$('#ai-chat-toolbar').dxToolbar({
items: [{
location: 'before',
text: 'AI Assistant',
}, {
widget: 'dxButton',
location: 'after',
options: {
icon: 'clearhistory',
disabled: true,
// ...
}
}]
})
<!-- ... -->
<div class="demo-container">
<div class="ai-chat-container">
<div id="ai-chat-toolbar"></div>
<div id="dx-ai-chat"></div>
</div>
</div>ASP.NET Core Controls
<div class="ai-chat-container">
@(Html.DevExtreme().Toolbar()
.ID("ai-chat-toolbar")
.Items(i => {
i.Add().Location(ToolbarItemLocation.Before).Text("AI Assistant");
i.Add().Location(ToolbarItemLocation.After).Widget(w => w.Button()
.Icon("clearhistory")
.Disabled(true)
@* ... *@
);
})
)
@(Html.DevExtreme().Chat()
.ID("dx-ai-chat")
@* ... *@
)
</div>Angular
<div class="demo-container">
<div class="ai-chat-container">
<dx-toolbar id="ai-chat-toolbar">
<dxi-toolbar-item
location="before"
text="AI Assistant"
></dxi-toolbar-item>
<dxi-toolbar-item
location="after"
widget="dxButton"
[options]="clearButtonOptions"
></dxi-toolbar-item>
</dx-toolbar>
<dx-chat
#aiChat
>
<!-- ... -->
</dx-chat>
</div>
</div>
import { DxButtonModule, DxChatModule, DxToolbarModule } from 'devextreme-angular';
// ...
export class FullPageComponent {
clearButtonOptions = {
icon: 'clearhistory',
disabled: true,
}
}Vue
<template>
<div class="demo-container">
<div class="ai-chat-container">
<DxToolbar
id="ai-chat-toolbar"
>
<DxItem
location="before"
text="AI Assistant"
/>
<DxItem
location="after"
widget="dxButton"
:options="clearButtonOptions"
/>
</DxToolbar>
<DxChat
ref="chatInstance"
>
<!-- ... -->
</DxChat>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DxChat } from 'devextreme-vue/chat';
import { type DxButtonTypes } from 'devextreme-vue/button';
import { DxToolbar, DxItem } from 'devextreme-vue/toolbar';
const clearButtonOptions = ref<DxButtonTypes.Properties>({
icon: 'clearhistory',
disabled: true,
// ...
})
// ...
</script>React
import { Chat, type ChatRef } from 'devextreme-react/chat';
import { Toolbar, Item } from 'devextreme-react/toolbar';
import { type ButtonTypes } from 'devextreme-react/button';
const clearButtonOptions: ButtonTypes.Properties = {
icon: 'clearhistory',
disabled: true,
// ...
};
export default function FullPageExample(): JSX.Element {
// ...
return (
<div className='full-page demo-container'>
<div className='ai-chat-container'>
<Toolbar id='ai-chat-toolbar'>
<Item
location='before'
text='AI Assistant'
/>
<Item
location='after'
widget='dxButton'
options={clearButtonOptions}
/>
</Toolbar>
<Chat
ref={chatInstance}
>
{/* ... */}
</Chat>
</div>
</div>
);
}To visually combine the Chat and Toolbar components, define styles as follows:
- Container element: Define the background color and border styles to match DevExtreme component styling.
- Chat element: Disable the built-in border.
- Toolbar element: Add a bottom border to match DevExtreme component styling. Omit this style to match the built-in header appearance of the DevExpress Blazor AI Chat control.
.demo-container {
display: flex;
justify-content: center;
align-items: center;
}
.ai-chat-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100%;
background-color: var(--dx-component-color-bg);
width: 50%;
min-width: 22.45em;
max-width: 900px;
border-radius: 12px;
border: var(--dx-border-width) solid var(--dx-color-border);
}
.dx-chat {
border: none;
}
#ai-chat-toolbar.dx-toolbar {
border-bottom: var(--dx-border-width) solid var(--dx-color-border);
padding: 0 20px;
}Within a Drawer
This section shows how to configure a Clear Chat button in a DevExtreme Drawer.
Define a Drawer component and configure DevExtreme Toolbar and Chat in the Drawer's template as follows:
jQuery
$('#toggle-drawer-button').dxButton({
text: 'Reveal AI Chat',
icon: 'showpanel',
// ...
});
$('#drawer').dxDrawer({
position: 'right',
template() {
return $('<div>').addClass('drawer-ai-chat-container').append(
$('<div>').dxToolbar({
elementAttr: { id: 'ai-chat-drawer-toolbar' },
items: [{
location: 'before',
text: 'AI Assistant',
}, {
widget: 'dxButton',
location: 'after',
options: {
icon: 'clearhistory',
disabled: true,
// ...
}
}, /* ... */ ]
}),
$('<div>').dxChat({
elementAttr: { id: 'dx-ai-chat'},
// ...
}),
)
},
})
<!-- ... -->
<div class="demo-container">
<div id="drawer">
<div id="toggle-drawer-button"></div>
</div>
</div>ASP.NET Core Controls
@(Html.DevExtreme().Drawer()
.ID("ai-chat-drawer")
.Position(DrawerPosition.Right)
.Template(new TemplateName("DrawerAIChatTemplate"))
.Content(@<text>
@(Html.DevExtreme().Button()
.ID("toggle-drawer-button")
.Text("Reveal AI Chat")
.Icon("showpanel")
@* ... *@
)
</text>)
)
@using (Html.DevExtreme().NamedTemplate("DrawerAIChatTemplate")) {
<div class="drawer-ai-chat-container">
@(Html.DevExtreme().Toolbar()
.ID("ai-chat-drawer-toolbar")
.Width("100%")
.Items(i => {
i.Add().Location(ToolbarItemLocation.Before).Text("AI Assistant");
i.Add().Location(ToolbarItemLocation.After).Widget(w => w.Button()
.ElementAttr("id", @ChatData.ClearButtonId)
.Icon("clearhistory")
.Disabled(true)
@* ... *@
);
@* ... *@
})
)
@(Html.DevExtreme().Chat()
.ID("dx-ai-chat")
@* ... *@
)
</div>
}Angular
<div class="demo-container">
<dx-drawer
id="ai-chat-drawer"
template="drawerTemplate"
position="right"
>
<dx-button
id="toggle-drawer-button"
text="Reveal AI Chat"
icon="showpanel"
></dx-button>
<div *dxTemplate="let data of 'drawerTemplate'" style="height: 100%">
<div class="drawer-ai-chat-container">
<dx-toolbar id="ai-chat-drawer-toolbar">
<dxi-toolbar-item
location="before"
text="AI Assistant"
></dxi-toolbar-item>
<dxi-toolbar-item
location="after"
widget="dxButton"
[options]="clearButtonOptions"
></dxi-toolbar-item>
<!-- ... -->
</dx-toolbar>
<dx-chat
#aiChat
>
<!-- ... -->
</dx-chat>
</div>
</div>
</dx-drawer>
</div>
import { DxButtonModule, DxChatModule, DxToolbarModule, DxDrawerModule } from 'devextreme-angular';
// ...
export class DrawerComponent {
clearButtonOptions = {
icon: 'clearhistory',
disabled: true,
}
}Vue
<template>
<div class="demo-container">
<DxDrawer
id="ai-chat-drawer"
template="drawerTemplate"
position="right"
>
<DxButton
id="toggle-drawer-button"
text="Reveal AI Chat"
icon="showpanel"
/>
<template #drawerTemplate>
<div class="drawer-ai-chat-container">
<DxToolbar id="ai-chat-drawer-toolbar">
<DxItem
location="before"
text="AI Assistant"
/>
<DxItem
location="after"
widget="dxButton"
:options="clearButtonOptions"
/>
<!-- ... -->
</DxToolbar>
<DxChat
ref="chatInstance"
>
<!-- ... -->
</DxChat>
</div>
</template>
</DxDrawer>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DxDrawer } from 'devextreme-vue/drawer';
import { DxChat } from 'devextreme-vue/chat';
import { DxButton, type DxButtonTypes } from 'devextreme-vue/button';
import { DxToolbar, DxItem } from 'devextreme-vue/toolbar';
const clearButtonOptions = ref<DxButtonTypes.Properties>({
icon: 'clearhistory',
disabled: true,
// ...
})
// ...
</script>React
import { useCallback } from 'react';
import { Chat, type ChatRef } from 'devextreme-react/chat';
import { Toolbar, Item } from 'devextreme-react/toolbar';
import { Drawer } from 'devextreme-react/drawer';
import { Button, type ButtonTypes } from 'devextreme-react/button';
const clearButtonOptions: ButtonTypes.Properties = {
icon: 'clearhistory',
disabled: true,
// ...
};
export default function DrawerExample(): JSX.Element {
// ...
const renderDrawer = useCallback(() => (
<div className='drawer-ai-chat-container'>
<Toolbar id='ai-chat-drawer-toolbar'>
<Item
location='before'
text='AI Assistant'
/>
<Item
location='after'
widget='dxButton'
options={clearButtonOptions}
/>
{/* ... */}
</Toolbar>
<Chat
ref={chatInstance}
/>
</div>
), [/* ... */]);
return (
<div className='drawer demo-container'>
<Drawer
id='ai-chat-drawer'
render={renderDrawer}
position='right'
>
<Button
id='toggle-drawer-button'
text='Reveal AI Chat'
icon='showpanel'
/>
</Drawer>
</div>
);
}To visually combine the Chat and Toolbar components within the Drawer, define styles as follows:
- Drawer container element: Define the background color and border styles following DevExtreme component styling.
- Chat element: Disable the built-in border.
- Toolbar element: Add a bottom border following DevExtreme component styling.
.demo-container, .dx-drawer-content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.drawer-ai-chat-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100%;
min-width: 400px;
background-color: var(--dx-component-color-bg);
border-radius: 12px;
border: var(--dx-border-width) solid var(--dx-color-border);
}
.drawer-ai-chat-container #ai-chat-drawer-toolbar.dx-toolbar {
padding: 0 20px;
border-bottom: var(--dx-border-width) solid var(--dx-color-border);
}
.drawer-ai-chat-container .dx-chat {
border: none;
}
#toggle-drawer-button {
display: inline-flex;
}Within a Popup
This section shows how to configure a Clear Chat button in the built-in toolbar of DevExtreme Popup.
Configure a DevExtreme Popup and define a Chat component within the Popup's contentTemplate as follows:
jQuery
$('#open-popup-button').dxButton({
text: 'Reveal AI Chat',
icon: 'chatsparkleoutline',
// ...
})
$('#ai-chat-popup').dxPopup({
wrapperAttr: { id: 'ai-chat-popup-wrapper' },
toolbarItems: [{
location: 'before',
text: 'AI Assistant',
}, {
widget: 'dxButton',
location: 'after',
options: {
icon: 'clearhistory',
disabled: true,
// ...
}
}],
contentTemplate() {
return $('<div>').dxChat({
elementAttr: { id: 'dx-ai-chat' },
// ...
});
},
});
<!-- ... -->
<div class="demo-container">
<div id="open-popup-button"></div>
<div id="ai-chat-popup"></div>
</div>ASP.NET Core Controls
@(Html.DevExtreme().Button()
.ID("open-popup-button")
.Text("Reveal AI Chat")
.Icon("chatsparkleoutline")
@* ... *@
)
@(Html.DevExtreme().Popup()
.ID("ai-chat-popup")
.WrapperAttr(new { id = "ai-chat-popup-wrapper" })
.ToolbarItems(t => {
t.Add().Location(ToolbarItemLocation.Before).Text("AI Assistant");
t.Add().Location(ToolbarItemLocation.After).Widget(w => w.Button()
.Icon("clearhistory")
.Disabled(true)
@* ... *@
);
})
.ContentTemplate(@<text>
@(Html.DevExtreme().Chat()
.ID("dx-ai-chat")
@* ... *@
)
</text>)
)Angular
<div class="demo-container">
<dx-button
id="open-popup-button"
text="Reveal AI Chat"
icon="chatsparkleoutline"
></dx-button>
<dx-popup
contentTemplate="popupTemplate"
[wrapperAttr]="popupWrapperAttr"
>
<dxi-popup-toolbar-item
location="before"
text="AI Assistant"
></dxi-popup-toolbar-item>
<dxi-popup-toolbar-item
location="after"
widget="dxButton"
[options]="clearButtonOptions"
></dxi-popup-toolbar-item>
<div *dxTemplate="let data of 'popupTemplate'" style="height: 100%">
<dx-chat
#aiChat
>
<!-- ... -->
</dx-chat>
</div>
</dx-popup>
</div>
import { DxButtonModule, DxChatModule, DxToolbarModule, DxPopupModule } from 'devextreme-angular';
// ...
export class PopupComponent {
clearButtonOptions = {
icon: 'clearhistory',
disabled: true,
}
}Vue
<template>
<div class="demo-container">
<DxButton
id="open-popup-button"
text="Reveal AI Chat"
icon="chatsparkleoutline"
/>
<DxPopup
:wrapper-attr="popupWrapperAttr"
content-template="popupContent"
>
<DxToolbarItem
text="AI Assistant"
location="before"
/>
<DxToolbarItem
widget="dxButton"
location="after"
:options="clearButtonOptions"
/>
<template #popupContent>
<div>
<DxChat
ref="chatInstance"
>
<!-- ... -->
</DxChat>
</div>
</template>
</DxPopup>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DxChat } from 'devextreme-vue/chat';
import { DxButton, type DxButtonTypes } from 'devextreme-vue/button';
import { DxPopup, DxToolbarItem } from 'devextreme-vue/popup';
const clearButtonOptions = ref<DxButtonTypes.Properties>({
icon: 'clearhistory',
disabled: true,
// ...
})
// ...
</script>React
import { useCallback } from 'react';
import { Chat, type ChatRef } from 'devextreme-react/chat';
import { Popup, ToolbarItem } from 'devextreme-react/popup';
import { Button, type ButtonTypes } from 'devextreme-react/button';
const clearButtonOptions: ButtonTypes.Properties = {
icon: 'clearhistory',
disabled: true,
// ...
};
export default function PopupExample(): JSX.Element {
// ...
const renderPopup = useCallback(() => (
<Chat
id='dx-ai-chat'
>
{/* ... */}
</Chat>
), [/* ... */]);
return (
<div className='popup demo-container'>
<Button
id='open-popup-button'
text='Reveal AI Chat'
icon='chatsparkleoutline'
/>
<Popup
wrapperAttr={popupWrapperAttr}
contentRender={renderPopup}
>
<ToolbarItem
text='AI Assistant'
location='before'
/>
<ToolbarItem
widget='dxButton'
location='after'
options={clearButtonOptions}
/>
</Popup>
</div>
);
}To visually integrate the Chat component in the Popup, define styles as follows:
- Popup content element: Set padding to 0.
- Chat element: Disable the built-in border.
.demo-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: calc(100% - 24px);
padding: 12px;
}
#open-popup-button {
display: inline-flex;
}
#ai-chat-popup-wrapper .dx-popup-content {
padding: 0;
}
#dx-ai-chat {
border: none;
}