Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core- Is there a way to render group of elements each using custom tag-helpers?

I noticed in my project that all my form fields follow the same pattern. A typical example is:

<div class="col-x-x">
  <label asp-for="Property"></label>
  <span message="description">
  <input asp-for="Property" />
  <span asp-validation-for="Property"></span>
</div>

I would love to have some way of grouping this code so that i simply pass it the property on the model and it outputs the correct HTML. e.g.:

<form-field for="Property" ...>

or

@Html.StringFormField(...)

The issue I am having is that whatever method I try, the html outputted is the original html above, and not the html that is generated from the tag helpers. I have tried both methods and neither have been successful. Additionally I have tried to create a razor function, but all my attempts fail to compile, and I can't make a partial view work as I haven't been able to find a way to get the property information after passing a string to a view.

My latest attempt was using a tag helper, however this had the same issue mentioned previously. The latest version of the code is as follows:

[HtmlTargetElement("form-field", Attributes = "for")]
public class FormFieldTagHelper : TagHelper
{
    [HtmlAttributeName("for")]
    public ModelExpression For { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "div";
        output.TagMode = TagMode.StartTagAndEndTag;

        var contentBuilder = new HtmlContentBuilder();

        contentBuilder.AppendHtmlLine($"<label asp-for=\"{For}\"></label>");
        contentBuilder.AppendHtmlLine($"<span message=\"description.\"></span>");
        contentBuilder.AppendHtmlLine($"<input asp-for=\"{For}\"/>");
        contentBuilder.AppendHtmlLine($"<span asp-validation-for=\"{For}\"/></span>");

        output.Content.SetHtmlContent(contentBuilder);
    }
}

There is an issue addressing this (with no solution) which suggested the order of the imports was a potential issue, so my imports are as follows:

@addTagHelper Project.Web.Features.Shared.*, Project.Web
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Any solution would be welcome, either for a tag helper or another method.

like image 983
ArcticFox Avatar asked Nov 24 '25 10:11

ArcticFox


1 Answers

You could use IHtmlGenerator to generate these elements, refer to my below demo code:

[HtmlTargetElement("form-field", Attributes = "for")]
public class FormFieldTagHelper : TagHelper
{
    [HtmlAttributeName("for")]
    public ModelExpression For { get; set; }

    private readonly IHtmlGenerator _generator;

    [ViewContext]
    public ViewContext ViewContext { get; set; }

    public FormFieldTagHelper(IHtmlGenerator generator)
    {
        _generator = generator;
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        using (var writer = new StringWriter())
        {
            writer.Write(@"<div class=""form-group"">");

            var label = _generator.GenerateLabel(
                            ViewContext,
                            For.ModelExplorer,
                            For.Name, null,
                            new { @class = "control-label" });

            label.WriteTo(writer, NullHtmlEncoder.Default);
            writer.Write(@"<span message=""description.""></span>");

            var textArea = _generator.GenerateTextBox(ViewContext,
                                For.ModelExplorer,
                                For.Name,
                                For.Model,
                                null,
                                new { @class = "form-control" });

            textArea.WriteTo(writer, NullHtmlEncoder.Default);

            var validationMsg = _generator.GenerateValidationMessage(
                                    ViewContext,
                                    For.ModelExplorer,
                                    For.Name,
                                    null,
                                    ViewContext.ValidationMessageElement,
                                    new { @class = "text-danger" });

            validationMsg.WriteTo(writer, NullHtmlEncoder.Default);

            writer.Write(@"</div>");

            output.Content.SetHtmlContent(writer.ToString());

        }
    }
}

View:

 <form-field for="ManagerName"></form-field>

Result:

enter image description here

like image 140
Ryan Avatar answered Nov 27 '25 14:11

Ryan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!