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:

View on GitHub

Configure a Chat Reset Function

To clear messages in DevExtreme Chat, follow these steps:

  1. Get the component DataSource instance and call store.push() to clear messages in the UI.
  2. Empty the dxChat's data source array.
  3. If you use a separate array to store AI messages, empty this array.

JavaScript
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:

JavaScript
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:

JavaScript
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:

Code
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
full-page.js
full-page.html
$('#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
FullPage.cshtml
<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
full-page.component.html
full-page.component.ts
<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
full-page.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
FullPage.tsx
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.

CSS
.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
drawer.js
drawer.html
$('#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
Drawer.cshtml
@(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
drawer.component.html
drawer.component.ts
<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
drawer.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
Drawer.tsx
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.

CSS
.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
popup.js
popup.html
$('#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
Popup.cshtml
@(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
popup.component.html
popup.component.ts
<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
popup.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
Popup.tsx
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.

CSS
.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;
}