Feel free to share demo-related thoughts here.
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Thank you for the feedback!
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Backend API
x
DevExtreme.MVC.Demos.ViewModels
Html.BeginForm()) { (
using(Html.DevExtreme().ValidationGroup()) {
AntiForgeryToken() .
Html.DevExtreme().Form<EditorsViewModel>() (
.OnInitialized("onInitialized")
.OnOptionChanged("onOptionChanged")
.ShowValidationSummary(true)
.Items(items => {
items.AddGroup()
.Caption("Credentials")
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.Email)
.Editor(e => e.TextBox()
.ValueChangeEvent("keyup")
);
groupItems.AddSimpleFor(m => m.Password)
.Editor(e => e.TextBox()
.Mode(TextBoxMode.Password)
.InputAttr("aria-label", "Password")
.OnValueChanged("passwordChanged")
.ValueChangeEvent("keyup")
.Buttons(buttons => {
buttons.Add()
.Name("password")
.Location(TextEditorButtonLocation.After)
.Widget(w => w.Button()
.Type(ButtonType.Default)
.Icon("eyeopen")
.StylingMode(ButtonStylingMode.Text)
.OnClick("() => changePasswordMode('Password')")
);
})
);
groupItems.AddSimpleFor(m => m.ConfirmPassword)
.Editor(e => e.TextBox()
.Mode(TextBoxMode.Password)
.InputAttr("aria-label", "Password")
.ValueChangeEvent("keyup")
.Buttons(buttons => {
buttons.Add()
.Name("password")
.Location(TextEditorButtonLocation.After)
.Widget(w => w.Button()
.Type(ButtonType.Default)
.Icon("eyeopen")
.StylingMode(ButtonStylingMode.Text)
.OnClick("() => changePasswordMode('ConfirmPassword')"));
})
);
});
items.AddGroup()
.Caption("Personal Data")
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.Name)
.Editor(e => e.TextBox()
.ValueChangeEvent("keyup")
);
groupItems.AddSimpleFor(m => m.Date)
.Editor(e => e
.DateBox()
.OpenOnFieldClick(true)
.Placeholder("Birth Date")
.AcceptCustomValue(false)
);
groupItems.AddSimpleFor(m => m.VacationDates)
.Editor(e => e
.DateRangeBox()
.AcceptCustomValue(false)
.StartDatePlaceholder("Start Date")
.EndDatePlaceholder("End Date")
)
.ValidationRules(validationRule => {
validationRule.AddCustom()
.Message("Both start and end dates must be selected")
.ValidationCallback("validateVacationDatesPresence");
});
});
items.AddGroup()
.Caption("Billing address")
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.Country)
.Editor(e => e
.SelectBox()
.InputAttr("aria-label", "Country")
.DataSource(d => d.WebApi().RouteName("GeoNames").LoadAction("Countries"))
);
groupItems.AddSimpleFor(m => m.City)
.Editor(e => e
.Autocomplete()
.ValueChangeEvent("keyup")
.MinSearchLength(2)
.DataSource(d => d.WebApi().RouteName("GeoNames").LoadAction("Cities"))
);
groupItems.AddSimpleFor(m => m.Address)
.Editor(e => e.TextBox()
.ValueChangeEvent("keyup")
);
groupItems.AddSimpleFor(m => m.Phone)
.HelpText("Enter the phone number in USA phone format")
.Editor(e => e.TextBox()
.Mask("+1 (X00) 000-0000")
.InputAttr("aria-label", "Phone")
.ValueChangeEvent("keyup")
.MaskRules(new { X = new JS("/[02-9]/") })
.MaskInvalidMessage("The phone must have a correct USA phone format")
);
});
items.AddGroup()
.CssClass("last-group")
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.Items(groupItems => {
groupItems.AddSimpleFor(m => m.Accepted)
.Label(l => l.Visible(false))
.Editor(editor => editor.CheckBox()
.Width(270)
.Text("I agree to the Terms and Conditions")
);
groupItems.AddGroup()
.ColCountByScreen(c => c.Md(2).Sm(2).Lg(2))
.CssClass("buttons-group")
.Items(secondGroupItems => {
secondGroupItems.AddButton()
.Name("Reset")
.ButtonOptions(b => b.Text("Reset")
.Icon(Url.Content("refresh"))
.Width(120)
.Disabled(true)
.OnClick("onResetButtonClick")
);
secondGroupItems.AddButton()
.ButtonOptions(b => b.Text("Register")
.Type(ButtonType.Default)
.Width(120)
.UseSubmitBehavior(true)
);
});
});
})
.FormData(Model)
)
}
}
<script>
let formInstance;
function onInitialized(e) {
formInstance = e.component;
}
function onOptionChanged(e) {
if(e.name === 'isDirty') {
const resetButton = formInstance.getButton('Reset');
resetButton.option('disabled', !e.value);
}
}
function onResetButtonClick(e) {
formInstance.reset();
}
function passwordChanged(e) {
const editor = formInstance.getEditor('ConfirmPassword');
if (editor.option('value')) {
editor.element().dxValidator('validate');
}
}
function changePasswordMode(name) {
let editor = formInstance.getEditor(name);
editor.option('mode', editor.option('mode') === 'text' ? 'password' : 'text');
}
function validateVacationDatesPresence({ value }) {
const [startDate, endDate] = value;
if (startDate === null && endDate === null) {
return true;
}
return startDate !== null && endDate !== null;
}
</script>
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
Note that the "Register" button here does not implement the usual onClick event handler. Instead, it has the useSubmitBehavior property set to true. This setting tells the button to validate and submit the HTML form in which it is nested, with no further configuration required.