Html.BeginForm in MVC3 is rendering too much - asp.net-mvc

I have the following code. Note that I am not using a using as I want to begin and end the form in two different areas of my code. Has anyone seen this action where the helper adds "System.Web.Mvc.Html.MvcForm". I cannot see why this is added.
Code:
#Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue })
#{ Html.EndForm() }
Result:
<form action="/adminTests/Edit" method="post">System.Web.Mvc.Html.MvcForm
When I use the following I don't get the "System.Web.Mvc.Html.MvcForm"
#using (Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue }))
{
}

You need to wrap that in a using statement. BeginForm writes directly to the response stream, it isn't intended to return a string like the other helpers. The way you are using it, it is writing it's output, then returning the MvcForm object which, when disposed, will write the closing form tag. Instead, your syntax forces it to call ToString on the returned object resulting in the output you see.
#using(Html.BeginForm(...
{
...
}
Edit: Since you can't wrap it in a using statement, you need to either explicitly close the form with HTML or call EndForm later. In either case you have to use the BeginForm (and EndForm) in a code block instead of using it with the string output syntax. Both of these methods write directly to the response stream.
#{ Html.BeginForm() .. }
</form> or #{ Html.EndForm() }
Here's the relevant bits of the code for those who think I'm wrong (without permission):
public static void EndForm(this HtmlHelper htmlHelper) {
HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response;
httpResponse.Write("</form>"); // <-- look ma, Response.Write()
}
private static MvcForm FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes) {
TagBuilder 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), true);
HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response;
httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag)); <-- and here
return new MvcForm(htmlHelper.ViewContext.HttpContext.Response);
}

The BeginForm/EndForm helpers don't return a IHtmlResult so you should use them like this:
#{ Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue }); }
...
#{ Html.EndForm(); }

Try something like this:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<p>
#Html.LabelFor(model => model.Username) <br />
#Html.EditorFor(model => model.Username)
#Html.ValidationMessageFor(model => model.Username)
</p>
<p>
#Html.LabelFor(model => model.Password) <br />
#Html.EditorFor(model => model.Password) #Html.RouteLink("Forgot Password", "password") <br />
#Html.ValidationMessageFor(model => model.Password)
</p>
<p class="mtop10">
<div class="float-left small blue super-button">
<a class="auto-submit" href="#">Login</a>
</div>
<div class="alt-action">
or #Html.RouteLink("Register", "register")
</div>
</p>
</fieldset>
}

I also saw System.Web.Mvc.Html.MvcForm when using:
Html.BeginForm("Index", "Home", FormMethod.Post)
The following change fixed it:
Html.BeginForm("Index", "Home", "POST");
Seems like FormMethod.Post forces the unwanted output but I can't explain it better than that. The change worked for me though.

Related

BeginForm<TController> won't post model first time round

I have an issue using the latest mvc futures BeginForm extension. I have disabled clientsidevalidation as I have read that there may still be a bug with it. Basically the first time it gets posted the model is null, the second time (and any time after that) it will post the model fine.
It works if I switch it back to #using(Html.BeginForm()) or #using(Html.BeginForm("action", "controller" etc but I would rather make use of the strongly typed version. Here is my code:
Controller
[HandleError]
public class HomeController : BaseServiceController<IUserService>
{
public HomeController(IUserService service, IMapper mapper, ICustomPrincipal user)
: base(service, mapper, user)
{}
[AllowAnonymous]
[HttpGet]
public ActionResult Logon(string ReturnUrl)
{
LogonModel model = new LogonModel() { ReturnUrl = ReturnUrl };
return View(model);
}
[AllowAnonymous]
[HttpPost]
public ActionResult Logon(LogonModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
SfUser sfUser = service.Logon(model.UserName, model.Password);
if (sfUser == null)
{
ModelState.AddModelError("General", "Username or Password incorrect");
return View(model);
}
Response.Cookies.Add(TicketMaster.setAuthCookie(new CustomPrincipalSerializeModel(sfUser)));
return Redirect(model.ReturnUrl);
}
View
#model LogonModel
#{
ViewBag.Title = "Logon";
}
//#using(Html.BeginForm()) //Works
#using(Html.BeginForm<HomeController>(c => c.Logon(Model)))
{
#Html.HiddenFor(m => m.ReturnUrl)
<div class="editor-label">
#Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<br />
<p>
<input type="submit" value="Login" /><br />
#Html.ValidationSummary(true)
</p>
}
I could understand the problem if it always posted a null model, but only on first post? Driving me mad.
I know this is old and probably already solved somehow, but you should pass null instead of Model to the expression. That way, the model will be built using the form values.
It took me a few hours to find this. Go to the Strong Typed Html BeginForm<TController> section. This is the key point:
You want to pass your values within the form’s scope, not the
BeginForm method itself.
In your case, just change
#using(Html.BeginForm<HomeController>(c => c.Logon(Model)))
to
#using(Html.BeginForm<HomeController>(c => c.Logon(null)))

