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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="float: left; border: thick solid lightskyblue; margin-left: 15px; padding-right: 10px">
|
<div style="float: right; border: 5px solid red;">
|
||||||
<form action="/Home/Hello" method="post">
|
@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>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -153,6 +160,16 @@
|
||||||
@Html.TextBoxFor(m => m.Address, htmlAttributes: new { @class = "form-control" })
|
@Html.TextBoxFor(m => m.Address, htmlAttributes: new { @class = "form-control" })
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label col-md-2">Anon.Name</label>
|
<label class="control-label col-md-2">Anon.Name</label>
|
||||||
|
|
@ -191,21 +208,17 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@Html.TextBox("Anon.Address.Country", value: null, format: "{0} (4)",
|
@Html.TextBox("Anon.Address.Country", value: null, format: "{0} (4)",
|
||||||
htmlAttributes: new { @class = "form-control" })
|
htmlAttributes: new { @class = "form-control" })
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="submit" value="Save" class="btn btn-default" />
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@{ Html.EndForm(); }
|
||||||
<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)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -318,5 +331,4 @@
|
||||||
<div style="float: left; border: 5px solid green;">
|
<div style="float: left; border: 5px solid green;">
|
||||||
@Html.DisplayForModel()
|
@Html.DisplayForModel()
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public string Action(string action, string controller, object values)
|
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)
|
if (action != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,29 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
ViewContext = viewContext;
|
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)
|
public string Encode(string value)
|
||||||
{
|
{
|
||||||
return (!string.IsNullOrEmpty(value)) ? WebUtility.HtmlEncode(value) : string.Empty;
|
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;
|
return value != null ? WebUtility.HtmlEncode(value.ToString()) : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string FormatValue(object value, string format)
|
public string FormatValue(object value, string format)
|
||||||
{
|
{
|
||||||
return ViewDataDictionary.FormatValue(value, format);
|
return ViewDataDictionary.FormatValue(value, format);
|
||||||
|
|
@ -183,7 +205,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
additionalViewData);
|
additionalViewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual HtmlString Name(string name)
|
public virtual HtmlString Name(string name)
|
||||||
{
|
{
|
||||||
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(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)
|
public HtmlString TextBox(string name, object value, string format, IDictionary<string, object> htmlAttributes)
|
||||||
{
|
{
|
||||||
return GenerateTextBox(metadata: null, name: name, value: value, format: format,
|
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);
|
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)
|
protected string EvalString(string key, string format)
|
||||||
{
|
{
|
||||||
return Convert.ToString(ViewData.Eval(key, format), CultureInfo.CurrentCulture);
|
return Convert.ToString(ViewData.Eval(key, format), CultureInfo.CurrentCulture);
|
||||||
|
|
@ -397,6 +427,56 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
return new Dictionary<string, object>();
|
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,
|
protected virtual HtmlString GenerateTextBox(ModelMetadata metadata, string name, object value, string format,
|
||||||
IDictionary<string, object> htmlAttributes)
|
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>
|
/// </summary>
|
||||||
ViewDataDictionary<TModel> ViewData { get; }
|
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>
|
/// <summary>
|
||||||
/// Returns HTML markup for each property in the object that is represented by the expression, using the specified
|
/// 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.
|
/// template, HTML field ID, and additional view data.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue