The DevExtreme Form ships with AI-powered Smart Paste functionality. When a user copies unstructured text from external sources such as documents, spreadsheets, web pages, or emails, Smart Paste processes clipboard data and populates related form fields automatically.
Use the following APIs to activate Smart Paste in our Form component:
Configure each Form item using aiOptions:
@section ExternalDependencies {
<script type="module">
import { AzureOpenAI } from "https://esm.sh/openai@4.73.1";
window.AzureOpenAI = AzureOpenAI;
</script>
}
@using DevExtreme.MVC.Demos.ViewModels
@{
var textAreaText = @"Payment: Amount - $123.00
Statement Date: 10/15/2024
Name: John Smith
Contact: (123) 456-7890
Email: john@myemail.com
Address:
- 123 Elm St Apt 4B
- New York, NY 10001";
}
<div id="textarea-label" class="instruction">
Copy text from the editor below to the clipboard. Edit the text to see how your changes affect Smart Paste result.
</div>
<div class="instruction">
Paste text from the clipboard to populate the form. Press Ctrl+Shift+V (when the form is focused) or use the "Smart Paste" button under the form.
</div>
<div class="textarea-container">
@(Html.DevExtreme().Button()
.Text("Copy Text")
.Icon("copy")
.Type("default")
.StylingMode(ButtonStylingMode.Contained)
.Width("fit-content")
.OnClick("onCopy")
)
@(Html.DevExtreme().TextArea()
.ID("textarea")
.Value(textAreaText)
.InputAttr("aria-labelledby", "textarea-label")
.StylingMode(EditorStylingMode.Filled)
.Height("100%")
)
</div>
@(Html.DevExtreme().Form<SmartPasteFormViewModel>()
.ID("form")
.AiIntegration(new JS("aiIntegration"))
.LabelMode(FormLabelMode.Outside)
.LabelLocation(FormLabelLocation.Top)
.ShowColonAfterLabel(false)
.MinColWidth(220)
.Items(items => {
items.AddGroup()
.Caption("Billing Summary")
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.AmountDue)
.Editor(e => e
.TextBox()
.Placeholder("$0.00")
.StylingMode(EditorStylingMode.Filled)
)
.AiOptions(e => e
.Instruction("Format as the following: $0.00")
);
groupItems.AddSimpleFor(m => m.StatementDate)
.Editor(e => e
.DateBox()
.Placeholder("MM/DD/YYYY")
.StylingMode(EditorStylingMode.Filled)
)
.AiOptions(e => e
.Instruction("Format as the following: MM/DD/YYYY")
)
.IsRequired(false);
});
items.AddGroup()
.Caption("Billing Information")
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.FirstName)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
);
groupItems.AddSimpleFor(m => m.LastName)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
);
groupItems.AddSimpleFor(m => m.PhoneNumber)
.Editor(e => e
.TextBox()
.Placeholder("(000) 000-0000")
.StylingMode(EditorStylingMode.Filled)
)
.AiOptions(e => e
.Instruction("Format as the following: (000) 000-0000")
);
groupItems.AddSimpleFor(m => m.Email)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
)
.ValidationRules(
vr => {
vr.AddEmail();
}
)
.AiOptions(e => e
.Instruction("Do not fill this field if the text contains an invalid email address. A valid email is in the following format: email@example.com")
);
});
items.AddGroup()
.Caption("Billing Address")
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.StreetAddress)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
);
groupItems.AddSimpleFor(m => m.City)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
);
groupItems.AddSimpleFor(m => m.State)
.DataField("State/Province/Region")
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
);
groupItems.AddSimpleFor(m => m.ZIP)
.Editor(e => e
.TextBox()
.StylingMode(EditorStylingMode.Filled)
)
.AiOptions(e => e
.Instruction("If the text does not contain a ZIP, determine the ZIP code from the provided address.")
);
});
items.AddGroup()
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.CssClass("buttons-group")
.Items(groupItems => {
groupItems.AddButton().Name("smartPaste")
.ButtonOptions(b => b
.Type(ButtonType.Default)
.StylingMode(ButtonStylingMode.Contained)
);
groupItems.AddButton().Name("reset")
.ButtonOptions(b => b
.Type(ButtonType.Normal)
.StylingMode(ButtonStylingMode.Outlined)
);
});
})
)
<script>
let aiService;
const deployment = 'gpt-4o-mini';
const apiVersion = '2024-02-01';
const endpoint = 'https://public-api.devexpress.com/demo-openai';
const apiKey = 'DEMO';
async function getAIResponse(messages, signal) {
const params = {
messages,
model: deployment,
max_tokens: 1000,
temperature: 0.7,
};
return aiService.chat.completions.create(params, { signal });
}
const aiIntegration = new DevExpress.aiIntegration({
sendRequest({ prompt }) {
const controller = new AbortController();
const signal = controller.signal;
const aiPrompt = [
{ role: 'system', content: prompt.system, },
{ role: 'user', content: prompt.user, },
];
const promise = new Promise(async (resolve, reject) => {
try {
const response = await getAIResponse(aiPrompt, signal);
const result = response.choices[0].message?.content;
resolve(result);
} catch {
showNotification('Something went wrong. Please try again.', '#form', true);
reject();
}
});
const result = {
promise,
abort: () => {
controller.abort();
},
};
return result;
},
});
$(() => {
aiService = new AzureOpenAI({
dangerouslyAllowBrowser: true,
deployment,
endpoint,
apiVersion,
apiKey,
});
const form = $('#form').dxForm('instance');
form.registerKeyHandler('V', (event) => {
if (event.ctrlKey && event.shiftKey) {
navigator.clipboard.readText()
.then((text) => {
if (text) {
form.smartPaste(text);
} else {
showNotification(
'Clipboard is empty. Copy text before pasting',
'#form',
);
}
})
.catch(() => {
showNotification(
'Could not access the clipboard',
'#form',
);
});
}
});
});
function showNotification(message, of, isError, offset) {
DevExpress.ui.notify({
message,
position: {
my: 'bottom center',
at: 'bottom center',
of,
offset: offset ?? '0 -50',
},
width: 'fit-content',
maxWidth: 'fit-content',
minWidth: 'fit-content',
}, isError ? 'error' : 'info', 1500);
};
function onCopy(data) {
const textAreaText = $('#textarea').dxTextArea('instance').option('value');
navigator.clipboard.writeText(textAreaText);
showNotification('Text copied to clipboard', "#textarea", false, '0 -20');
}
</script>
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using DevExtreme.MVC.Demos.Models.SampleData;
using DevExtreme.MVC.Demos.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Web.Mvc;
namespace DevExtreme.MVC.Demos.Controllers {
public class FormController : Controller {
public ActionResult SmartPaste() {
return View(new SmartPasteFormViewModel {});
}
}
}
using System;
using System.Collections.Generic;
namespace DevExtreme.MVC.Demos.ViewModels {
public class FormViewModel {
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public string Position { get; set; }
public string OfficeNo { get; set; }
public DateTime BirthDate { get; set; }
public DateTime HireDate { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Skype { get; set; }
public string Notes { get; set; }
}
public class DynamicFormViewModel {
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public List<string> Phones { get; set; }
}
public class SmartPasteFormViewModel {
public string AmountDue { get; set; }
public DateTime StatementDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIP { get; set; }
}
}
.demo-container {
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-rows: auto auto;
gap: 24px 40px;
min-width: 720px;
max-width: 900px;
margin: auto;
}
.instruction {
color: var(--dx-texteditor-color-label);
}
.textarea-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.dx-layout-manager .dx-field-item.dx-last-row {
padding-top: 4px;
}
.dx-toast-info .dx-toast-icon {
display: none;
}
.buttons-group {
display: flex;
width: 100%;
justify-content: end;
}
.buttons-group .dx-item-content {
gap: 8px;
}
.buttons-group .dx-field-item:not(.dx-first-col),
.buttons-group .dx-field-item:not(.dx-last-col) {
padding: 0;
}
.buttons-group .dx-item {
flex: unset !important;
}