diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
index 15a8b0819c..54e9712d30 100644
--- a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
+++ b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
@@ -58,49 +58,49 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
- `[TextArea()]`: Converts the input into a text area.
-* `[Placeholder()]`: Sets placeholder for input. You can use a localization key directly.
-* `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
-* `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
+* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
+* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
+* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
-* `[DisabledInput]` : Input is disabled.
-* `[ReadOnlyInput]`: Input is read-only.
+* `[DisabledInput]` : Sets the input as disabled.
+* `[ReadOnlyInput]`: Sets the input as read-only.
#### Tag Attributes
-* `info`: Sets a small info text for input. You can use a localization key directly.
-* `auto-focus`: If true, browser auto focuses on the element.
-* `size`: Sets size of form-control wrapper element. Available values are
+* `info`: Sets text for the input. You can directly use a localization key.
+* `auto-focus`: It lets the browser set focus to the element when its value is true.
+* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
-* `disabled`: Input is disabled.
-* `readonly`: Input is read-only.
-* `label`: Sets the label for input.
-* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
+* `disabled`: Sets the input as disabled.
+* `readonly`: Sets the input as read-only.
+* `label`: Sets the label of input.
+* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
-You can set label of your input in different ways:
+You can set the label of the input in several ways:
-- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value"`.
-- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
+- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
+- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys, if `label` or `[DisplayName]` attributes are not set.
## abp-select
-`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`.
+`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.)
@@ -170,14 +170,14 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
* `[SelectItems()]`: Sets the select data. Parameter should be the name of the data list. (see example above)
-- `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
-- `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
+- `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
+- `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
@@ -186,21 +186,21 @@ You can set some of the attributes on your c# property, or directly on html tag.
#### Tag Attributes
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
-- `info`: Sets a small info text for input. You can use a localization key directly.
-- `size`: Sets size of form-control wrapper element. Available values are
+- `info`: Sets text for the input. You can directly use a localization key.
+- `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
-- `label`: Sets the label for input.
-- `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
+- `label`: Sets the label of input.
+- `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
### Label & Localization
-You can set label of your input in different ways:
+You can set the label of the input in several ways:
- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value".`
-- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
+- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys.
Localizations of combobox values are set by `abp-select` for **Enum** property. It searches for "{EnumTypeName}.{EnumPropertyName}" or "{EnumPropertyName}" localization keys. For instance, in the example above, it will use "CarType.StationWagon" or "StationWagon" keys for localization when it localizes combobox values.
@@ -251,7 +251,7 @@ Model:
### Attributes
-You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@@ -261,3 +261,172 @@ You can set some of the attributes on your c# property, or directly on html tag.
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other.
+
+## abp-date-picker & abp-date-range-picker
+
+`abp-date-picker` and `abp-date-range-picker` tags creates a Bootstrap form date picker for a given c# property. `abp-date-picker` is for single date selection, `abp-date-range-picker` is for date range selection. It uses [datepicker](https://www.daterangepicker.com/) jQuery plugin.
+
+Usage:
+
+````xml
+
+
+
+````
+
+Model:
+
+````csharp
+ public class FormElementsModel : PageModel
+ {
+ public SampleModel MyModel { get; set; }
+
+ public DynamicForm DynamicFormExample { get; set; }
+
+ public void OnGet()
+ {
+ MyModel = new SampleModel();
+
+ DynamicFormExample = new DynamicForm();
+ }
+
+ public class SampleModel
+ {
+ public DateTime MyDate { get; set; }
+
+ public DateTime MyDateRangeStart { get; set; }
+
+ public DateTime MyDateRangeEnd { get; set; }
+ }
+
+ public class DynamicForm
+ {
+ [DateRangePicker("MyPicker",true)]
+ public DateTime StartDate { get; set; }
+
+ [DateRangePicker("MyPicker",false)]
+ [DatePickerOptions(nameof(DatePickerOptions))]
+ public DateTime EndDate { get; set; }
+
+ public DateTime DateTime { get; set; }
+
+ public DynamicForm()
+ {
+ StartDate = DateTime.Now;
+ EndDate = DateTime.Now;
+ DateTime = DateTime.Now;
+ }
+ }
+
+ public AbpDatePickerOptions DatePickerOptions { get; set; }
+ }
+````
+
+### Attributes
+
+You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
+
+#### Property Attributes
+
+* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
+* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
+* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
+ - `AbpFormControlSize.Default`
+ - `AbpFormControlSize.Small`
+ - `AbpFormControlSize.Medium`
+ - `AbpFormControlSize.Large`
+* `[DisabledInput]` : Sets the input as disabled.
+* `[ReadOnlyInput]`: Sets the input as read-only.
+- `[DatePickerOptions()]`: Sets the predefined options of the date picker. Parameter should be the name of the options property (see example above). See the available [datepicker options](https://www.daterangepicker.com/#options). You can use a localization key directly.
+
+##### abp-date-picker
+`[DatePicker]` : Sets the input as datepicker. Especially for string properties.
+
+##### abp-date-range-picker
+`[DateRangePicker()]` : Sets the picker id for the date range picker. You can set the property as a start date by setting IsStart=true or leave it as default/false to set it as an end date.
+
+#### Tag Attributes
+
+* `info`: Sets text for the input. You can directly use a localization key.
+* `auto-focus`: It lets the browser set focus to the element when its value is true.
+* `size`: Sets the size of the form-control wrapper element. Available values are
+ - `AbpFormControlSize.Default`
+ - `AbpFormControlSize.Small`
+ - `AbpFormControlSize.Medium`
+ - `AbpFormControlSize.Large`
+* `disabled`: Sets the input as disabled.
+* `readonly`: Sets the input as read-only.
+* `label`: Sets the label of input.
+* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
+* `open-button`: A button to open the datepicker will be added when its `True`. The default value is `True`.
+* `clear-button`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
+* `single-open-and-clear-button`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
+* `is-utc`: Converts the date to UTC when its `True`. The default value is `False`.
+* `is-iso`: Converts the date to ISO format when its `True`. The default value is `False`.
+* `date-format`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
+* `date-separator`: Sets a character to separate start and end dates. The default value is `-`
+* Other non-mapped attributes will be automatically added to the input element as is. See the available [datepicker options](https://www.daterangepicker.com/#options). Eg: `data-start-date="2020-01-01"`
+
+##### abp-date-picker
+
+* `asp-date`: Sets the date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
+
+##### abp-date-range-picker
+
+* `asp-for-start`: Sets the start date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
+* `asp-for-end`: Sets the end date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
+
+### Label & Localization
+
+You can set the label of the input in several ways:
+
+- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
+- You can set it using `[Display(name="{LocalizationKey}")]` attribute ASP.NET Core.
+- You can just let **abp** find the localization key for the property. If the `label` or `[DisplayName]` attributes are not set, it tries to find the localization by convention. For example `DisplayName:{YourPropertyName}` or `{YourPropertyName}`. If your property name is FullName, it will search for `DisplayName:FullName` or `{FullName}`.
+
+### JavaScript Usage
+
+````javascript
+var newPicker = abp.libs.bootstrapDateRangePicker.createDateRangePicker(
+ {
+ label: "New Picker",
+ }
+);
+newPicker.insertAfter($('body'));
+````
+
+````javascript
+var newPicker = abp.libs.bootstrapDateRangePicker.createSinglePicker(
+ {
+ label: "New Picker",
+ }
+);
+newPicker.insertAfter($('body'));
+````
+
+#### Options
+
+* `label`: Sets the label of the input.
+* `placeholder`: Sets the placeholder of the input.
+* `value`: Sets the value of the input.
+* `name`: Sets the name of the input.
+* `id`: Sets the id of the input.
+* `required`: Sets the input as required.
+* `disabled`: Sets the input as disabled.
+* `readonly`: Sets the input as read-only.
+* `size`: Sets the size of the form-control wrapper element. Available values are
+ - `AbpFormControlSize.Default`
+ - `AbpFormControlSize.Small`
+ - `AbpFormControlSize.Medium`
+ - `AbpFormControlSize.Large`
+* `openButton`: A button to open the datepicker will be added when its `True`. The default value is `True`.
+* `clearButton`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
+* `singleOpenAndClearButton`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
+* `isUtc`: Converts the date to UTC when its `True`. The default value is `False`.
+* `isIso`: Converts the date to ISO format when its `True`. The default value is `False`.
+* `dateFormat`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
+* `dateSeparator`: Sets a character to separate start and end dates. The default value is `-`.
+* `startDateName`: Sets the name of the hidden start date input.
+* `endDateName`: Sets the name of the hidden end date input.
+* `dateName`: Sets the name of the hidden date input.
+* Other [datepicker options](https://www.daterangepicker.com/#options). Eg: `startDate: "2020-01-01"`.
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
index 38f55ca04e..775b19beb1 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
@@ -14,6 +14,7 @@ using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
+using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
@@ -144,6 +145,10 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService();
+ abpDateInputTagHelper.AspFor = model;
+ abpDateInputTagHelper.ViewContext = TagHelper.ViewContext;
+ abpDateInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true;
+ return abpDateInputTagHelper;
+ }
+
+ private AbpTagHelper GetAbpDateRangeInputTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
+ {
+ var modelAttribute = model.ModelExplorer.GetAttribute();
+
+ var pickerId = modelAttribute.PickerId;
+
+ var abpDateRangeInputTagHelper = _serviceProvider.GetRequiredService();
+ abpDateRangeInputTagHelper.PickerId = pickerId;
+ abpDateRangeInputTagHelper.ViewContext = TagHelper.ViewContext;
+
+ if (modelAttribute.IsStart)
+ {
+ abpDateRangeInputTagHelper.AspForStart = model;
+
+ var otherModelExists = TryToGetOtherDateModel(model, pickerId, out var otherModel);
+ if (otherModelExists && otherModel.GetAttribute().IsEnd)
+ {
+ abpDateRangeInputTagHelper.AspForEnd = ModelExplorerToModelExpressionConverter(otherModel);
+ }
+ }
+
+ return abpDateRangeInputTagHelper;
+ }
+
+ private bool TryToGetOtherDateModel(ModelExpression model, string pickerId, out ModelExplorer otherModel)
+ {
+ otherModel = TagHelper.Model.ModelExplorer.Properties.SingleOrDefault(x => x != model.ModelExplorer && x.GetAttribute()?.PickerId == pickerId);
+ return otherModel != null;
+ }
+
+ private static bool IsDateRangeGroup(ModelExplorer modelModelExplorer)
+ {
+ return modelModelExplorer.GetAttribute() != null;
+ }
+
protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List items)
{
var models = GetModels(context, output);
@@ -294,6 +357,25 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService() != null ||
+ model.ModelExplorer.GetAttribute() != null)
+ {
+ return true;
+ }
+
+ if (model.Metadata.ModelType == typeof(DateTime) ||
+ model.Metadata.ModelType == typeof(DateTime?) ||
+ model.Metadata.ModelType == typeof(DateTimeOffset) ||
+ model.Metadata.ModelType == typeof(DateTimeOffset?))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
protected virtual bool IsEnum(ModelExplorer explorer)
{
return explorer.Metadata.IsEnum;
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
index 0a3d1e1c08..a3cecfa471 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
@@ -294,8 +294,10 @@ public class AbpInputTagHelperService : AbpTagHelperService
{
return "";
}
+
+ var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
- return TagHelper.AspFor.ModelExplorer.GetAttribute() != null ? " * " : "";
+ return TagHelper.AspFor.ModelExplorer.GetAttribute() != null || isHaveRequiredAttribute ? " * " : "";
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
index 73a49ea82d..f7fa9802ad 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
@@ -197,8 +197,10 @@ public class AbpSelectTagHelperService : AbpTagHelperService
{
return "";
}
+
+ var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
- return TagHelper.AspFor.ModelExplorer.GetAttribute() != null ? " * " : "";
+ return TagHelper.AspFor.ModelExplorer.GetAttribute() != null || isHaveRequiredAttribute ? " * " : "";
}
protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs
new file mode 100644
index 0000000000..10923f25af
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Razor.TagHelpers;
+
+namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
+
+public abstract class
+ AbpDatePickerBaseTagHelper : AbpTagHelper>, IAbpDatePickerOptions
+ where TTagHelper : AbpDatePickerBaseTagHelper
+
+{
+ private readonly IAbpDatePickerOptions _abpDatePickerOptionsImplementation;
+
+ public string Label { get; set; }
+
+ public string LabelTooltip { get; set; }
+
+ public string LabelTooltipIcon { get; set; } = "bi-info-circle";
+
+ public string LabelTooltipPlacement { get; set; } = "right";
+
+ public bool LabelTooltipHtml { get; set; } = false;
+
+ [HtmlAttributeName("info")]
+ public string InfoText { get; set; }
+
+ [HtmlAttributeName("disabled")]
+ public bool IsDisabled { get; set; } = false;
+
+ [HtmlAttributeName("readonly")]
+ public bool? IsReadonly { get; set; } = false;
+
+ public bool AutoFocus { get; set; }
+
+ public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default;
+
+ [HtmlAttributeName("required-symbol")]
+ public bool DisplayRequiredSymbol { get; set; } = true;
+
+ public string Name { get; set; }
+
+ public string Value { get; set; }
+
+ public bool SuppressLabel { get; set; }
+
+ protected AbpDatePickerBaseTagHelper(AbpDatePickerBaseTagHelperService service) : base(service)
+ {
+ _abpDatePickerOptionsImplementation = new AbpDatePickerOptions();
+ }
+
+ public string PickerId {
+ get => _abpDatePickerOptionsImplementation.PickerId;
+ set => _abpDatePickerOptionsImplementation.PickerId = value;
+ }
+
+ public DateTime? MinDate {
+ get => _abpDatePickerOptionsImplementation.MinDate;
+ set => _abpDatePickerOptionsImplementation.MinDate = value;
+ }
+
+ public DateTime? MaxDate {
+ get => _abpDatePickerOptionsImplementation.MaxDate;
+ set => _abpDatePickerOptionsImplementation.MaxDate = value;
+ }
+
+ public object MaxSpan {
+ get => _abpDatePickerOptionsImplementation.MaxSpan;
+ set => _abpDatePickerOptionsImplementation.MaxSpan = value;
+ }
+
+ public bool? ShowDropdowns {
+ get => _abpDatePickerOptionsImplementation.ShowDropdowns;
+ set => _abpDatePickerOptionsImplementation.ShowDropdowns = value;
+ }
+
+ public int? MinYear {
+ get => _abpDatePickerOptionsImplementation.MinYear;
+ set => _abpDatePickerOptionsImplementation.MinYear = value;
+ }
+
+ public int? MaxYear {
+ get => _abpDatePickerOptionsImplementation.MaxYear;
+ set => _abpDatePickerOptionsImplementation.MaxYear = value;
+ }
+
+ public AbpDatePickerWeekNumbers WeekNumbers {
+ get => _abpDatePickerOptionsImplementation.WeekNumbers;
+ set => _abpDatePickerOptionsImplementation.WeekNumbers = value;
+ }
+
+ public bool? TimePicker {
+ get => _abpDatePickerOptionsImplementation.TimePicker;
+ set => _abpDatePickerOptionsImplementation.TimePicker = value;
+ }
+
+ public int? TimePickerIncrement {
+ get => _abpDatePickerOptionsImplementation.TimePickerIncrement;
+ set => _abpDatePickerOptionsImplementation.TimePickerIncrement = value;
+ }
+
+ public bool? TimePicker24Hour {
+ get => _abpDatePickerOptionsImplementation.TimePicker24Hour;
+ set => _abpDatePickerOptionsImplementation.TimePicker24Hour = value;
+ }
+
+ public bool? TimePickerSeconds {
+ get => _abpDatePickerOptionsImplementation.TimePickerSeconds;
+ set => _abpDatePickerOptionsImplementation.TimePickerSeconds = value;
+ }
+
+ public List Ranges {
+ get => _abpDatePickerOptionsImplementation.Ranges;
+ set => _abpDatePickerOptionsImplementation.Ranges = value;
+ }
+
+ public bool? ShowCustomRangeLabel {
+ get => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel;
+ set => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel = value;
+ }
+
+ public bool? AlwaysShowCalendars {
+ get => _abpDatePickerOptionsImplementation.AlwaysShowCalendars;
+ set => _abpDatePickerOptionsImplementation.AlwaysShowCalendars = value;
+ }
+
+ public AbpDatePickerOpens Opens {
+ get => _abpDatePickerOptionsImplementation.Opens;
+ set => _abpDatePickerOptionsImplementation.Opens = value;
+ }
+
+ public AbpDatePickerDrops Drops {
+ get => _abpDatePickerOptionsImplementation.Drops;
+ set => _abpDatePickerOptionsImplementation.Drops = value;
+ }
+
+ public string ButtonClasses {
+ get => _abpDatePickerOptionsImplementation.ButtonClasses;
+ set => _abpDatePickerOptionsImplementation.ButtonClasses = value;
+ }
+
+ public string TodayButtonClasses {
+ get => _abpDatePickerOptionsImplementation.TodayButtonClasses;
+ set => _abpDatePickerOptionsImplementation.TodayButtonClasses = value;
+ }
+
+ public string ApplyButtonClasses {
+ get => _abpDatePickerOptionsImplementation.ApplyButtonClasses;
+ set => _abpDatePickerOptionsImplementation.ApplyButtonClasses = value;
+ }
+
+ public string ClearButtonClasses {
+ get => _abpDatePickerOptionsImplementation.ClearButtonClasses;
+ set => _abpDatePickerOptionsImplementation.ClearButtonClasses = value;
+ }
+
+ public object Locale {
+ get => _abpDatePickerOptionsImplementation.Locale;
+ set => _abpDatePickerOptionsImplementation.Locale = value;
+ }
+
+ public bool? AutoApply {
+ get => _abpDatePickerOptionsImplementation.AutoApply;
+ set => _abpDatePickerOptionsImplementation.AutoApply = value;
+ }
+
+ public bool? LinkedCalendars {
+ get => _abpDatePickerOptionsImplementation.LinkedCalendars;
+ set => _abpDatePickerOptionsImplementation.LinkedCalendars = value;
+ }
+
+ public bool? AutoUpdateInput {
+ get => _abpDatePickerOptionsImplementation.AutoUpdateInput;
+ set => _abpDatePickerOptionsImplementation.AutoUpdateInput = value;
+ }
+
+ public string ParentEl {
+ get => _abpDatePickerOptionsImplementation.ParentEl;
+ set => _abpDatePickerOptionsImplementation.ParentEl = value;
+ }
+
+ public string DateFormat {
+ get => _abpDatePickerOptionsImplementation.DateFormat;
+ set => _abpDatePickerOptionsImplementation.DateFormat = value;
+ }
+
+ public bool OpenButton {
+ get => _abpDatePickerOptionsImplementation.OpenButton;
+ set => _abpDatePickerOptionsImplementation.OpenButton = value;
+ }
+
+ public bool ClearButton {
+ get => _abpDatePickerOptionsImplementation.ClearButton;
+ set => _abpDatePickerOptionsImplementation.ClearButton = value;
+ }
+
+ public bool SingleOpenAndClearButton {
+ get => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton;
+ set => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton = value;
+ }
+
+ public bool? IsUtc {
+ get => _abpDatePickerOptionsImplementation.IsUtc;
+ set => _abpDatePickerOptionsImplementation.IsUtc = value;
+ }
+
+ public bool? IsIso {
+ get => _abpDatePickerOptionsImplementation.IsIso;
+ set => _abpDatePickerOptionsImplementation.IsIso = value;
+ }
+
+ public object Options {
+ get => _abpDatePickerOptionsImplementation.Options;
+ set => _abpDatePickerOptionsImplementation.Options = value;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs
new file mode 100644
index 0000000000..82273097cf
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs
@@ -0,0 +1,712 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Localization.Resources.AbpUi;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.AspNetCore.Mvc.TagHelpers;
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Microsoft.AspNetCore.Razor.TagHelpers;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Localization;
+using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
+using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;
+using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
+using Volo.Abp.Json;
+
+namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
+
+public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelperService
+ where TTagHelper : AbpDatePickerBaseTagHelper
+{
+ protected readonly Dictionary> SupportedInputTypes = new() {
+ {typeof(string), o => DateTime.Parse((string)o).ToString("O")},
+ {typeof(DateTime), o => ((DateTime) o).ToString("O")},
+ {typeof(DateTime?), o => ((DateTime?) o)?.ToString("O")},
+ {typeof(DateTimeOffset), o => ((DateTimeOffset) o).ToString("O")},
+ {typeof(DateTimeOffset?), o => ((DateTimeOffset?) o)?.ToString("O")}
+ };
+
+ protected readonly IJsonSerializer JsonSerializer;
+ protected readonly IHtmlGenerator Generator;
+ protected readonly HtmlEncoder Encoder;
+ protected readonly IServiceProvider ServiceProvider;
+ protected readonly IAbpTagHelperLocalizer TagHelperLocalizer;
+ protected virtual string TagName { get; set; } = "abp-date-picker";
+ protected IStringLocalizer L { get; }
+ protected InputTagHelper InputTagHelper { get; set; }
+ protected abstract TagHelperOutput TagHelperOutput { get; set; }
+
+ protected AbpDatePickerBaseTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator,
+ HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer l,
+ IAbpTagHelperLocalizer tagHelperLocalizer)
+ {
+ JsonSerializer = jsonSerializer;
+ Generator = generator;
+ Encoder = encoder;
+ ServiceProvider = serviceProvider;
+ L = l;
+ TagHelperLocalizer = tagHelperLocalizer;
+
+ InputTagHelper = new InputTagHelper(Generator) { InputTypeName = "text" };
+ }
+
+ protected virtual T GetAttribute() where T : Attribute
+ {
+ return GetAttributeAndModelExpression(out _);
+ }
+
+ protected abstract T GetAttributeAndModelExpression(out ModelExpression modelExpression) where T : Attribute;
+
+
+ public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
+ {
+ TagHelperOutput = new TagHelperOutput("input", GetInputAttributes(context, output), (_, _) => Task.FromResult(new DefaultTagHelperContent()));
+
+ InputTagHelper.ViewContext = TagHelper.ViewContext;
+
+ if (!TagHelper.Name.IsNullOrEmpty())
+ {
+ InputTagHelper.Name = TagHelper.Name;
+ }
+
+ if (!TagHelper.Value.IsNullOrEmpty())
+ {
+ InputTagHelper.Value = TagHelper.Value;
+ }
+
+ AddDisabledAttribute(TagHelperOutput);
+ AddAutoFocusAttribute(TagHelperOutput);
+ AddFormControls(context, output, TagHelperOutput);
+ AddReadOnlyAttribute(TagHelperOutput);
+ AddPlaceholderAttribute(TagHelperOutput);
+ AddInfoTextId(TagHelperOutput);
+
+ // Open and close button
+ var openButtonContent = TagHelper.OpenButton
+ ? await ProcessButtonAndGetContentAsync(context, output, "calendar", "open")
+ : "";
+ var clearButtonContent = TagHelper.ClearButton
+ ? await ProcessButtonAndGetContentAsync(context, output, "times", "clear", visible:!TagHelper.SingleOpenAndClearButton)
+ : "";
+
+ var labelContent = await GetLabelAsHtmlAsync(context, output, TagHelperOutput);
+ var infoContent = GetInfoAsHtml(context, output, TagHelperOutput);
+ var validationContent = await GetValidationAsHtmlAsync(context, output);
+
+ var inputGroup = new TagHelperOutput("div",
+ new TagHelperAttributeList(new[] { new TagHelperAttribute("class", "input-group") }),
+ (_, _) => Task.FromResult(new DefaultTagHelperContent()));
+ inputGroup.Content.AppendHtml(
+ TagHelperOutput.Render(Encoder) + openButtonContent + clearButtonContent
+ );
+
+ var abpDatePickerTag = new TagHelperOutput(TagName, GetBaseTagAttributes(context, output, TagHelper),
+ (_, _) => Task.FromResult(new DefaultTagHelperContent()));
+ abpDatePickerTag.Content.AppendHtml(inputGroup.Render(Encoder));
+ abpDatePickerTag.Content.AppendHtml(validationContent);
+ abpDatePickerTag.Content.AppendHtml(GetExtraInputHtml(context, output));
+
+ var innerHtml = labelContent + abpDatePickerTag.Render(Encoder) + infoContent;
+
+ var order = GetOrder();
+
+ AddGroupToFormGroupContents(
+ context,
+ GetPropertyName(),
+ SurroundInnerHtmlAndGet(context, output, innerHtml),
+ order
+ );
+
+
+ output.TagMode = TagMode.StartTagAndEndTag;
+ output.TagName = "div";
+ LeaveOnlyGroupAttributes(context, output);
+ output.Attributes.AddClass("mb-3");
+
+ output.Content.AppendHtml(innerHtml);
+ }
+
+ protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput)
+ {
+ if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false &&
+ (TagHelper.IsReadonly != false || GetAttribute() != null))
+ {
+ inputTagHelperOutput.Attributes.Add("readonly", "");
+ }
+ }
+
+ protected virtual TagHelperAttributeList GetInputAttributes(TagHelperContext context, TagHelperOutput output)
+ {
+ var groupPrefix = "group-";
+
+ var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList();
+
+ var attrList = new TagHelperAttributeList();
+
+ foreach (var tagHelperAttribute in tagHelperAttributes)
+ {
+ attrList.Add(tagHelperAttribute);
+ }
+
+ attrList.Add("type", "text");
+
+ if (attrList.ContainsName("value"))
+ {
+ attrList.Remove(attrList.First(a => a.Name == "value"));
+ }
+
+ if (!TagHelper.Name.IsNullOrEmpty() && !attrList.ContainsName("name"))
+ {
+ attrList.Add("name", TagHelper.Name);
+ }
+
+ if (!attrList.ContainsName("autocomplete"))
+ {
+ attrList.Add("autocomplete", "off");
+ }
+
+ return attrList;
+ }
+ protected virtual void LeaveOnlyGroupAttributes(TagHelperContext context, TagHelperOutput output)
+ {
+ var groupPrefix = "group-";
+ var tagHelperAttributes = output.Attributes.Where(a => a.Name.StartsWith(groupPrefix)).ToList();
+
+ output.Attributes.Clear();
+
+ foreach (var tagHelperAttribute in tagHelperAttributes)
+ {
+ var nameWithoutPrefix = tagHelperAttribute.Name.Substring(groupPrefix.Length);
+ var newAttribute = new TagHelperAttribute(nameWithoutPrefix, tagHelperAttribute.Value);
+ output.Attributes.Add(newAttribute);
+ }
+ }
+
+ protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml)
+ {
+ return "