Create new parent and child on the same page

My MVC application has a classic parent-child (master-detail) relations.
I want to have a single page that create both the new parent and the children on the same page. I have added an action the returns a partial view that and adds the child HTML to the parent’s view, but I don’t know how to associate the newly created child in the action to the original parent (in other word, how to I add the new child entity to the collection of these entities in the parent entity).
I guess that when I submit the form the action should get the parent entity with the newly created children in its collection.
So to make things short, what should be the code of the action that creates the child entity and how is the child added to its parent collection?
I have read a lot of posts here (and other sites) and couldn’t find an example.
The application uses MVC 4 and Entity Framework 5.
Code sample (I removed some of the code the keep it simple).
The model is Form (parent) and FormField (child) entities.
public partial class Form
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<FormField> FormFields { get; set; }
}
public partial class FormField
{
public int ID { get; set; }
public string Name { get; set; }
public int FormID { get; set; }
}
The following partial view (_CreateFormField.cshtml) creates new FormField (child).
#model FormField
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>FormField</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.FormID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormID)
#Html.ValidationMessageFor(model => model.FormID)
</div>
</fieldset>
}
And the following view (Create.cshtml) is the one the creates the Form.
#model Form
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Form</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div>
#Html.ActionLink(
"Add Field",
"CreateFormField",
new { id = -1},
new { #class = "form-field" })
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="CreateFormField"></div>
#section Scripts {
<script>
$(function () {
$('.form-field').on('click', function (e) {
$.get($(this).prop('href'), function (response) {
$('#CreateFormField').append(response)
});
e.preventDefault();
});
});
</script>
#Scripts.Render("~/bundles/jqueryval")
}
The following actions handle the creation in the FormController.
[HttpPost]
public ActionResult Create(Form form)
{
if (ModelState.IsValid)
{
db.Forms.Add(form);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(form);
}
public ActionResult CreateFormField(string id = null)
{
// I guess something is missing here.
return PartialView("_CreateFormField", new FormField());
}
Thanks in advance,
Sharon.
I think the best and simplest way for you is that you have a view for creating Form and at the bottom of it put a fieldset to assign FormFields to it.
For the fieldset, you should have two partial views: One for create and another for edit. The partial view for creating should be something like this:
#model myPrj.Models.Form_FormFieldInfo
#{
var index = Guid.NewGuid().ToString();
string ln = (string)ViewBag.ListName;
string hn = ln + ".Index";
}
<tr>
<td>
<input type="hidden" name="#hn" value="#index" />
#Html.LabelFor(model => model.FormFieldID)
</td>
<td>
#Html.DropDownList(ln + "[" + index + "].FormFieldID",
new SelectList(new myPrj.Models.DbContext().FormFields, "ID", "FieldName"))
</td>
<td>
<input type="button" onclick="$(this).parent().parent().remove();"
value="Remove" />
</td>
</tr>
By calling this partial view in the create place view ajaxly, you can render some elements for each tag. Each line of elements contains a label, a DropDownList containing tags, and a remove button to simply remove the created elements.
In the create place view, you have a bare table which will contain those elements you create through the partial view:
<fieldset>
<legend>Form and FormFields</legend>
#Html.ValidationMessageFor(model => model.FormFields)</label>
<table id="tblFields"></table>
<input type="button" id="btnAddTag" value="Add new Field"/>
<img id="imgSpinnerl" src="~/Images/indicator-blue.gif" style="display:none;" />
</fieldset>
and you have the following script to create a line of elements for each tag:
$(document).ready(function () {
$("#btnAddField").click(function () {
$.ajax({
url: "/Controller/GetFormFieldRow/FormFields",
type: 'GET', dataType: 'json',
success: function (data, textStatus, jqXHR) {
$("#tblFields").append(jqXHR.responseText);
},
error: function (jqXHR, textStatus, errorThrown) {
$("#tblFields").append(jqXHR.responseText);
},
beforeSend: function () { $("#imgSpinnerl").show(); },
complete: function () { $("#imgSpinnerl").hide(); }
});
});
});
The action method GetFormFieldRow is like the following:
public PartialViewResult GetFormFieldRow(string id = "")
{
ViewBag.ListName = id;
return PartialView("_FormFieldPartial");
}
and your done for the create... The whole solution for your question has many codes for views, partial views, controllers, ajax calls and model binding. I tried to just show you the way because I really can't to post all of them in this answer.
Here is the full info and how-to.
Hope that this answer be useful and lead the way for you.
You can use #Html.RenderPartial(_CreateFormField.cshtml) htlper method inside your parent cshtml page.
For mode info, http://msdn.microsoft.com/en-us/library/dd492962(v=vs.118).aspx
I am providing an pseudo code example,
#Foreach(childModel in model.FormFields)
{
#Html.RenderPartial("childView.cshtml","Name", childModel)
}
Please try it in proper c# syntactical way and you will get your partial views rendered for each collection item.
The problem is that your partial view needs to use:
#model Form //Instead of FormField
and then inside of the partial view you must use model => model.FormField.x
<div class="editor-label">
#Html.LabelFor(model => model.FormField.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormField.Name)
#Html.ValidationMessageFor(model => model.FormField.Name)
</div>
This only works for one-to-one relationships (or so I read in this thread: Here). It also does not matter if you have #Html.Form inside of the partial view.
After making those changes I had no problem getting all of the posted data back to the controller.
EDIT: I've run into problems with this as anyone would soon figure out.
The better way to do this (that I've seen) is instead use an EditorTemplate. This way the editor stays independent of the Parent, and you can add a Form on any page, not simply a page where you are also adding a FormField.
You would then replace
#Html.Partial("view")
with
#Html.EditorFor()
Basically, I've found out that it's far better to use an #Html.EditorFor instead of a partial view when doing editing (It is called a view - not an editor).
If you want to use grid based layout you may want to try Kendo UI grid
Use jQueryto add FormField on click of button.
Similar I've used it as follows
<div class="accomp-multi-field-wrapper">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>FormId</th>
<th>Remove</th>
</tr>
</thead>
<tbody class="accomp-multi-fields">
<tr class="multi-field">
<td> <input name="FormFields[0].Name" type="text" value=""></td>
<td> <input name="FormFields[0].FormId" type="text" value=""></td>
<td> <button type="button" class="remove-field">X</button></td>
</tr>
</tbody>
<tr>
<th><button type="button" class="add-field btn btn-xs btn-primary addclr"><i class="fa fa-plus"></i> Add field</button> </th>
</tr>
</table>
</div>
and jQuery is as follows for adding field and removing
<script>
$('.accomp-multi-field-wrapper').each(function () {
var $wrapper = $('.accomp-multi-fields', this);
$(".add-field", $(this)).click(function (e) {
var $len = $('.multi-field', $wrapper).length;
$('.multi-field:first-child', $wrapper).clone(false, true).appendTo($wrapper).find('select,input,span').val('').each(function () {
$(this).attr('name', $(this).attr('name').replace('\[0\]', '\[' + $len + '\]'));
});
$(document).on("click",'.multi-field .remove-field', function () {
if ($('.multi-field', $wrapper).length > 1) {
$(this).closest('tr').remove();
//
$('.multi-field', $wrapper).each(function (indx) {
$(this).find('input,select,span').each(function () {
$(this).attr('name', $(this).attr('name').replace(/\d+/g, indx));
})
})
}
});
</script>
Hope so this is going to help you.

Creating Some thing #using (Html.BeginForm()) Helpers

In Asp.net MVC3 when you write below code , it generates wrapping html itself
#using (Html.BeginForm()) {
#Html.ValidationMessageFor(model => model.Text)
}
It generates codes in below format,
<form method="post" action="/Feeds">
<!-- Fields Here -->
</form>
My question in #using (Html.BeginForm()) automatically adds <form> tag at beginning and end, how can i create something like that of my own.
I am looking for some thing like below
#using (Html.BeginMYCUSTOMDIV())
{
I am text inside div
}
Expected Generated Output
<div class="customDivClass">
I am text inside div
</div>
Something along the lines:
public class MyDiv : IDisposable
{
private readonly TextWriter _writer;
public MyDiv(TextWriter writer)
{
_writer = writer;
}
public void Dispose()
{
_writer.WriteLine("</div>");
}
}
public static class MyExtensions
{
public static MyDiv BeginMYCUSTOMDIV(this HtmlHelper htmlHelper)
{
var div = new TagBuilder("div");
div.AddCssClass("customDivClass");
htmlHelper.ViewContext.Writer.WriteLine(div.ToString(TagRenderMode.StartTag));
return new MyDiv(htmlHelper.ViewContext.Writer);
}
}
and in the view:
#using (Html.BeginMYCUSTOMDIV())
{
<span>Hello</span>
}
generates:
<div class="customDivClass">
<span>Hello</span>
</div>
If I'm not mistaken, Html.BeginForm() returns an IDisposable object. When used in the using block, the object's Disposemethod is called, which is the responsible to write the closing tag to the output.
how does using HtmlHelper.BeginForm() work?
Html.BeginForm() type of extension

Razor: Cannot render Html.Label helper in a #Section (outputting source only)

I was unable to figure out how to use #class inside Html.LabelFor, so I updated an extension helper for Html.Label with code found on SO.
My LabelExtension.cs file has the following class:
public static class LabelExtensions
{
public static string Label(this HtmlHelper helper,
string labelFor,
string value,
object htmlAttributes)
{
TagBuilder labelBuilder = new TagBuilder("label");
if (!string.IsNullOrEmpty(labelFor))
{
labelBuilder.Attributes.Add("for", labelFor);
}
labelBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
labelBuilder.SetInnerText(value);
return labelBuilder.ToString(TagRenderMode.Normal);
}
}
I originally used the following in a MVC2 .aspx page thusly (which I am converting to .cshtml):
<% Html.BeginForm(); %> <%= Html.Label("txt_name_udq", "Your First Name (required):",
new {
#class
= "mylabelstyle" } )%>
<br />
<%= Html.TextBox("txt_name_udq", null, new {
#class
= "myinputstyle" } )%>
<br />
<%= Html.Label("txt_email_udq", "Your E-Mail Address (required):", new {
#class
= "mylabelstyle" } )%>
<br />
<%= Html.TextBox("txt_email_udq", null, new {
#class
= "myinputstyle" })%>
<br />
<% Html.EndForm(); %>
This rendered very well in MVC2 (I left out usings and such for brevity) . However, today while converting to .cshtml (using a _layout.cshtml) I found that the Html.Label is not rendering and instead outputting source. Here is the code:
#section QuoteForm
{
<div class="entryfull">
<div class="entryfull_box">
<div class="entryfull_text">
<h2>
Quote Request
</h2>
<ul class="ul-check">
<li>Free</li>
<li>Confidential</li>
<li>Anonymous</li>
<li>No Obligation</li>
</ul>
#using (Html.BeginForm())
{
#Html.Label("txt_name_udq", "Your First Name (required):", new { #class = "mylabelstyle" })
<br />
#Html.TextBox("txt_name_udq", null, new { #class = "myinputstyle" })
<br />
#Html.Label("txt_email_udq", "Your E-Mail Address (required):", new { #class = "mylabelstyle" })
<br />
#Html.TextBox("txt_email_udq", null, new { #class = "myinputstyle" })
<br />
}
</div>
</div>
</div>
}
The above is just something simple, not final product. I am just trying to get it to render first. Note: 1) I tried various iterations of Html.BeginForm; and 2) I even enclosed it in . Still, its not "working".
Here is what you see on the browser for the label (source outputted in browswer), right above the textbox (which renders):
<label class="mylabelstyle" for="txt_name_udq">Your First Name (required):</label>;
And if you "View Source", here is what you see:
<label class="mylabelstyle" for="txt_name_udq">Your First Name (required):</label>;
Is this something to do with #section? Or is it that I am trying to use an extension from MVC2?
Thanks in advance for any thoughts/advice.
EDIT: This is the code I reference in the comment that worked in my situation. Thanks for the help.
public static MvcHtmlString Label(this HtmlHelper htmlHelper, string labelFor, string value,
object htmlAttributes)
{
var tagBuilder = new TagBuilder("label");
tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
tagBuilder.MergeAttribute("for", labelFor.Replace(".", tagBuilder.IdAttributeDotReplacement), true);
tagBuilder.SetInnerText(value);
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
That is because your helper is returning a string and that is encoded by default. Your helper should return MvcHtmlString and change the return statement to
return new MvcHtmlString(labelBuilder.ToString(TagRenderMode.Normal));

PartialView Dynamic BeginForm Parameters

If I have the below PartialView
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.Photo>" %>
<% using (Html.BeginForm("MyAction", "MyController", FormMethod.Post, new { enctype = "multipart/form-data" })) { %>
<%= Html.EditorFor( c => c.Caption ) %>
<div class="editField">
<label for="file" class="label">Select photo:</label>
<input type="file" id="file" name="file" class="field" style="width:300px;"/>
</div>
<input type="submit" value="Add photo"/>
<%} %>
As you can see, the Action and the Controller are hard coded. Is there a way I can make them dynamic?
My goal is to have this partial view generic enough that I can use it in many places and have it submit to the Action and Controller it is sitting within.
I know I can use ViewData but really don't want to and likewise with passing a VormViewModel to the view and using the model properties.
Is there a nicer way than the two I listed above?
I Checked the source code of MVC and dig into the System.Web.Mvc --> Mvc --> Html --> FormExtensions so I find you can write some code like :
public static class FormHelpers
{
public static MvcForm BeginFormImage(this HtmlHelper htmlHelper, IDictionary<string, object> htmlAttributes)
{
string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
return FormHelper(htmlHelper, formAction, FormMethod.Post, htmlAttributes);
}
public static MvcForm FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes)
{
TagBuilder tagBuilder = new TagBuilder("form");
tagBuilder.MergeAttributes(htmlAttributes);
// action is implicitly generated, so htmlAttributes take precedence.
tagBuilder.MergeAttribute("action", formAction);
tagBuilder.MergeAttribute("enctype", "multipart/form-data");
// method is an explicit parameter, so it takes precedence over the htmlAttributes.
tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
MvcForm theForm = new MvcForm(htmlHelper.ViewContext);
if (htmlHelper.ViewContext.ClientValidationEnabled)
{
htmlHelper.ViewContext.FormContext.FormId = tagBuilder.Attributes["id"];
}
return theForm;
}
}
I'm not sure this is exactly what you really want to get but I'm sure you can get it if you change this lines in way satisfies your needs.
Hope this helps.

Resources