skip to Main Content

I am trying to implement server side validation, by changing the class of an HTML child element within a TagHelper. The TagHelper is the "kendo-datepicker", however the current code modifies the class of the "span" tag and I would like to modify the class of the child "input" tag.

After some research it appears GetChildContentAsync may be useful, but all my attempts to use it are failing.

Taghelper:

[HtmlTargetElement("kendo-datepicker")]
[HtmlTargetElement("select")]
[HtmlTargetElement("input")]
public class ValidationErrorClassTagHelper : TagHelper
{
    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {

        //output.Attributes.ContainsName("class")

        if (context.TagName == "kendo-datepicker")
        {
            TagHelperAttribute taghelperAttribute = context.AllAttributes["for"];
            if (taghelperAttribute != null)
            {
           
                ModelExpression modelExpression = (ModelExpression)taghelperAttribute.Value;

                ViewContext.ViewData.ModelState.TryGetValue(modelExpression.Name, out ModelStateEntry entry);

                if (entry != null && entry.Errors.Any())
                {
                    output.Attributes.SetAttribute("class", "form-control " + "is-invalid");
                 
                }
            }
        }

    }
}

Current (incorrect) HTML Output:

<span class="k-datepicker k-input form-control is-invalid k-input-solid k-input-md k-rounded-md" style=""><input class="form-control k-input-inner valid" required="" data-val="true" data-val-required="Enter the date observed." id="ADR_Date" name="ADR.Date" type="text" value="6/07/2023" data-role="datepicker" role="combobox" aria-expanded="false" aria-haspopup="grid" aria-controls="ADR_Date_dateview" autocomplete="off" aria-disabled="false" aria-readonly="false" aria-describedby="ADR_Date-error" aria-invalid="false"><button aria-label="select" tabindex="-1" class="k-input-button k-button k-icon-button k-button-md k-button-solid k-button-solid-base" type="button" role="button"><span class="k-icon k-i-calendar k-button-icon"></span></button></span>

2

Answers


  1. A very raw idea would be to do

    TagHelperContent innerContent = await output.GetChildContentAsync();
    

    and then get the content via GetContent as a string and then parse this string and then SetContent after you added the class. This is an admittedly dirty solution we should only resort to if there is nothing else that we can do.

    Alternatively, you could look into context.Items and see whether the input is there under some key. If so, then you could work with that one.

    Login or Signup to reply.
  2. I think the simplest way would be:

    if (entry != null && entry.Errors.Any()) {
      // Asynchronously get string containing child content
      string childContent = (await output.GetChildContentAsync()).GetContent();
      // Insert your classes by matching the opening input tag along with the class attribute and replacing it.
      string modifiedContent = Regex.Replace(
        childContent,
        Regex.Escape("<input class=""),
        "<input class="form-control is-invalid ");
      // Update the output
      output.Content.SetHtmlContent(modifiedContent);
    }
    

    Note: if your HTML structure ever changes to include a second input, this will require updating.

    String manipulation isn’t exactly the preferred method for doing this, but without additional context about your issue, it’s the most straightforward. Depending on your environment, SetHtmlContent() may cause problems. If that’s the case, remove both leading < in the Regex and update SetHtmlContent() to SetContent(). I only included it for the extremely minor performance boost of skipping a call to HTMLEncoder.Encode().

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search