To recap, the HMTL5 week
input is supported by a number
of browsers and enables the user to select a week of the year:
It works with
values formatted according to the ISO 8601 standard, which, in the case of a
week of the year is in the format yyyy-Www
, where yyyy
is the full year -W
is literal, and ww
represents the
ISO 8601 week of
the year. In the instance above, the value is set to 2021-W01
- the
first week of 2021. The input tag helper renders input type="week"
for properties that have a DataType
attribute set to
"week"
.
You have a decision to make in respect of how you want to represent the
week in your server code. You could work with it as a string
,
or a DateTime
, or you can create your own custom type to
represent the week:
public class Week { public int Year { get; set; } public int WeekNumber { get; set; } public static Week TryParse(string input) { var result = input.Split("-W"); if (result.Length != 2) { return null; } if (int.TryParse(result[0], out int year) && int.TryParse(result[1], out int week)) { return new Week { Year = year, WeekNumber = week }; } return null; } public override string ToString() { return $"{Year}-W{WeekNumber:D2}"; } }
The TryParse
method takes a string and attempts to
generate a valid instance of the Week
type. The overridden ToString
method generates the correctly
formatted value that will be applied to the week
input when using the input
tag helper. Next, you need a way to call the TryParse
method in
user input. So, based on the recommendation to use a TypeConverter
,
here is the WeekConverter
:
public class WeekConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string input) { return Week.TryParse(input); } return base.ConvertFrom(context, culture, value); } }
The class derives from the TypeConverter
and overrides two
methods: CanConvertFrom()
and ConvertFrom()
. The
first method returns a bool
indicating whether this converter
can manage conversions from the specified type. This converter is intended
to be used in model binding, so it is designed to convert from strings. The
ConvertFrom
method contains the code that tests whether the
input is a string, and if so, uses the Week.TryParse
method to
attempt to return a new Week
instance.
Finally, you need to register the WeekConverter
. You do this
by applying the TypeConverterAttribute
to the Week
class:
[TypeConverter(typeof(WeekConverter))] public class Week { ... }
Note that this attribute is not applied to the PageModel property like
the ModelBinder
attribute. It must be applied to the class
definition. There is an
open issue on
Github that will enable the declaration of the
TypeConverterAttribute
on model properties and parameters, but in the
meantime, it is not supported as part of the model binding infrastructure. The PageModel property has the BindProperty
and
DataType
attributes applied:
[BindProperty, DataType("week")] public Week CustomWeek { get; set; }
Now the TypeConverter
takes care of assigning the posted
value from the input to the PageModel property:
Summary
As I mentioned at the beginning, the recommendation is to use a
TypeConverter
when you need to map a single value in a request to a
complex object. Doing so does not have to be complex at all, as this article
shows. Since type converters are used by the model binding system anyway,
once you have implemented your own and configured it on the class, it "just
works".