Add form HTML helpers and `MVCForm`
- copy over from legacy MVC - fixup namespaces, remove copyright notices, ... - temporarily remove `RouteBeginForm() overloads - move main components to `HtmlHelper` and declare in `IHtmlHelper<T>` - change `UrlHelper` to avoid treating an existing dictionary as an object - add `HtmlHelper.Createform()` factory - use in MVC sample; move BeginForm / TextBox samples to a new row
This commit is contained in:
parent
94c028a5df
commit
1cd15fbf60
|
|
@ -134,8 +134,15 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div style="float: left; border: thick solid lightskyblue; margin-left: 15px; padding-right: 10px">
|
||||
<form action="/Home/Hello" method="post">
|
||||
<div style="float: right; border: 5px solid red;">
|
||||
@await Component.InvokeAsync("Tags", 15)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div style="float: left; border: thick solid lightskyblue; margin-bottom: 10px; margin-top: -180px; padding-right: 10px">
|
||||
@using (Html.BeginForm(controllerName: "Home", actionName: "Hello", method: FormMethod.Post))
|
||||
{
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
|
@ -153,6 +160,16 @@
|
|||
@Html.TextBoxFor(m => m.Address, htmlAttributes: new { @class = "form-control" })
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
@{ Html.BeginForm(method: FormMethod.Post, htmlAttributes: new { someAttribute = "some value", }); }
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label class="control-label col-md-2">Anon.Name</label>
|
||||
|
|
@ -191,21 +208,17 @@
|
|||
</td>
|
||||
<td>
|
||||
@Html.TextBox("Anon.Address.Country", value: null, format: "{0} (4)",
|
||||
htmlAttributes: new { @class = "form-control" })
|
||||
htmlAttributes: new { @class = "form-control" })
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2">
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="float: left; border: 5px solid red;">
|
||||
@await Component.InvokeAsync("Tags", 15)
|
||||
@{ Html.EndForm(); }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -318,5 +331,4 @@
|
|||
<div style="float: left; border: 5px solid green;">
|
||||
@Html.DisplayForModel()
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,15 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
public string Action(string action, string controller, object values)
|
||||
{
|
||||
var valuesDictionary = new RouteValueDictionary(values);
|
||||
var valuesDictionary = values as IDictionary<string, object>;
|
||||
if (valuesDictionary == null)
|
||||
{
|
||||
valuesDictionary = new RouteValueDictionary(values);
|
||||
}
|
||||
else
|
||||
{
|
||||
valuesDictionary = new RouteValueDictionary(valuesDictionary);
|
||||
}
|
||||
|
||||
if (action != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -139,6 +139,29 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
ViewContext = viewContext;
|
||||
}
|
||||
|
||||
public MvcForm BeginForm(string actionName, string controllerName, object routeValues, FormMethod method,
|
||||
object htmlAttributes)
|
||||
{
|
||||
// Only need a dictionary if htmlAttributes is non-null. TagBuilder.MergeAttributes() is fine with null.
|
||||
IDictionary<string, object> htmlAttributeDictionary = null;
|
||||
if (htmlAttributes != null)
|
||||
{
|
||||
htmlAttributeDictionary = htmlAttributes as IDictionary<string, object>;
|
||||
if (htmlAttributeDictionary == null)
|
||||
{
|
||||
htmlAttributeDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
return GenerateForm(actionName, controllerName, routeValues, method, htmlAttributeDictionary);
|
||||
}
|
||||
|
||||
public void EndForm()
|
||||
{
|
||||
var mvcForm = CreateForm();
|
||||
mvcForm.EndForm();
|
||||
}
|
||||
|
||||
public string Encode(string value)
|
||||
{
|
||||
return (!string.IsNullOrEmpty(value)) ? WebUtility.HtmlEncode(value) : string.Empty;
|
||||
|
|
@ -149,7 +172,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return value != null ? WebUtility.HtmlEncode(value.ToString()) : string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatValue(object value, string format)
|
||||
{
|
||||
return ViewDataDictionary.FormatValue(value, format);
|
||||
|
|
@ -183,7 +205,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
additionalViewData);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual HtmlString Name(string name)
|
||||
{
|
||||
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
|
||||
|
|
@ -355,7 +376,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public HtmlString TextBox(string name, object value, string format, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
return GenerateTextBox(metadata: null, name: name, value: value, format: format,
|
||||
|
|
@ -367,6 +387,16 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return GenerateValue(name, value: null, format: format, useViewData: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to return an <see cref="MvcForm"/> subclass. That subclass may change
|
||||
/// <see cref="EndForm()"/> behavior.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="MvcForm"/> instance.</returns>
|
||||
protected virtual MvcForm CreateForm()
|
||||
{
|
||||
return new MvcForm(ViewContext);
|
||||
}
|
||||
|
||||
protected string EvalString(string key, string format)
|
||||
{
|
||||
return Convert.ToString(ViewData.Eval(key, format), CultureInfo.CurrentCulture);
|
||||
|
|
@ -397,6 +427,56 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an opening <form> tag to the response. When the user submits the form,
|
||||
/// the request will be processed by an action method.
|
||||
/// </summary>
|
||||
/// <param name="actionName">The name of the action method.</param>
|
||||
/// <param name="controllerName">The name of the controller.</param>
|
||||
/// <param name="routeValues">An object that contains the parameters for a route. The parameters are retrieved
|
||||
/// through reflection by examining the properties of the object. This object is typically created using object
|
||||
/// initializer syntax. Alternatively, an <see cref="IDictionary{string, object}"/> instance containing the
|
||||
/// route parameters.</param>
|
||||
/// <param name="method">The HTTP method for processing the form, either GET or POST.</param>
|
||||
/// <param name="htmlAttributes">An <see cref="IDictionary{string, object}"/> instance containing HTML
|
||||
/// attributes to set for the element.</param>
|
||||
/// <returns>An <see cref="MvcForm"/> instance which emits the closing {form} tag when disposed.</returns>
|
||||
protected virtual MvcForm GenerateForm(string actionName, string controllerName, object routeValues,
|
||||
FormMethod method, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
var urlHelper = ViewContext.Url;
|
||||
var formAction = urlHelper.Action(action: actionName, controller: controllerName, values: routeValues);
|
||||
|
||||
var tagBuilder = new TagBuilder("form");
|
||||
tagBuilder.MergeAttributes(htmlAttributes);
|
||||
|
||||
// action is implicitly generated, so htmlAttributes take precedence.
|
||||
tagBuilder.MergeAttribute("action", formAction);
|
||||
|
||||
// method is an explicit parameter, so it takes precedence over the htmlAttributes.
|
||||
tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), replaceExisting: true);
|
||||
|
||||
var traditionalJavascriptEnabled = ViewContext.ClientValidationEnabled &&
|
||||
!ViewContext.UnobtrusiveJavaScriptEnabled;
|
||||
if (traditionalJavascriptEnabled)
|
||||
{
|
||||
// TODO revive ViewContext.FormIdGenerator(), WebFx-199
|
||||
// forms must have an ID for client validation
|
||||
var formName = "form" + new Guid().ToString();
|
||||
tagBuilder.GenerateId(formName, IdAttributeDotReplacement);
|
||||
}
|
||||
|
||||
ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
|
||||
var theForm = CreateForm();
|
||||
|
||||
if (traditionalJavascriptEnabled)
|
||||
{
|
||||
ViewContext.FormContext.FormId = tagBuilder.Attributes["id"];
|
||||
}
|
||||
|
||||
return theForm;
|
||||
}
|
||||
|
||||
protected virtual HtmlString GenerateTextBox(ModelMetadata metadata, string name, object value, string format,
|
||||
IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public class MvcForm : IDisposable
|
||||
{
|
||||
private readonly ViewContext _viewContext;
|
||||
private bool _disposed;
|
||||
|
||||
public MvcForm([NotNull] ViewContext viewContext)
|
||||
{
|
||||
_viewContext = viewContext;
|
||||
|
||||
// Push the new FormContext; GenerateEndForm() does the corresponding pop.
|
||||
_viewContext.FormContext = new FormContext();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the closing </form> tag to the response.
|
||||
/// </summary>
|
||||
public void EndForm()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
}
|
||||
|
||||
protected virtual void GenerateEndForm()
|
||||
{
|
||||
_viewContext.Writer.Write("</form>");
|
||||
|
||||
// TODO revive viewContext.OutputClientValidation(), this requires GetJsonValidationMetadata(), GitHub #163
|
||||
_viewContext.FormContext = null;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
GenerateEndForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public static class HtmlHelperFormExtensions
|
||||
{
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper)
|
||||
{
|
||||
// Generates <form action="{current url}" method="post">.
|
||||
return htmlHelper.BeginForm(actionName: null, controllerName: null, routeValues: null,
|
||||
method: FormMethod.Post, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, FormMethod method)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName: null, controllerName: null, routeValues: null,
|
||||
method: method, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, FormMethod method,
|
||||
object htmlAttributes)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName: null, controllerName: null, routeValues: null,
|
||||
method: method, htmlAttributes: htmlAttributes);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, object routeValues)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName: null, controllerName: null, routeValues: routeValues,
|
||||
method: FormMethod.Post, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, string actionName,
|
||||
string controllerName)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName, controllerName, routeValues: null,
|
||||
method: FormMethod.Post, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, string actionName,
|
||||
string controllerName, object routeValues)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName, controllerName, routeValues,
|
||||
FormMethod.Post, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, string actionName,
|
||||
string controllerName, FormMethod method)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName, controllerName, routeValues: null,
|
||||
method: method, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, string actionName,
|
||||
string controllerName, object routeValues, FormMethod method)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName, controllerName, routeValues,
|
||||
method, htmlAttributes: null);
|
||||
}
|
||||
|
||||
public static MvcForm BeginForm<TModel>([NotNull] this IHtmlHelper<TModel> htmlHelper, string actionName,
|
||||
string controllerName, FormMethod method, object htmlAttributes)
|
||||
{
|
||||
return htmlHelper.BeginForm(actionName, controllerName, routeValues: null,
|
||||
method: method, htmlAttributes: htmlAttributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,29 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
/// </summary>
|
||||
ViewDataDictionary<TModel> ViewData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Writes an opening <form> tag to the response. When the user submits the form,
|
||||
/// the request will be processed by an action method.
|
||||
/// </summary>
|
||||
/// <param name="actionName">The name of the action method.</param>
|
||||
/// <param name="controllerName">The name of the controller.</param>
|
||||
/// <param name="routeValues">An object that contains the parameters for a route. The parameters are retrieved
|
||||
/// through reflection by examining the properties of the object. This object is typically created using object
|
||||
/// initializer syntax. Alternatively, an <see cref="IDictionary{string, object}"/> instance containing the
|
||||
/// route parameters.</param>
|
||||
/// <param name="method">The HTTP method for processing the form, either GET or POST.</param>
|
||||
/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.
|
||||
/// Alternatively, an <see cref="IDictionary{string, object}"/> instance containing the HTML attributes.
|
||||
/// </param>
|
||||
/// <returns>An <see cref="MvcForm"/> instance which emits the closing {form} tag when disposed.</returns>
|
||||
MvcForm BeginForm(string actionName, string controllerName, object routeValues, FormMethod method,
|
||||
object htmlAttributes);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the closing </form> tag to the response.
|
||||
/// </summary>
|
||||
void EndForm();
|
||||
|
||||
/// <summary>
|
||||
/// Returns HTML markup for each property in the object that is represented by the expression, using the specified
|
||||
/// template, HTML field ID, and additional view data.
|
||||
|
|
|
|||
Loading…
Reference in New Issue