diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs index 6c5e0c14a1..81160e28b6 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs @@ -1,13 +1,24 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; +using Volo.Abp.Threading; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers { public abstract class AbpTagHelperService : IAbpTagHelperService where TTagHelper : TagHelper { + protected const string FormGroupContents = "FormGroupContents"; + public TTagHelper TagHelper { get; set; } - + public virtual int Order { get; } public virtual void Init(TagHelperContext context) @@ -25,5 +36,78 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers Process(context, output); return Task.CompletedTask; } + + protected virtual TagHelperOutput GetInnerTagHelper(TagHelperAttributeList attributeList, TagHelperContext context, TagHelper tagHelper, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) + { + var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) + { + TagMode = tagMode + }; + + var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); + + if (runAsync) + { + AsyncHelper.RunSync(() => tagHelper.ProcessAsync(innerContext, innerOutput)); + } + else + { + tagHelper.Process(innerContext, innerOutput); + } + + return innerOutput; + } + + protected virtual string RenderTagHelper(TagHelperAttributeList attributeList, TagHelperContext context, TagHelper tagHelper, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) + { + var innerOutput = GetInnerTagHelper(attributeList, context, tagHelper, tagName, tagMode, runAsync); + + return RenderTagHelperOutput(innerOutput, htmlEncoder); + } + + protected virtual string RenderTagHelperOutput(TagHelperOutput output, HtmlEncoder htmlEncoder) + { + using (var writer = new StringWriter()) + { + output.WriteTo(writer, htmlEncoder); + return writer.ToString(); + } + } + + protected virtual T GetAttribute(ModelExplorer property) where T : Attribute + { + return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute(); + } + + protected virtual List GetFormGroupContentsList(TagHelperContext context) + { + return context.Items[FormGroupContents] as List; + } + + protected virtual string GetIdAttributeAsString(TagHelperOutput inputTag) + { + var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); + + return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; + } + + protected virtual int GetInputOrder(ModelExplorer explorer) + { + return GetAttribute(explorer)?.Number ?? 0; + } + + protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order) + { + var list = GetFormGroupContentsList(context); + + if (list != null && !list.Any(igc => igc.Html.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) + { + list.Add(new FormGroupContent + { + Html = html, + Order = order + }); + } + } } } \ No newline at end of file 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 new file mode 100644 index 0000000000..1bef51ea2e --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpDynamicFormTagHelper : AbpTagHelper + { + [HtmlAttributeName("asp-model")] + public ModelExpression Model { get; set; } + + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + + public AbpDynamicFormTagHelper(AbpDynamicFormTagHelperService tagHelperService) + : base(tagHelperService) + { + + } + } +} 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 new file mode 100644 index 0000000000..50dca5e705 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -0,0 +1,184 @@ +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; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpDynamicFormTagHelperService : AbpTagHelperService + { + private readonly HtmlEncoder _htmlEncoder; + private readonly AbpInputTagHelper _abpInputTagHelper; + private readonly AbpSelectTagHelper _abpSelectTagHelper; + + public AbpDynamicFormTagHelperService(HtmlEncoder htmlEncoder, AbpInputTagHelper abpInputTagHelper, AbpSelectTagHelper abpSelectTagHelper) + { + _htmlEncoder = htmlEncoder; + _abpInputTagHelper = abpInputTagHelper; + _abpSelectTagHelper = abpSelectTagHelper; + } + + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var list = InitilizeFormGroupContentsContext(context); + + await output.GetChildContentAsync(); + + ProcessFields(context, output); + + SetContent(output,list); + } + + protected virtual void SetContent(TagHelperOutput output, List list) + { + foreach (var itemConfig in list.OrderBy(o => o.Order)) + { + output.PostContent.SetHtmlContent(output.PostContent.GetContent() + itemConfig.Html); + } + } + + protected virtual void SetFormAttributes(TagHelperOutput output) + { + output.TagName = "form"; + output.Attributes.Add("method", "post"); + output.Attributes.Add("action", "#"); + } + + protected virtual List InitilizeFormGroupContentsContext(TagHelperContext context) + { + var list = new List(); + context.Items.Add(FormGroupContents, list); + return list; + } + + protected virtual void ProcessFields(TagHelperContext context, TagHelperOutput output) + { + var models = GetModels(context, output); + + foreach (var model in models) + { + if (IsSelectGroup(context, model, out var selectItems)) + { + ProcessSelectGroup(context, model, selectItems); + continue; + } + + ProcessInputGroup(context, model); + } + } + + protected virtual void ProcessSelectGroup(TagHelperContext context, ModelExpression model, IEnumerable selectItems) + { + _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) + { + _abpInputTagHelper.AspFor = model; + _abpInputTagHelper.Label = ""; + _abpInputTagHelper.ViewContext = TagHelper.ViewContext; + + 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)) + { + list.Add(ModelExplorerToModelExpressionConverter(model)); + + return list; + } + + if (IsListOfSelectItem(model.ModelType)) + { + return list; + } + + return model.Properties.Aggregate(list, ExploreModelsRecursively); + } + + protected virtual ModelExpression ModelExplorerToModelExpressionConverter(ModelExplorer explorer) + { + 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); + } + + protected virtual bool IsCsharpClassOrPrimitive(Type type) + { + return type.IsPrimitive || + type.IsValueType || + type == typeof(DateTime) || + type == typeof(ValueType) || + 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.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/AbpInputTagHelper.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs new file mode 100644 index 0000000000..a2f157be30 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpInputTagHelper : AbpTagHelper, ITransientDependency + { + public ModelExpression AspFor { get; set; } + + public string Label { get; set; } + + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + + public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) + : base(tagHelperService) + { + + } + } +} 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 new file mode 100644 index 0000000000..51b3cc9204 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.Encodings.Web; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.TagHelpers; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpInputTagHelperService : AbpTagHelperService + { + private readonly IHtmlGenerator _generator; + private readonly HtmlEncoder _encoder; + private readonly IStringLocalizer _localizer; + + public AbpInputTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IStringLocalizer localizer) + { + _generator = generator; + _encoder = encoder; + _localizer = localizer; + } + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + var html = GetFormInputGroupAsHtml(context, output); + + var order = GetInputOrder(TagHelper.AspFor.ModelExplorer); + + AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order); + + output.SuppressOutput(); + } + + protected virtual string GetFormInputGroupAsHtml(TagHelperContext context, TagHelperOutput output) + { + var inputTag = GetInputTag(context, out var isCheckbox); + var inputHtml = RenderTagHelperOutput(inputTag, _encoder); + var label = GetLabelAsHtml(inputTag, isCheckbox); + + var validation = isCheckbox ? "" : GetValidationAsHtml(context); + + return GetContent(label, inputHtml, validation, isCheckbox); + } + protected virtual string GetValidationAsHtml(TagHelperContext context) + { + var validationMessageTagHelper = new ValidationMessageTagHelper(_generator) + { + For = TagHelper.AspFor, + ViewContext = TagHelper.ViewContext + }; + + var attributeList = new TagHelperAttributeList { { "class", "text-danger" } }; + + return RenderTagHelper(attributeList, context, validationMessageTagHelper, _encoder, "span", TagMode.StartTagAndEndTag, true); + } + + protected virtual string GetContent(string label, string inputHtml, string validation, bool isCheckbox) + { + var innerContent = isCheckbox ? + inputHtml + Environment.NewLine + label : + label + Environment.NewLine + inputHtml; + + return "
" + + Environment.NewLine + innerContent + Environment.NewLine + + Environment.NewLine + validation + Environment.NewLine + + "
"; + } + + protected virtual TagHelperOutput GetInputTag(TagHelperContext context, out bool isCheckbox) + { + var inputTagHelper = new InputTagHelper(_generator) + { + For = TagHelper.AspFor, + ViewContext = TagHelper.ViewContext + }; + + var inputTagHelperOutput = GetInnerTagHelper(new TagHelperAttributeList(), context, inputTagHelper, "input"); + isCheckbox = IsInputCheckbox(inputTagHelperOutput.Attributes); + + inputTagHelperOutput.Attributes.Add("class", isCheckbox ? "form-check-input" : "form-control"); + + return inputTagHelperOutput; + } + + protected virtual bool IsInputCheckbox(TagHelperAttributeList attributes) + { + return attributes.Any(a => a.Value != null && a.Name == "type" && a.Value.ToString() == "checkbox"); + } + + protected virtual string GetLabelAsHtml(TagHelperOutput inputTag, bool isCheckbox) + { + if (string.IsNullOrEmpty(TagHelper.Label) && string.IsNullOrEmpty(TagHelper.AspFor.Metadata.DisplayName)) + { + return ""; + } + + var checkboxClass = isCheckbox ? "class=\"form-check-label\" " : ""; + + return ""; + } + + protected virtual string GetLabelValue() + { + return string.IsNullOrEmpty(TagHelper.Label) ? + TagHelper.AspFor.Metadata.DisplayName : + TagHelper.Label; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs new file mode 100644 index 0000000000..cf2786e726 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpSelectTagHelper : AbpTagHelper, ITransientDependency + { + public ModelExpression AspFor { get; set; } + + public string Label { get; set; } + + public IEnumerable AspItems { get; set; } + + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + + public AbpSelectTagHelper(AbpSelectTagHelperService tagHelperService) + : base(tagHelperService) + { + + } + } +} 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 new file mode 100644 index 0000000000..313b39fb67 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.TagHelpers; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpSelectTagHelperService : AbpTagHelperService + { + private readonly IHtmlGenerator _generator; + private readonly HtmlEncoder _encoder; + private readonly IStringLocalizer _localizer; + + public AbpSelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IStringLocalizer localizer) + { + _generator = generator; + _encoder = encoder; + _localizer = localizer; + } + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + var html = GetFormInputGroupAsHtml(context, output); + + var order = GetInputOrder(TagHelper.AspFor.ModelExplorer); + + AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order); + + output.SuppressOutput(); + } + + protected virtual string GetFormInputGroupAsHtml(TagHelperContext context, TagHelperOutput output) + { + var selectTag = GetSelectTag(context); + var selectAsHtml = RenderTagHelperOutput(selectTag, _encoder); + var label = GetLabelAsHtml(selectTag); + + return GetContent(label, selectAsHtml); + } + + protected virtual string GetContent(string label, string inputHtml) + { + var innerContent = label + Environment.NewLine + inputHtml; + + return "
" + Environment.NewLine + innerContent + Environment.NewLine + "
"; + } + + protected virtual TagHelperOutput GetSelectTag(TagHelperContext context) + { + var selectTagHelper = new SelectTagHelper(_generator) + { + For = TagHelper.AspFor, + Items = TagHelper.AspItems, + ViewContext = TagHelper.ViewContext + }; + + var inputTagHelperOutput = GetInnerTagHelper(new TagHelperAttributeList(), context,selectTagHelper, "select", TagMode.StartTagAndEndTag); ; + + inputTagHelperOutput.Attributes.Add("class", "form-control"); + + return inputTagHelperOutput; + } + + protected virtual string GetLabelAsHtml(TagHelperOutput selectTag) + { + if (string.IsNullOrEmpty(TagHelper.Label) && string.IsNullOrEmpty(TagHelper.AspFor.Metadata.DisplayName)) + { + return ""; + } + + return ""; + } + + protected virtual string GetLabelText() + { + return string.IsNullOrEmpty(TagHelper.Label) ? + TagHelper.AspFor.Metadata.DisplayName : + TagHelper.Label; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelper.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelper.cs new file mode 100644 index 0000000000..99c9fc4cce --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelper.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + [HtmlTargetElement(Attributes = "asp-validation-for")] + public class AbpValidationAttributeTagHelper : AbpTagHelper, ITransientDependency + { + public AbpValidationAttributeTagHelper(AbpValidationAttributeTagHelperService tagHelperService) + : base(tagHelperService) + { + + } + } +} diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelperService.cs new file mode 100644 index 0000000000..0c78cf5396 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpValidationAttributeTagHelperService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.TagHelpers; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class AbpValidationAttributeTagHelperService : AbpTagHelperService + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.Attributes.Add("class","text-danger"); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisplayOrder.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisplayOrder.cs new file mode 100644 index 0000000000..7f311790d9 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisplayOrder.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + [AttributeUsage(AttributeTargets.Property)] + public class DisplayOrder : Attribute + { + public int Number { get; set; } + + public DisplayOrder(int number) + { + Number = number; + } + } +} diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupContent.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupContent.cs new file mode 100644 index 0000000000..9a1c9e9f3e --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupContent.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public class FormGroupContent + { + public string Html { get; set; } + + public int Order { get; set; } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..ef4e708c65 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectItems.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + [AttributeUsage(AttributeTargets.Property)] + public class SelectItems: Attribute + { + public string ItemsListPropertyName { get; set; } + + public SelectType SelectType { get; set; } = SelectType.Dropdown; + + public IEnumerable GetItems(ModelExplorer explorer) + { + var properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName.Equals(ItemsListPropertyName)).ToList(); + + return properties.Count > 0 + ? properties.First().Model as IEnumerable + : null; + } + } +} diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectType.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectType.cs new file mode 100644 index 0000000000..b1c3bfa846 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/SelectType.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + public enum SelectType + { + Dropdown, + Radio + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelper.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelper.cs index 446f52e531..fdd6b973a4 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelper.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelper.cs @@ -1,6 +1,4 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid { public class AbpColBreakTagHelper : AbpTagHelper { diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelperService.cs index f414d7682d..ccbb4e638a 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColBreakTagHelperService.cs @@ -1,6 +1,4 @@ -using System; -using System.Text; -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColumnTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColumnTagHelperService.cs index 82aa86fcd5..b095a8a0a0 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColumnTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpColumnTagHelperService.cs @@ -1,6 +1,4 @@ -using System; -using System.Text; -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpContainerTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpContainerTagHelperService.cs index 0c2c429907..75650f7e13 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpContainerTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpContainerTagHelperService.cs @@ -1,5 +1,4 @@ -using System.Text; -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpRowTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpRowTagHelperService.cs index 53b7588764..437e608e55 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpRowTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Grid/AbpRowTagHelperService.cs @@ -1,6 +1,4 @@ -using System.ComponentModel; -using System.Text; -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid diff --git a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Label/AbpLabelTagHelperService.cs b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Label/AbpLabelTagHelperService.cs index 45bafc780b..24a094732a 100644 --- a/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Label/AbpLabelTagHelperService.cs +++ b/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Label/AbpLabelTagHelperService.cs @@ -1,5 +1,4 @@ -using System.Text; -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Label { 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 30d8008e0e..5cada5ef21 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,23 +1,128 @@ @page -@model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.FormsModel +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components +@model FormsModel + @{ - ViewData["Title"] = "Forms"; + ViewData["Title"] = "Forms"; }

Forms

Based on Bootstrap form.

-

# Example

+

# Dynamic Form Example

+ +
-
-
+        
       
+        <abp-dynamic-form asp-model="Model.Ahmet" >
+        </abp-dynamic-form>
         
+ +@*

# 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-form-group>
+                    <abp-select asp-for="@Model.Country" asp-items="@Model.Countries"></abp-select>
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-select asp-for="@Model.City" asp-items="@Html.GetEnumSelectList(typeof(Cities))"></abp-select>
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.Name" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.Password" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.PhoneNumber" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.EmailAddress" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.Count" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.Day" />
+                </abp-form-group>
+                <abp-form-group>
+                    <abp-input asp-for="@Model.IsActive" />
+                </abp-form-group>
+            </form>
+            
+
+
*@ \ No newline at end of file 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 ab186db51a..27acba66c6 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 @@ -1,12 +1,83 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components { public class FormsModel : PageModel { + public PersonViewModel Ahmet { get; set; } = new PersonViewModel(); + public void OnGet() { - + Ahmet = new PersonViewModel + { + Name = "ahmet", + Age = 65, + Phone = new Phone {Number = "326346231",Name = "MyPhone"} + }; } } + + public enum Cities + { + Istanbul, + NewJersey, + Moscow + } + + public class PersonViewModel + { + [Required] + [DisplayName("Name")] + public string Name { get; set; } = "MyName"; + + [Required] + [DisplayOrder(61)] + [DisplayName("Age")] + [Range(1, 100)] + public int Age { get; set; } + + [Required] + [DisplayName("City")] + public Cities City { get; set; } + + public Phone Phone { get; set; } + + [DataType(DataType.Date)] + [DisplayName("Day")] + public DateTime Day { get; set; } + + [DisplayOrder(51)] + [DisplayName("Is Active")] + 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 + { + [Required] + [DisplayName("Number")] + public string Number { get; set; } + + [Required] + [DisplayOrder(71)] + [DisplayName("PhoneName")] + public string Name { get; set; } + } } \ No newline at end of file