diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs index 4ff93159c0..1bef51ea2e 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs @@ -6,8 +6,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { public class AbpDynamicFormTagHelper : AbpTagHelper { + [HtmlAttributeName("asp-model")] public ModelExpression Model { get; set; } - [HtmlAttributeNotBound] [ViewContext] diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index 3f8889a477..1e005209a8 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Rendering; @@ -13,11 +14,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { private readonly HtmlEncoder _htmlEncoder; private readonly AbpInputTagHelper _abpInputTagHelper; + private readonly AbpSelectTagHelper _abpSelectTagHelper; - public AbpDynamicFormTagHelperService(HtmlEncoder htmlEncoder, AbpInputTagHelper abpInputTagHelper) + public AbpDynamicFormTagHelperService(HtmlEncoder htmlEncoder, AbpInputTagHelper abpInputTagHelper, AbpSelectTagHelper abpSelectTagHelper) { _htmlEncoder = htmlEncoder; _abpInputTagHelper = abpInputTagHelper; + _abpSelectTagHelper = abpSelectTagHelper; } public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) @@ -45,13 +48,24 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form foreach (var model in models) { + if (IsSelectGroup(context, model, out var selectItems)) + { + ProcessSelectGroup(context, model, selectItems); + continue; + } + ProcessInputGroup(context, model); } } - protected virtual List GetModels(TagHelperContext context, TagHelperOutput output) + protected virtual void ProcessSelectGroup(TagHelperContext context, ModelExpression model, IEnumerable selectItems) { - return ExploreModelsRecursively(new List(), TagHelper.Model.ModelExplorer); + _abpSelectTagHelper.AspFor = model; + _abpSelectTagHelper.AspItems = selectItems; + _abpSelectTagHelper.Label = ""; + _abpSelectTagHelper.ViewContext = TagHelper.ViewContext; + + RenderTagHelper(new TagHelperAttributeList(), context, _abpSelectTagHelper, _htmlEncoder, "div", TagMode.StartTagAndEndTag); } protected virtual void ProcessInputGroup(TagHelperContext context, ModelExpression model) @@ -63,6 +77,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form RenderTagHelper(new TagHelperAttributeList(), context, _abpInputTagHelper, _htmlEncoder, "div", TagMode.StartTagAndEndTag); } + protected virtual List GetModels(TagHelperContext context, TagHelperOutput output) + { + return TagHelper.Model.ModelExplorer.Properties.Aggregate(new List(), ExploreModelsRecursively); + } + protected virtual List ExploreModelsRecursively(List list, ModelExplorer model) { if (IsCsharpClassOrPrimitive(model.ModelType)) @@ -71,6 +90,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return list; } + if (IsListOfSelectItem(model.ModelType)) + { + return list; + } + return model.Properties.Aggregate(list, ExploreModelsRecursively); } @@ -78,13 +102,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { var temp = explorer; var propertyName = explorer.Metadata.PropertyName; - + while (temp?.Container?.Metadata?.PropertyName != null) { temp = temp.Container; propertyName = temp.Metadata.PropertyName + "." + propertyName; } - + return new ModelExpression(propertyName, explorer); } @@ -94,17 +118,52 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form type.IsValueType || type == typeof(DateTime) || type == typeof(ValueType) || - type == typeof(string) || - type == typeof(decimal) || - type == typeof(double) || + type == typeof(String) || + type == typeof(Decimal) || + type == typeof(Double) || type == typeof(Guid) || + type == typeof(Char) || + type == typeof(Byte) || + type == typeof(Boolean) || type == typeof(TimeSpan) || type == typeof(DateTimeOffset) || + type == typeof(Int16) || + type == typeof(Int32) || + type == typeof(Int64) || + type == typeof(ushort) || + type == typeof(uint) || + type == typeof(ulong) || type == typeof(float) || - type == typeof(short) || - type == typeof(int) || - type == typeof(long) || type.IsEnum; } + + protected virtual bool IsListOfSelectItem(Type type) + { + return type == typeof(List) || type == typeof(IEnumerable); + } + + protected virtual bool IsSelectGroup(TagHelperContext context, ModelExpression model, out IEnumerable selectItems) + { + return IsEnum(model.ModelExplorer, out selectItems) || AreSelectItemsProvided(model.ModelExplorer, out selectItems); + } + + protected virtual bool IsEnum(ModelExplorer explorer, out IEnumerable selectItems) + { + selectItems = explorer.Metadata.IsEnum ? GetSelectItemsFromEnum(explorer.ModelType) : null; + return explorer.Metadata.IsEnum; + } + + protected virtual IEnumerable GetSelectItemsFromEnum(Type enumType) + { + return enumType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) + .Select((t, i) => new SelectListItem { Value = i.ToString(), Text = t.Name }).ToList(); + } + + protected virtual bool AreSelectItemsProvided(ModelExplorer explorer, out IEnumerable selectItems) + { + selectItems = GetAttribute(explorer)?.GetItems(explorer); + + return selectItems != null; + } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 7e255a4847..d44f74c8e1 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -52,11 +52,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string GetContent(string label, string inputHtml, bool isCheckbox) { - var content = isCheckbox ? + var innerContent = isCheckbox ? inputHtml + Environment.NewLine + label : label + Environment.NewLine + inputHtml; - return "
" + Environment.NewLine + content + Environment.NewLine + "
"; + return "
" + Environment.NewLine + innerContent + Environment.NewLine + "
"; } protected virtual TagHelperOutput GetInputTag(TagHelperContext context) diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs index 27ad072108..e0bd59792d 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text.Encodings.Web; using Localization.Resources.AbpUi; @@ -24,24 +25,34 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form public override void Process(TagHelperContext context, TagHelperOutput output) { - output.TagName = "div"; - SetDivAttributes(output); - output.TagMode = TagMode.StartTagAndEndTag; - var selectTag = GetSelectTag(context); var seelctAsHtml = RenderTagHelperOutput(selectTag, _encoder); var labelAsHtml = GetLabelAsHtml(selectTag); - var content = labelAsHtml + Environment.NewLine + seelctAsHtml; + var content = GetContent(labelAsHtml,seelctAsHtml); + + var order = GetAttribute(TagHelper.AspFor.ModelExplorer); + var list = context.Items["InputGroupContents"] as List; + + if (list != null && !list.Any(igc => igc.Html.Contains("id=\"" + TagHelper.AspFor.Name.Replace('.', '_') + "\""))) + { + list.Add(new InputGroupContent + { + Html = content, + Order = order?.Number ?? 0 + }); + } - output.Content.SetHtmlContent(content); + output.SuppressOutput(); } - protected virtual void SetDivAttributes(TagHelperOutput output) + + + protected virtual string GetContent(string label, string inputHtml) { - output.Attributes.RemoveAll("asp-for"); - output.Attributes.RemoveAll("asp-items"); - output.Attributes.Add("class", "form-group"); + var innerContent = label + Environment.NewLine + inputHtml; + + return "
" + Environment.NewLine + innerContent + Environment.NewLine + "
"; } protected virtual TagHelperOutput GetSelectTag(TagHelperContext context) diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectItems.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectItems.cs index b1e29b1a32..ef4e708c65 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectItems.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectItems.cs @@ -13,41 +13,15 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { public string ItemsListPropertyName { get; set; } - public Type EnumType { get; set; } - public SelectType SelectType { get; set; } = SelectType.Dropdown; public IEnumerable GetItems(ModelExplorer explorer) { - if (IsEnumItem()) - { - return GetItemsFromEnum(); - } - else - { - return GetItemsFromListField(explorer); - } - } - - private IEnumerable GetItemsFromListField(ModelExplorer explorer) - { - var properties = explorer.Properties.Where(p => p.Metadata.PropertyName.Equals(ItemsListPropertyName)).ToList(); + var properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName.Equals(ItemsListPropertyName)).ToList(); return properties.Count > 0 ? properties.First().Model as IEnumerable - : new List(); - } - - private IEnumerable GetItemsFromEnum() - { - var enumItems = EnumType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static); - - return enumItems.Select((t, i) => new SelectListItem() { Value = i.ToString(), Text = t.Name }).ToList(); - } - - private bool IsEnumItem() - { - return EnumType != null && EnumType.GetTypeInfo().IsEnum; + : null; } } } diff --git a/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml b/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml index 532c283118..8df7bad9cf 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml +++ b/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml @@ -1,8 +1,9 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components -@model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.FormsModel +@model FormsModel + @{ - ViewData["Title"] = "Forms"; + ViewData["Title"] = "Forms"; }

Forms

@@ -13,11 +14,8 @@
- + - - -
@@ -31,36 +29,36 @@ @*

# Input Group Example

-
-
-
- - - - - - - - - - -
-
-
       
-        <form method="post" action="#">
-            <abp-input asp-for="Model.Name"/>
-            <abp-input asp-for="Model.Password" label="Password"/>
-            <abp-input asp-for="Model.IsActive" />
-            <abp-input asp-for="Model.PhoneNumber" />
-            <abp-input asp-for="Model.EmailAddress" />
-            <abp-input asp-for="Model.Count" />
-            <abp-input asp-for="Model.Day" />
-            <abp-select asp-for="Model.Country" asp-items="Model.Countries" label="Country"></abp-select>
-            <abp-select asp-for="Model.City" asp-items="Html.GetEnumSelectList(typeof(Cities))"></abp-select>
-        </form>
-        
-
-
*@ +
+
+
+ + + + + + + + + + +
+
+
+            <form method="post" action="#">
+                <abp-input asp-for="Model.Name"/>
+                <abp-input asp-for="Model.Password" label="Password"/>
+                <abp-input asp-for="Model.IsActive" />
+                <abp-input asp-for="Model.PhoneNumber" />
+                <abp-input asp-for="Model.EmailAddress" />
+                <abp-input asp-for="Model.Count" />
+                <abp-input asp-for="Model.Day" />
+                <abp-select asp-for="Model.Country" asp-items="Model.Countries" label="Country"></abp-select>
+                <abp-select asp-for="Model.City" asp-items="Html.GetEnumSelectList(typeof(Cities))"></abp-select>
+            </form>
+            
+
+
*@ @*
diff --git a/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml.cs b/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml.cs index 0682045d01..e0d49d5ebd 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Forms.cshtml.cs @@ -11,52 +11,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components { public class FormsModel : PageModel { - //[Required] - //[DisplayName("Name")] - //public string Name { get; set; } - - //[Required] - //[EmailAddress] - //[DisplayName("Email")] - //public string EmailAddress { get; set; } - - //[DataType(DataType.Password)] - //public string Password { get; set; } = "MyPass"; - - //[Phone] - //[DisplayName("Phone Number")] - //[DisplayOrder(4)] - //public string PhoneNumber { get; set; } - - //[DisplayName("Count")] - //public int Count { get; set; } - - //[DataType(DataType.Date)] - //[DisplayName("Day")] - //public DateTime Day { get; set; } - - //[DisplayName("Is Active")] - //public bool IsActive { get; set; } - - //[DisplayName("Country")] - //public string Country { get; set; } - - //[DisplayName("City")] - //public Cities City { get; set; } - - public Person Person { get; set; } - - public List Countries { get; } = new List - { - new SelectListItem { Value = "MX", Text = "Mexico" }, - new SelectListItem { Value = "CA", Text = "Canada" }, - new SelectListItem { Value = "US", Text = "USA" }, - }; - - public static IEnumerable EnumCityList; + public PersonViewModel Ahmet { get; set; } = new PersonViewModel(); public void OnGet() { + Ahmet = new PersonViewModel + { + Name = "ahmet", + Age = 65, + Phone = new Phone {Number = "326346231",Name = "MyPhone"} + }; } } @@ -67,11 +31,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components Moscow } - public class Person + public class PersonViewModel { [Required] [DisplayName("Name")] - public string Name { get; set; } + public string Name { get; set; } = "MyName"; [Required] [DisplayOrder(61)] @@ -93,7 +57,15 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public bool IsActive { get; set; } [DisplayName("Country")] + [SelectItems(ItemsListPropertyName = nameof(Countries))] public string Country { get; set; } + + public List Countries { get; set; } = new List + { + new SelectListItem { Value = "MX", Text = "Mexico" }, + new SelectListItem { Value = "CA", Text = "Canada" }, + new SelectListItem { Value = "US", Text = "USA" }, + }; } public class Phone