Open Closed

Overriding abp-select #6282


User avatar
0
viswajwalith created

ABP Framework version: v7.1.3 UI Type: MVC Database System: SQL Server / MongoDB Tiered (for MVC) or Auth Server Separated (for Angular): yes Exception message and full stack trace: Steps to reproduce the issue:

We would like to override the abp-select control using AbpSelectTagHelper.cs but somehow it is not overriding as expected. Any suggition.

The file we are trying is

namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AbpTagHelperService<AbpSelectTagHelper>))]
    public class MySelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
    {
        private readonly IHtmlGenerator _generator;
        private readonly HtmlEncoder _encoder;
        private readonly IAbpTagHelperLocalizer _tagHelperLocalizer;
        private readonly IStringLocalizerFactory _stringLocalizerFactory;

        public MySelectTagHelperService(
            IHtmlGenerator generator,
            HtmlEncoder encoder,
            IAbpTagHelperLocalizer tagHelperLocalizer,
            IStringLocalizerFactory stringLocalizerFactory)
        {
            _generator = generator;
            _encoder = encoder;
            _tagHelperLocalizer = tagHelperLocalizer;
            _stringLocalizerFactory = stringLocalizerFactory;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var innerHtml = await GetFormInputGroupAsHtmlAsync(context, output);

            var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder();

            AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml), order, out var suppress);

            if (suppress)
            {
                output.SuppressOutput();
            }
            else
            {
                output.TagName = "div";
                LeaveOnlyGroupAttributes(context, output);
                output.Attributes.AddClass("form-group");
                output.TagMode = TagMode.StartTagAndEndTag;
                output.Content.SetHtmlContent(innerHtml);
            }
        }

        protected virtual async Task<string> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output)
        {
            var selectTag = await GetSelectTagAsync(context, output);
            var selectAsHtml = selectTag.Render(_encoder);
            var label = await GetLabelAsHtmlAsync(context, output, selectTag);
            var validation = await GetValidationAsHtmlAsync(context, output, selectTag);
            var infoText = GetInfoAsHtml(context, output, selectTag);

            return label + Environment.NewLine + selectAsHtml + Environment.NewLine + infoText + Environment.NewLine + validation;
        }

        protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml)
        {
            return "<div class=\"form-group\">" + Environment.NewLine + innerHtml + Environment.NewLine + "</div>";
        }

        protected virtual async Task<TagHelperOutput> GetSelectTagAsync(TagHelperContext context, TagHelperOutput output)
        {
            var selectTagHelper = new SelectTagHelper(_generator)
            {
                For = TagHelper.AspFor,
                ViewContext = TagHelper.ViewContext
            };

            if (TagHelper.AutocompleteApiUrl.IsNullOrEmpty())
            {
                selectTagHelper.Items = GetSelectItems(context, output);
            }
            else if (!TagHelper.AutocompleteSelectedItemName.IsNullOrEmpty())
            {
                selectTagHelper.Items = new[]
                {
                    new SelectListItem(TagHelper.AutocompleteSelectedItemName,
                        TagHelper.AutocompleteSelectedItemValue, false)
                };
            }

            var selectTagHelperOutput = await selectTagHelper.ProcessAndGetOutputAsync(GetInputAttributes(context, output), context, "select", TagMode.StartTagAndEndTag);

            selectTagHelperOutput.Attributes.AddClass("form-control");
            selectTagHelperOutput.Attributes.AddClass(GetSize(context, output));
            AddDisabledAttribute(selectTagHelperOutput);
            AddInfoTextId(selectTagHelperOutput);
            AddAutocompleteAttributes(selectTagHelperOutput);

            return selectTagHelperOutput;
        }

        protected virtual void AddAutocompleteAttributes(TagHelperOutput output)
        {
            if (!TagHelper.AutocompleteApiUrl.IsNullOrEmpty())
            {
                output.Attributes.AddClass("auto-complete-select");
                output.Attributes.Add("data-autocomplete-api-url", TagHelper.AutocompleteApiUrl);
                output.Attributes.Add("data-autocomplete-items-property", TagHelper.AutocompleteItemsPropertyName);
                output.Attributes.Add("data-autocomplete-display-property", TagHelper.AutocompleteDisplayPropertyName);
                output.Attributes.Add("data-autocomplete-value-property", TagHelper.AutocompleteValuePropertyName);
                output.Attributes.Add("data-autocomplete-filter-param-name", TagHelper.AutocompleteFilterParamName);
                output.Attributes.Add("data-autocomplete-selected-item-name", TagHelper.AutocompleteSelectedItemName);
                output.Attributes.Add("data-autocomplete-selected-item-value", TagHelper.AutocompleteSelectedItemValue);
            }
        }

        protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput)
        {
            var disabledAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute<DisabledInput>();

            if (disabledAttribute != null && !inputTagHelperOutput.Attributes.ContainsName("disabled"))
            {
                inputTagHelperOutput.Attributes.Add("disabled", "");
            }
        }

        protected virtual List<SelectListItem> GetSelectItems(TagHelperContext context, TagHelperOutput output)
        {
            if (TagHelper.AspItems != null)
            {
                return TagHelper.AspItems.ToList();
            }

            if (IsEnum())
            {
                return GetSelectItemsFromEnum(context, output, TagHelper.AspFor.ModelExplorer);
            }

            var selectItemsAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute<SelectItems>();
            if (selectItemsAttribute != null)
            {
                return GetSelectItemsFromAttribute(selectItemsAttribute, TagHelper.AspFor.ModelExplorer);
            }

            throw new Exception("No items provided for select attribute.");
        }

        private bool IsEnum()
        {
            var value = TagHelper.AspFor.Model;
            if (value != null && value.GetType().IsEnum)
            {
                return true;
            }

            return TagHelper.AspFor.ModelExplorer.Metadata.IsEnum;
        }

        protected virtual async Task<string> GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput selectTag)
        {
            if (!string.IsNullOrEmpty(TagHelper.Label))
            {
                var label = new TagBuilder("label");
                label.Attributes.Add("for", GetIdAttributeValue(selectTag));
                label.InnerHtml.AppendHtml(TagHelper.Label);

                return label.ToHtmlString() + GetRequiredSymbol(context, output);
            }

            return await GetLabelAsHtmlUsingTagHelperAsync(context, output) + GetRequiredSymbol(context, output);
        }

        protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output)
        {
            if (!TagHelper.DisplayRequiredSymbol)
            {
                return "";
            }

            return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
        }

        protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput)
        {
            if (TagHelper.AspFor.ModelExplorer.GetAttribute<InputInfoText>() == null)
            {
                return;
            }

            var idAttr = inputTagHelperOutput.Attributes.FirstOrDefault(a => a.Name == "id");

            if (idAttr == null)
            {
                return;
            }

            var infoText = _tagHelperLocalizer.GetLocalizedText(idAttr.Value + "InfoText", TagHelper.AspFor.ModelExplorer);

            inputTagHelperOutput.Attributes.Add("aria-describedby", infoText);
        }

        protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag)
        {
            var text = "";

            if (!string.IsNullOrEmpty(TagHelper.InfoText))
            {
                text = TagHelper.InfoText;
            }
            else
            {
                var infoAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute<InputInfoText>();
                if (infoAttribute != null)
                {
                    text = infoAttribute.Text;
                }
                else
                {
                    return "";
                }
            }

            var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id");
            var localizedText = _tagHelperLocalizer.GetLocalizedText(text, TagHelper.AspFor.ModelExplorer);

            var small = new TagBuilder("small");
            small.Attributes.Add("id", idAttr?.Value?.ToString() + "InfoText");
            small.AddCssClass("form-text text-muted");
            small.InnerHtml.Append(localizedText);

            return small.ToHtmlString();
        }

        protected virtual List<SelectListItem> GetSelectItemsFromEnum(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer)
        {
            var selectItems = new List<SelectListItem>();
            var isNullableType = Nullable.GetUnderlyingType(explorer.ModelType) != null;
            var enumType = explorer.ModelType;

            if (isNullableType)
            {
                enumType = Nullable.GetUnderlyingType(explorer.ModelType);
                selectItems.Add(new SelectListItem());
            }

            var containerLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(explorer.Container.ModelType.Assembly);

            foreach (var enumValue in enumType.GetEnumValues())
            {
                var memberName = enumType.GetEnumName(enumValue);
                var localizedMemberName = AbpInternalLocalizationHelper.LocalizeWithFallback(
                    new[]
                    {
                        containerLocalizer,
                        _stringLocalizerFactory.CreateDefaultOrNull()
                    },
                    new[]
                    {
                        $"Enum:{enumType.Name}.{memberName}",
                        $"{enumType.Name}.{memberName}",
                        memberName
                    },
                    memberName
                );

                selectItems.Add(new SelectListItem
                {
                    Value = enumValue.ToString(),
                    Text = localizedMemberName
                });
            }

            return selectItems;
        }

        protected virtual List<SelectListItem> GetSelectItemsFromAttribute(
            SelectItems selectItemsAttribute,
            ModelExplorer explorer)
        {
            var selectItems = selectItemsAttribute.GetItems(explorer)?.ToList();

            if (selectItems == null)
            {
                return new List<SelectListItem>();
            }

            return selectItems;
        }

        protected virtual async Task<string> GetLabelAsHtmlUsingTagHelperAsync(TagHelperContext context, TagHelperOutput output)
        {
            var labelTagHelper = new LabelTagHelper(_generator)
            {
                For = TagHelper.AspFor,
                ViewContext = TagHelper.ViewContext
            };

            return await labelTagHelper.RenderAsync(new TagHelperAttributeList(), context, _encoder, "label", TagMode.StartTagAndEndTag);
        }

        protected virtual async Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag)
        {
            var validationMessageTagHelper = new ValidationMessageTagHelper(_generator)
            {
                For = TagHelper.AspFor,
                ViewContext = TagHelper.ViewContext
            };

            var attributeList = new TagHelperAttributeList { { "class", "text-danger" } };

            return await validationMessageTagHelper.RenderAsync(attributeList, context, _encoder, "span", TagMode.StartTagAndEndTag);
        }

        protected virtual string GetSize(TagHelperContext context, TagHelperOutput output)
        {
            var attribute = TagHelper.AspFor.ModelExplorer.GetAttribute<FormControlSize>();

            if (attribute != null)
            {
                TagHelper.Size = attribute.Size;
            }

            switch (TagHelper.Size)
            {
                case AbpFormControlSize.Small:
                    return "custom-select-sm";
                case AbpFormControlSize.Medium:
                    return "custom-select-md";
                case AbpFormControlSize.Large:
                    return "custom-select-lg";
            }

            return "";
        }

        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.AddClass("custom-select");

            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 newAttritube = new TagHelperAttribute(nameWithoutPrefix, tagHelperAttribute.Value);
                output.Attributes.Add(newAttritube);
            }
        }

        protected virtual string GetIdAttributeValue(TagHelperOutput inputTag)
        {
            var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id");

            return idAttr != null ? idAttr.Value.ToString() : string.Empty;
        }

        protected virtual string GetIdAttributeAsString(TagHelperOutput inputTag)
        {
            var id = GetIdAttributeValue(inputTag);

            return !string.IsNullOrWhiteSpace(id) ? "for=\"" + id + "\"" : string.Empty;
        }

        protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress)
        {
            var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>();
            suppress = list == null;

            if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\"")))
            {
                list.Add(new FormGroupItem
                {
                    HtmlContent = html,
                    Order = order,
                    PropertyName = propertyName
                });
            }
        }
    }
}

 

5 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    You can try to replace the AbpSelectTagHelperService.

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AbpSelectTagHelperService))]
    public class MySelectTagHelperService : AbpSelectTagHelperService
    
  • User Avatar
    0
    viswajwalith created

    Hi,

    You can try to replace the AbpSelectTagHelperService.

    [Dependency(ReplaceServices = true)] 
    [ExposeServices(typeof(AbpSelectTagHelperService))] 
    public class MySelectTagHelperService : AbpSelectTagHelperService 
    

    I dont think I got ur point, do u mean something like below code?

    will you able to provide the path for the specific file which you are refering us to override. Thanks in advance

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    No, As I said you try to replace the AbpSelectTagHelperService instead of AbpTagHelperService<AbpSelectTagHelper>, AbpSelectTagHelper

    For example:

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AbpSelectTagHelperService))]
    public class MySelectTagHelperService : AbpSelectTagHelperService
    {
        public MySelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer, IStringLocalizerFactory stringLocalizerFactory, IAbpEnumLocalizer abpEnumLocalizer) : base(generator, encoder, tagHelperLocalizer, stringLocalizerFactory, abpEnumLocalizer)
        {
        }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            ...
            base.Process(context, output);
        }
    }
    
  • User Avatar
    0
    viswajwalith created

    No, As I said you try to replace the AbpSelectTagHelperService instead of AbpTagHelperService<AbpSelectTagHelper>, AbpSelectTagHelper

    For example:

    [Dependency(ReplaceServices = true)] 
    [ExposeServices(typeof(AbpSelectTagHelperService))] 
    public class MySelectTagHelperService : AbpSelectTagHelperService 
    { 
        public MySelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer, IStringLocalizerFactory stringLocalizerFactory, IAbpEnumLocalizer abpEnumLocalizer) : base(generator, encoder, tagHelperLocalizer, stringLocalizerFactory, abpEnumLocalizer) 
        { 
        } 
     
        public override void Process(TagHelperContext context, TagHelperOutput output) 
        { 
            ... 
            base.Process(context, output); 
        } 
    } 
    

    It worked thanks, will let you know in case of any thing else.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    ok

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11