rolling my own @Html.BeginfBrm()
Asked Answered
B

2

7

I am writing a custom validation set that will display all missing elements on a div. I'd like to be able to use a custom @Html.BeginForm() method that will write out that div but I'm really not sure where to even begin as this nut is a little tougher to crack than just a html extension that writes out a Tag or String (the form encapsulates data/controls and is closed by } at the end).

I looked at the metadata version of the built in BeginForm() method and it wasn't much help to me. Essentially, I just want to extend that method if possible and have it write out a MvcHtmlString of a div that will be show/hidden from JavaScript.

ultimately where I'm struggling is figuring out how to write this custom helper that has the beginning and ending component to it.

@using(Html.BeginForm())
{
...

}

I'd want to be able to do something like this:

@using(Html.VBeginForm())
{
...

}

and have that render my extra html

EDIT: adding code from suggestion below

public class VBeginForm : IDisposable
{
    private readonly HtmlHelper _helper;
    public VBeginForm(HtmlHelper htmlHelper, string areaName)
    {
        _helper = htmlHelper;
        var container = new TagBuilder("form");
        container.GenerateId(areaName);
        var writer = _helper.ViewContext.Writer;
        writer.Write(container.ToString(TagRenderMode.StartTag));
    }

    public void Dispose()
    {
        _helper.ViewContext.Writer.Write("</form>");
    }
}
Beyond answered 17/6, 2011 at 14:35 Comment(0)
F
6

You need to write an extension method for the HtmlHelper class that prints to helper.ViewContext.Writer.

The method should return an IDisposable that prints the closing tag in its Dispose method.

Frosted answered 17/6, 2011 at 14:38 Comment(5)
Great this definitely got me started in the right direction. I'm a little lost on the IDisposible bit. I tried creating my class to extend IDisposible with two methods, the first to use the writer to write out my form tag and the second was my dispose method to write out the end tag. VS2010 isn't liking something with that approach. You say in your answer to actually have my method return IDisposible...how would I then have that return type invoke its dispose() method?...sorry just a bit confused here.Beyond
The using block will Dispose() the object you put in it at the end of the block.Frosted
Right, I get that, but how do I write into my method that it should be a rendered closing form tag?Beyond
I just edited my OP with what I"m trying that isn't working (I will clean up the tagbuilder stuff when I get it straightened out)...but that approach isn't flying.Beyond
You need to make an extension method in a separate static class that returns an instance of your IDisposable class. (and passes the HtmlHelper to that instance so it can print)Frosted
I
4

SLaks answer is right, but is missing some extra information.

If you want to use traditional (not unobtrusive) client side validation, you need to supply a formContext, and give it an id, so that the client side validation can work. In his explanation this part is missing.

The easiest way to achieve this is to use return an instance of the MvcForm class, that creates the formContext, and implements the IDisposable interface.

In this implementation I needed to supply the form's id:

public static MvcForm BeginFormDatosAdicionales(this HtmlHelper htmlHelper, 
   string id, ..., IDictionary<string, object> htmlAttributes = null)
{
  TagBuilder form = new TagBuilder("form");
  // attributes
  form.MergeAttributes(htmlAttributes);
  // action
  string formAction = ...;
  form.MergeAttribute("action", formAction);
  // method
  FormMethod method = ...;
  form.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
  // id
  form.MergeAttribute("id", id);

  // writes the form's opening tag in the ViewContext.Writer
  htmlHelper.ViewContext.Writer.Write(form.ToString(TagRenderMode.StartTag));

  // creates an MvcForm (disposable), which creates a FormContext, needed for
  // client-side validation. You need to supply and id for it
  MvcForm theForm = new MvcForm(htmlHelper.ViewContext);
  htmlHelper.ViewContext.FormContext.FormId = form.Attributes["id"];

  // The returned object implements IDisposable, and writes the closing form tag
  return theForm;
  }

Of course this can be customized for your particular case. If you only want to provide an id for your form when absolutely neccesary, check this contidition:

bool idRequired = htmlHelper.ViewContext.ClientValidationEnabled
&& !htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;

In this case you must be careful to create different Ids for each form in a page. For example you can add an integer suffix, that can be stored in HttpContext.Items and incremented every time a new Id is generated. This ensures that in a single page all generated ids are different.

HttpContext.Current.Items["lastFormId"]
Impassable answered 31/7, 2013 at 9:4 Comment(1)
Terrific answer. I cribbed this code sample and it suited my needs perfectly.Aoristic

© 2022 - 2024 — McMap. All rights reserved.