0

I need to be able to parse the attributes from an MvcHtmlString (the results of an HtmlHelper extension), so that I can update and add to them.

For example, take this HTML element:

<input type="text" id="Name" name="Name" 
 data-val-required="First name is required.|Please provide a first name.">

I need to take the value from data-val-required, split it into two attributes with the second value going into a new attribute:

<input type="text" id="Name" name="Name" 
 data-val-required="First name is required."
 data-val-required-summary="Please provide a first name.">

I'm trying to do this with an HtmlHelper extension method, but I'm not sure the best way to parse the attributes.

Jerad Rose
  • 15,235
  • 18
  • 82
  • 153
  • 2
    Wow, let's start from the beginning because this smells/stinks/emanates a suffocating odeur. What exactly are you trying to achieve? And please don't answer: `parse attributes from MvcHtmlString`. I am expecting an answer about your initial goal. Because `parse attributes from MvcHtmlString` is probably one way to reach your initial goal. There might be other ways. Better ways. But without knowing your initial goal it's hard to offer those better ways to you. – Darin Dimitrov Jul 13 '12 at 20:46
  • 1
    Well, for the purpose of this question, I'm really just interested in how to `parsing attributes generated by some other helper`. :) But I'll humor you, and say that our UX team has requirements on providing separate validation messages -- field-level vs. summary-level -- for our users. Using built-in validation, ModelStateDictionary only supports one message. So without completely rewriting the built-in validation, this was the best approach I can come up with. If you have any ideas, let me know and we can discuss off line (as to not hijack this topic). – Jerad Rose Jul 13 '12 at 20:49
  • And, for the record, I agree: it does smell. Badly. – Jerad Rose Jul 13 '12 at 20:50
  • Please prove me otherwise. You will be my best friend. – Jerad Rose Jul 13 '12 at 20:51
  • You need multiple validation messages per field? I don't understand your requirement. Tell me a usecase. – Darin Dimitrov Jul 13 '12 at 20:51
  • Yes. "First name is required." should display next to the field. But when the summary-level should say "Please provide a first name." (and also include an anchor link to the field, but that's another story). In other words, Html.ValidationMessageFor() should show Message A, and Html.ValidationSummary() should render Message B. – Jerad Rose Jul 13 '12 at 20:52
  • Perfect. Crystal clear. Now that's your real question. Do you need to support client side validation as well? I guess the answer is `yes` because you talked about the `data-*` attributes in your question but just wanted to make sure. – Darin Dimitrov Jul 13 '12 at 20:56
  • [Added a new question.](http://stackoverflow.com/questions/11478505/support-two-different-validation-messages-per-field-rule-with-unobtrusive-valida) – Jerad Rose Jul 13 '12 at 21:17

1 Answers1

-1

One idea to accomplish what you want is to use a global action filter. This will take the results of the action and allow you to modify them before they are sent back to the browser. I used this technique to add CSS classes to the body tag of the page, but I believe it will work for your application too.

Here is the code (boiled down to the basics) that I used:

public class GlobalCssClassFilter : ActionFilterAttribute {
    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        //build the data you need for the filter here and put it into the filterContext
        //ex: filterContext.HttpContext.Items.Add("key", "value");

        //activate the filter, passing the HtppContext we will need in the filter.
        filterContext.HttpContext.Response.Filter = new GlobalCssStream(filterContext.HttpContext);
    }
}

public class GlobalCssStream : MemoryStream {
    //to store the context for retrieving the area, controller, and action
    private readonly HttpContextBase _context;

    //to store the response for the Write override
    private readonly Stream _response;

    public GlobalCssStream(HttpContextBase context) {
        _context = context;
        _response = context.Response.Filter;
    }

    public override void Write(byte[] buffer, int offset, int count) {
        //get the text of the page being output
        var html = Encoding.UTF8.GetString(buffer);

        //get the data from the context
        //ex var area = _context.Items["key"] == null ? "" : _context.Items["key"].ToString();

        //find your tags and add the new ones here
        //modify the 'html' variable to accomplish this

        //put the modified page back to the response.
        buffer = Encoding.UTF8.GetBytes(html);
        _response.Write(buffer, offset, buffer.Length);
    }
}

One thing to watch out for is that the HTML is buffered into, I believe, 8K chunks, so you might have to determine how to deal with that if you have pages over that size. For my application, I didn't have to deal with that.

Also, since everything is sent through this filter, you need to be sure that the changes you are making won't effect things on things like JSON results.

nikeaa
  • 1,047
  • 7
  • 17