$(() => {
const sendRequest = function (value) {
const invalidEmail = 'test@dx-email.com';
const d = $.Deferred();
setTimeout(() => {
d.resolve(value !== invalidEmail);
}, 1000);
return d.promise();
};
const changePasswordMode = function (name) {
const editor = formWidget.getEditor(name);
editor.option('mode', editor.option('mode') === 'text' ? 'password' : 'text');
};
const formWidget = $('#form').dxForm({
formData,
readOnly: false,
showColonAfterLabel: true,
showValidationSummary: true,
validationGroup: 'customerData',
onOptionChanged: (e) => {
if (e.name === 'isDirty') {
const resetButton = formWidget.getButton('Reset');
resetButton.option('disabled', !e.value);
}
},
items: [{
itemType: 'group',
caption: 'Credentials',
items: [{
dataField: 'Email',
editorOptions: {
valueChangeEvent: 'keyup',
},
validationRules: [{
type: 'required',
message: 'Email is required',
}, {
type: 'email',
message: 'Email is invalid',
}, {
type: 'async',
message: 'Email is already registered',
validationCallback(params) {
return sendRequest(params.value);
},
}],
}, {
dataField: 'Password',
editorOptions: {
mode: 'password',
valueChangeEvent: 'keyup',
onValueChanged() {
const editor = formWidget.getEditor('ConfirmPassword');
if (editor.option('value')) {
editor.element().dxValidator('validate');
}
},
buttons: [{
name: 'password',
location: 'after',
options: {
stylingMode: 'text',
icon: 'eyeopen',
onClick: () => changePasswordMode('Password'),
},
}],
},
validationRules: [{
type: 'required',
message: 'Password is required',
}],
}, {
name: 'ConfirmPassword',
dataField: 'ConfirmPassword',
label: {
text: 'Confirm Password',
},
editorType: 'dxTextBox',
editorOptions: {
mode: 'password',
valueChangeEvent: 'keyup',
buttons: [{
name: 'password',
location: 'after',
options: {
icon: 'eyeopen',
stylingMode: 'text',
onClick: () => changePasswordMode('ConfirmPassword'),
},
}],
},
validationRules: [{
type: 'required',
message: 'Confirm Password is required',
}, {
type: 'compare',
message: "'Password' and 'Confirm Password' do not match",
comparisonTarget() {
return formWidget.option('formData').Password;
},
}],
}],
}, {
itemType: 'group',
caption: 'Personal Data',
items: [{
dataField: 'Name',
editorOptions: {
valueChangeEvent: 'keyup',
},
validationRules: [{
type: 'required',
message: 'Name is required',
}, {
type: 'pattern',
pattern: '^[^0-9]+$',
message: 'Do not use digits in the Name',
}],
}, {
dataField: 'Date',
editorType: 'dxDateBox',
label: {
text: 'Date of birth',
},
editorOptions: {
placeholder: 'Birth Date',
acceptCustomValue: false,
openOnFieldClick: true,
},
validationRules: [{
type: 'required',
message: 'Date of birth is required',
}, {
type: 'range',
max: new Date().setFullYear(new Date().getFullYear() - 21),
message: 'You must be at least 21 years old',
}],
}, {
dataField: 'VacationDates',
editorType: 'dxDateRangeBox',
label: {
text: 'Vacation Dates',
},
validationRules: [{
type: 'custom',
validationCallback: ({ value }) => {
const [startDate, endDate] = value;
if (startDate === null || endDate === null) {
return true;
}
const millisecondsPerDay = 24 * 60 * 60 * 1000;
const daysDifference = Math.abs((endDate - startDate) / millisecondsPerDay);
return daysDifference < 25;
},
message: 'The vacation period must not exceed 25 days',
}, {
type: 'custom',
validationCallback: ({ value }) => {
const [startDate, endDate] = value;
if (startDate === null && endDate === null) {
return true;
}
return startDate !== null && endDate !== null;
},
message: 'Both start and end dates must be selected',
}],
editorOptions: {
startDatePlaceholder: 'Start Date',
endDatePlaceholder: 'End Date',
acceptCustomValue: false,
},
}],
}, {
itemType: 'group',
caption: 'Billing address',
items: [{
dataField: 'Country',
editorType: 'dxSelectBox',
editorOptions: {
dataSource: countries,
},
validationRules: [{
type: 'required',
message: 'Country is required',
}],
}, {
dataField: 'City',
editorType: 'dxAutocomplete',
editorOptions: {
dataSource: cities,
minSearchLength: 2,
valueChangeEvent: 'keyup',
},
validationRules: [{
type: 'pattern',
pattern: '^[^0-9]+$',
message: 'Do not use digits in the City name',
}, {
type: 'stringLength',
min: 2,
message: 'City must have at least 2 symbols',
}, {
type: 'required',
message: 'City is required',
}],
}, {
dataField: 'Address',
editorOptions: {
valueChangeEvent: 'keyup',
},
validationRules: [{
type: 'required',
message: 'Address is required',
}],
}, {
dataField: 'Phone',
helpText: 'Enter the phone number in USA phone format',
editorOptions: {
mask: '+1 (X00) 000-0000',
maskRules: {
X: /[02-9]/,
},
maskInvalidMessage: 'The phone must have a correct USA phone format',
valueChangeEvent: 'keyup',
},
validationRules: [{
type: 'pattern',
pattern: /^[02-9]\d{9}$/,
message: 'The phone must have a correct USA phone format',
}],
}],
}, {
itemType: 'group',
cssClass: 'last-group',
colCountByScreen: {
xs: 2,
sm: 2,
md: 2,
lg: 2,
},
items: [{
dataField: 'Accepted',
label: {
visible: false,
},
editorOptions: {
width: 270,
text: 'I agree to the Terms and Conditions',
},
validationRules: [{
type: 'compare',
comparisonTarget() { return true; },
message: 'You must agree to the Terms and Conditions',
}],
}, {
itemType: 'group',
cssClass: 'buttons-group',
colCountByScreen: {
xs: 2,
sm: 2,
md: 2,
lg: 2,
},
items: [{
itemType: 'button',
name: 'Reset',
buttonOptions: {
onClick: () => {
formWidget.reset();
},
icon: 'refresh',
text: 'Reset',
disabled: true,
width: '120px',
},
}, {
itemType: 'button',
buttonOptions: {
text: 'Register',
type: 'default',
useSubmitBehavior: true,
width: '120px',
},
}],
}],
}],
}).dxForm('instance');
$('#form-container').on('submit', (e) => {
DevExpress.ui.notify({
message: 'You have submitted the form',
position: {
my: 'center top',
at: 'center top',
},
}, 'success', 3000);
e.preventDefault();
});
});
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>window.jQuery || document.write(decodeURIComponent('%3Cscript src="js/jquery.min.js"%3E%3C/script%3E'))</script>
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.2.3/css/dx.light.css" />
<script src="js/dx.all.js"></script>
<script src="data.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="index.js"></script>
</head>
<body class="dx-viewport">
<div class="demo-container">
<form action="your-action" id="form-container">
<div id="form"></div>
</form>
</div>
</body>
</html>
form {
margin: 10px 10px 15px;
}
.last-group {
margin-top: 30px;
margin-bottom: 10px;
}
.last-group .dx-item-content {
align-items: start;
justify-content: center;
}
.last-group .dx-field-item {
padding: 0 !important;
}
.buttons-group {
display: flex;
width: 100%;
justify-content: end;
}
.buttons-group .dx-item-content {
gap: 10px;
}
const formData = {
Email: '',
Password: '',
Name: 'Peter',
Date: null,
VacationDates: [null, null],
Country: '',
City: null,
Address: '',
Phone: '',
Accepted: false,
};
const countries = ['Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan', 'The Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burma', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Central African Republic', 'Chad', 'Chile', 'China', 'Colombia', 'Comoros', 'Democratic Republic of the Congo', 'Republic of the Congo', 'Costa Rica', 'Ivory Coast', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'East Timor', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Fiji', 'Finland', 'France', 'Gabon', 'The Gambia', 'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada', 'Guatemala', 'Guinea', 'Guinea-Bissau', 'Guyana', 'Haiti', 'Honduras', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Republic of Ireland', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'North Korea', 'South Korea', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Republic of Macedonia', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Mauritania', 'Mauritius', 'Mexico', 'Federated States of Micronesia', 'Moldova', 'Monaco', 'Mongolia', 'Montenegro', 'Morocco', 'Mozambique', 'Namibia', 'Nauru', 'Nepal', 'Kingdom of the Netherlands', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman', 'Pakistan', 'Palau', 'State of Palestine', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia', 'Rwanda', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Vincent and the Grenadines', 'Samoa', 'San Marino', 'São Tomé and Príncipe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Tajikistan', 'Tanzania', 'Thailand', 'Togo', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican City', 'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe'];
const cities = ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Philadelphia', 'Phoenix', 'San Antonio', 'San Diego', 'Dallas', 'San Jose', 'Austin', 'Indianapolis', 'Jacksonville', 'San Francisco', 'Columbus', 'Charlotte', 'Fort Worth', 'Detroit', 'El Paso', 'Memphis', 'Seattle', 'Denver', 'Washington', 'Boston', 'Nashville', 'Baltimore', 'Oklahoma City', 'Louisville', 'Portland', 'Las Vegas', 'Milwaukee', 'Albuquerque', 'Tucson', 'Fresno', 'Sacramento', 'Long Beach', 'Kansas City', 'Mesa', 'Virginia Beach', 'Atlanta', 'Colorado Springs', 'Omaha', 'Raleigh', 'Miami', 'Oakland', 'Minneapolis', 'Tulsa', 'Cleveland', 'Wichita', 'Arlington', 'New Orleans', 'Bakersfield', 'Tampa', 'Honolulu', 'Aurora', 'Anaheim', 'Santa Ana', 'St. Louis', 'Riverside', 'Corpus Christi', 'Lexington', 'Pittsburgh', 'Anchorage', 'Stockton', 'Cincinnati', 'Saint Paul', 'Toledo', 'Greensboro', 'Newark', 'Plano', 'Henderson', 'Lincoln', 'Buffalo', 'Jersey City', 'Chula Vista', 'Fort Wayne', 'Orlando', 'St. Petersburg', 'Chandler', 'Laredo', 'Norfolk', 'Durham', 'Madison', 'Lubbock', 'Irvine', 'Winston–Salem', 'Glendale', 'Garland', 'Hialeah', 'Reno', 'Chesapeake', 'Gilbert', 'Baton Rouge', 'Irving', 'Scottsdale', 'North Las Vegas', 'Fremont', 'Boise', 'Richmond'];