DateRangeField
Since 0.1.0-alpha.20Static server-side rendering: no circuit, no WebAssembly.
Two whole days, both included: the browser's own date inputs in one box, so each posts with the form, as {name}.Start and {name}.End, and takes a typed date, and the library's months to choose from. Bind it to a DateRange.
Usage
<DateRangeField @bind-Value="Model.Period" />
<DateRangeField @bind-Value="Model.Period" Min="today" MaxDays="90" />
<DateRangeField @bind-Value="Model.Period" DisplayMonths="1" />
<DateRangeField @bind-Value="Model.Period" Presets="DateRangePreset.Common(today)" />
<DateRangeField @bind-Value="Model.Period" UseNative />Limits
Min, Max, MinDays and MaxDays hold on the server. DateDisabled turns away a range with a disabled day in it; AllowDisabledInRange keeps that to its ends.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="date-range-limits" OnValidSubmit="Save" MaxWidth="24.Rem" Grow>
<DateRangeField @bind-Value="Model!.Stay" Min="First" MinDays="2" MaxDays="14" DateDisabled="NoArrivals" AllowDisabledInRange
Hint="Two nights to two weeks. No one arrives or leaves on a Sunday." />
<Row><Button Submit>Book the stay</Button></Row>
if (_saved is { } saved)
{
<Alert Status="Status.Success">Booked: saved.Start to saved.End, saved.Days days.</Alert>
}
</Form>
{
private static readonly DateOnly First = new(2027, 3, 1);
[SupplyParameterFromForm(FormName = "date-range-limits")]
private Booking? Model { get; set; }
// What was saved, not the field: on an interactive page it follows typing.
private DateRange? _saved;
protected override void OnInitialized() => Model ??= new();
private static bool NoArrivals(DateOnly day) => day.DayOfWeek == DayOfWeek.Sunday;
private void Save() => _saved = Model!.Stay;
public sealed class Booking
{
[Required]
public DateRange? Stay { get; set; } = new(new DateOnly(2027, 3, 8), new DateOnly(2027, 3, 12));
}
}Static: The server turns away a range outside the rules, whatever the browser let through.
Presets
Use Presets for ranges one click away, and DateRangePreset.Common for the usual ones, or list your own. They're absolute days from a today you pass in, so they follow your zone. One the field's rules would turn away is disabled.
Preview
Code
<DateRangeField @bind-Value="_period" Label="Period" Presets="DateRangePreset.Common(Today)" MaxWidth="24.Rem" Grow Hint="This example counts from March 15, 2027." />
{
// Yours would come from the clock, in the zone the app means.
private static readonly DateOnly Today = new(2027, 3, 15);
private DateRange? _period;
}Open ends, sizes, read-only and disabled
Either end can stay empty unless the field is Required. StartText and EndText change the captions.
Preview
Code
<Stack Gap="4" MaxWidth="24.Rem" Grow>
<DateRangeField @bind-Value="_open" Label="Open ended" StartText="From" EndText="Until" Clearable Hint="Either end can stay empty." />
<DateRangeField @bind-Value="_period" Label="Small" Size="ControlSize.Sm" />
<DateRangeField @bind-Value="_period" Label="Read-only" ReadOnly />
<DateRangeField @bind-Value="_period" Label="Disabled" Disabled />
</Stack>
{
private DateRange? _open;
private DateRange? _period = new(new DateOnly(2027, 3, 8), new DateOnly(2027, 3, 12));
}Details
The months write the inputs when the second day is chosen, so anything else that closes them changes nothing. A first day shades the days it can run to, and days past MinDays, MaxDays or a disabled day can't end it. UseNative keeps the browser's calendars. So do no script and a phone, unless the field has what a browser's calendar can't show: DateDisabled, MinDays, MaxDays or Presets. Presets the field's rules would turn away are disabled, and they're absolute days from a today the app passes in, so they follow its zone.
A DateRange holds days, so midnight never enters it. To query a time column: range.Contains(order.PlacedAt) a DateTime compares by its clock date var (from, until) = range.ToUtc(zone) from <= x && x < until, either may be null
Min, Max, MinDays, MaxDays and DateDisabled are checked on the server, along with the end not being before the start, so they hold against a phone, no script or a crafted post. DateDisabled turns away a span with a disabled day in it, unless AllowDisabledInRange. With Required, a field with only one end is turned away too. An empty field is null on an interactive page, and after a static post it can be a range with no ends: test IsEmpty rather than null.
Mode support
| Feature | Static | Interactive |
|---|---|---|
| Two date inputs in one field, posting each end, with the end's minimum following the start | Yes | Yes |
| Months side by side to choose from, and presets beside them | Yes | Yes |
| Min, Max, MinDays, MaxDays and disabled days checked on the server | Yes | Yes |
| Binds DateRange, whose days keep midnight out of it, with Contains and ToUtc for queries | Yes | Yes |
Static: static server-side rendering, with no circuit and no WebAssembly. Interactive: Server, WebAssembly or Auto.
Without JavaScript
Every page starts as plain HTML: before IzzyBlazor's script loads on a static page, while an interactive page prerenders, and with JavaScript off. With JavaScript off, the browser's own calendar opens on each date in place of the library's two months, and the end's minimum follows the start only after the form posts.
Accessibility
- It renders a
fieldsetnamed by the label, with alabellinked to each date input, and the hint and errors linked to the group. - Screen readers read each part of each date, and arrow keys change it.
- The arrow between the inputs is hidden from screen readers.
- The months' button is named "Choose dates" (
ChooseLabel), or "Change dates" and both days once they're set. Its panel is a non-modaldialog, named by the button, with each month agridnamed by its title. Both ends are selected in the grid. Focus goes to the start or today, and on closing back to where it opened from. A line under the months says how many days are chosen, and that a first day is waiting for its second. - Presets are a labelled group of buttons, and the one the field is set to is marked pressed.
Keyboard navigation
In a date
| Press this key | To do this |
|---|---|
| Up or Down | Change the focused part of the date |
| Left or Right | Move between the parts of the date |
| Tab | Move to the next part, then the other date |
| Alt+Down | Open the months |
In the months' days
| Press this key | To do this |
|---|---|
| Left or Right | Move a day (reversed in right-to-left) |
| Up or Down | Move a week |
| Home or End | Go to the start or end of the week |
| Page Up or Page Down | Move a month, keeping the day number or the month's last day |
| Shift+Page Up or Shift+Page Down | Move a year |
| Enter or Space | Choose the first day, or the last, which saves both and closes |
In the months
| Press this key | To do this |
|---|---|
| Tab or Shift+Tab | Move through the presets, the arrows and the days, and close past either end |
| Escape | Close without changing the value |
Parameters
| Prop | Type | Default |
|---|---|---|
ReadOnlyMakes the field read-only: it can be focused, and it still posts. | bool | false |
MinThe earliest day either end can be. | DateOnly? | |
MaxThe latest day either end can be. | DateOnly? | |
MinDaysThe fewest days a range can span, both ends counted. | int? | |
MaxDaysThe most days a range can span, both ends counted. | int? | |
AllowDisabledInRangeLets a range span days that DateDisabled turns off, as a stay can span a day with no arrivals. | bool | false |
UseNativeKeeps the browser's calendars in place of the library's months. | bool | false |
DisplayMonthsHow many months show side by side, from 1 to 6. | int | 2 |
ClearableA button in the field that empties both ends, there while it holds something. | bool | false |
PresetsRanges to choose in one click, beside the months, such as Common. | IReadOnlyList<DateRangePreset>? | |
ChooseLabelThe name of the months' button, in place of "Choose dates" and "Change dates" with both days. | string? | |
StartTextThe caption of the first date, in place of "Start". | string? | |
EndTextThe caption of the second date, in place of "End". | string? | |
FirstDayOfWeekThe first column. | DayOfWeek? | computed |
CultureThe culture for the month and day names, the first day and the week numbers. | CultureInfo? | computed |
ShowWeekNumbersAdds a first column with the week of the year, counted as the culture counts them. | bool | false |
ShowAdjacentDaysWhether to show the days of the months before and after, muted, in the first and last weeks. | bool | true |
DateDisabledWhether a day can't be chosen. | Func<DateOnly, bool>? | |
DateDisabledWindowHow far each way from the value or today to call DateDisabled and DayClass, in whole days, for an end the limits don't set. | TimeSpan? | |
DayClassA class for a day's button, or null. | Func<DateOnly, string?>? | |
@bind-ValueThe value, bound with @bind-Value. | DateRange? | |
DisplayNameThe field's name in validation messages from the input itself, such as a value that can't be parsed. | string? |
Plus 26 field props.
Full API