Why are my querystring parameters being doubled up in the post body of my ASP.NET MVC 4 Application? - asp.net-mvc

Here are the relevant bits of my
Viewmodel:
[Display(Name = "One Per Line")]
public bool OnePerLine { get; set; }
[Display(Name = "Comma Separated")]
public bool CommaSeparated { get; set; }
[Display(Name="Upper Case")]
public bool UpperCase { get; set; }
View:
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
.
.
<div class="control-group">
#Html.LabelFor(m => m.OnePerLine, "One Per Line?", new Dictionary<string, object> { { "class", "control-label" } })
<div class="controls">
#Html.CheckBoxFor(m => m.OnePerLine)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.CommaSeparated, "Comma separate them?", new Dictionary<string, object> { { "class", "control-label" } })
<div class="controls">
#Html.CheckBoxFor(m => m.CommaSeparated)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.UpperCase, "Uppercase?", new Dictionary<string, object> { { "class", "control-label" } })
<div class="controls">
#Html.CheckBoxFor(m => m.UpperCase)
</div>
</div>
.
.
}
And here's what I see in fiddler during the post:
OnePerLine=true&OnePerLine=false&CommaSeparated=true&CommaSeparated=false&UpperCase=true&UpperCase=false
When this gets to my controller the viewmodel has the correct values from the view, I just find the doubling up odd in the post body.

Dug around some more and found the answer here.
Turns out the Html.Checkbox helper renders an addition hidden input to address scenarios where unchecked checkboxes are not sent in the request.

Related

MVC parent child kind of model form submit doesn't send child collection to controller

I have a company model and it has employees list model as shown below
public class Company
{
[Required]
[Display(Name = "Company Name")]
public string Name { get; set; }
public List<EmployeeModel> Managers { get; set; }
}
and the Employee model as below
public class EmployeeModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
and my parent view is as shown below
#using (Html.BeginForm("CompanySignupSuccess", "Home", FormMethod.Post, new { #class = "horizontal-form", role = "form", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary("", new { #class = "text-danger" })
<div>
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "control-label" })<span class="required">*</span>
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
</div>
<div class="form-group">
<label for="file">Logo:</label>
<input type="file" name="logo" id="logo" accept=".png,.jpg,.jpeg" />
</div>
<div id="managerList">
<div id="editorRowsManagers">
#foreach (var item in Model.Managers)
{
#Html.Partial("DetailsView", item)
}
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-default pull-right" value="Send" />
</div>
</div>
}
and the partial view shown below
#model yourAssembly.EmployeeModel
<div style="border:1px solid;margin:20px; padding:10px;">
Manager Details:
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "control-label" }) <span class="required">*</span>
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Phone, new { #class = "control-label" }) <span class="required">*</span>
#Html.TextBoxFor(m => m.Phone, new { #class = "form-control phoneno" })
</div>
</div>
When I click on submit button, the model that goes to controller does have only Name and Logo properties filled in and the list object(Managers) is null, so I am not sure what is that I am missing here. BTW, I used the list of employees , because I would like add more employees by having a 'Add' button, and the Add button will just render another partial view.
public ActionResult CompanySignupSuccess(Company model)
{
if (ModelState.IsValid)
{
//do some process
}
else
{
ModelState.AddModelError("", "Invalid Data entered.");
}
// If we got this far, something failed, redisplay form
return View("CompanySignup", Model);
}
Can anyone please help me on how to send the child list object along with some properties on parent class when the Submit button is hit.
You cannot use a partial to generate controls for a collection unless you pass the HtmlFieldPrefix (refer this answer for an example). However the correct way to do this is to use an EditorTemplate. Rename your partial to EmployeeModel.cshtml (i.e. to match the name of the class) and move it to the /Views/Shared/EditorTemplates folder (or /Views/YourControllerName/EditorTemplates folder).
Then replace your loop in the view with
#Html.EditorFor(m => m.Managers)
which will correctly generate the necessary name attributes for binding, i.e
<input ... name="Managers[0].Name" />
<input ... name="Managers[1].Name" />
etc (currently all your generating is duplicate name attributes (and duplicate id attributes which is invalid html)

Strange Behavour with MVC Razor Syntax

I have a contact us form which is reused for varying purposes in the site
The rule is that if certain fields are already pre-populated ie the email address or subject is known then these should not be displayed ie becomes a HiddenFor
For the email this works fine, but subject it doesn't and im completely confused as they are identical string fields both required with a MaxLength of 100
#model List<TJI.ObjectModel.Entities.Form>
...
if (string.IsNullOrEmpty(Model.Email))
{
<div class="form-group">
#Html.LabelFor(model => model.Email, new {#class = "control-label col-sm-3 col-md-2"})
<div class="col-sm-8 col-md-4">
#Html.TextBoxFor(model => model.Email, new {#class = "form-control"})
</div>
<div class="col-sm-1 col-md-1">
#Html.ValidationMessageFor(m => m.Email, "*")
</div>
</div>
}
else
{
<div>
#Html.HiddenFor(model => model.Email)
</div>
}
if (string.IsNullOrEmpty(Model.Subject))
{
<div class="form-group">
#Html.LabelFor(model => model.Subject, new {#class = "control-label col-sm-3 col-md-2"})
<div class="col-sm-8 col-md-9">
#Html.TextBoxFor(model => model.Subject, new {#class = "form-control"})
</div>
<div class="col-sm-1 col-md-1">
#Html.ValidationMessageFor(model => model.Subject, "*")
</div>
</div>
}
else
{
<div>
#Html.HiddenFor(model => model.Subject)
</div>
}
...
public class Form : EntityBase
{
...
[Required]
[MaxLength(100)]
public string Email { get; set; }
[Required]
[MaxLength(100)]
public string Subject { get; set; }
....
}
After all that i realized i didn't pass the subject accross to the ViewModel in the controller
public ActionResult Contact(string contactTypeEnum, string subject, string body, string department )
{
if (string.IsNullOrEmpty(department))
{
department = Department.None.ToString();
}
var user = GetCurrentUser();
var model = new FormViewModel
{
Subject = subject,
Department = (Department) Enum.Parse(typeof (Department), department)
};
if (user != null)
{
model.Name = user.Name;
model.Email = user.Email;
}
CacheEntity(model);
return IsAjax() ? (ActionResult) PartialView("ContactDetail", model) : View(model);
}

What is the preferred way to standardise complex html views for data types?

I have code like this that I repeat through many MVC editing views. This example is the default way we display a checkbox, but similar repetition is found with other input types.
<div class="form-group">
#Html.LabelFor(model => model.IsLive, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-8 checkbox">
<div class="col-xs-1">
#Html.EditorFor(model => model.IsLive)
</div>
<div class="col-xs-10">
#Html.CheckboxLabelFor(model => model.IsLive)
</div>
</div>
<a class="infoonclick col-md-1" title="#Html.DisplayNameFor(model => model.IsLive)" data-content="#Html.DescriptionFor(model => model.IsLive)">
<span class="fa fa-info-circle"></span>
</a>
</div>
I am wondering what is the best way to DRY and standardise this?
I want to do something like #Html.DefaultCheckboxEditorFor(model => model.IsLive)
I tried creating a custom HtmlHelper, but this seemed to involve too many hard coded strings to be a good idea.
Rather I feel I should be using EditorTemplates for this, but I can't quite get the syntax right. The model for the view is a bool, but I need to get property specific stuff like the display name and descriptions.
#model bool
<div class="form-group">
#Html.LabelFor(model => model.IsLive, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-8 checkbox">
<div class="col-xs-1">
#Html.EditorFor(model => model.IsLive)
</div>
<div class="col-xs-10">
#Html.CheckboxLabelFor(model => model.IsLive)
</div>
</div>
<a class="infoonclick col-md-1" title="#Html.DisplayNameFor(model => model.IsLive)" data-content="#Html.DescriptionFor(model => model.IsLive)">
<span class="fa fa-info-circle"></span>
</a>
</div>
I have a project where most of my views look like:
(This also works with multi-level deep complex objects, but not with any type of collection, like IEnumerable, although it could be modified to do so)
<h3>Edit existing page</h3>
<div class="col-xs-12">
#using (Html.BeginForm("Edit", "Page", FormMethod.Post, new { role = "role" }))
{
#Html.EditorForModel()
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
I think that's pretty cool. So the model looks like:
public class PageEditViewModel
{
[Editable(false)]
[DisplayName("Page Id")]
public Guid Id { get; set; }
[Editable(false)]
[DisplayName("Url to resource (format: '/my-resource' or '/sub/resource)'")]
public string Url { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Maximum Length of 50 Exceeded.")]
[DisplayName("Title for page (must match Url ex: 'My Resource' or 'Sub Resource'")]
public string PageTitle { get; set; }
[MaxLength(int.MaxValue, ErrorMessage = "Content Exceeded Maximum Length")]
[DataType(DataType.MultilineText)]
public string Content { get; set; }
}
I have some editor templates:
\Views\Shared\EditorTemplates\multilinetext.cshtml
#model object
#{
var htmlAttributes = this.ViewData.ModelMetadata.GetHtmlAttributes();
}
<div class="form-group #Html.ErrorClassFor(m => m, "has-error")">
#Html.LabelFor(m => m, new { #class = "control-label" })
<div class="controls">
#Html.TextAreaFor(
m => m,
8, 8,
htmlAttributes)
#Html.ValidationMessageFor(m => m, null, new { #class = "help-block" })
</div>
</div>
And it all magically works with the a modified version of object.cshtml:
#model object
#using System.Text;
#using System.Data;
#{
ViewDataDictionary viewData = Html.ViewContext.ViewData;
TemplateInfo templateInfo = viewData.TemplateInfo;
ModelMetadata modelMetadata = viewData.ModelMetadata;
System.Text.StringBuilder builder = new StringBuilder();
string result;
// DDB #224751
if (templateInfo.TemplateDepth > 2)
{
result = modelMetadata.Model == null ? modelMetadata.NullDisplayText
: modelMetadata.SimpleDisplayText;
}
foreach (var prop in modelMetadata.Properties.Where(pm =>
pm.ShowForEdit
//&& pm.ModelType != typeof(System.Data.EntityState)
&& !templateInfo.Visited(pm)
)
.OrderBy(pm => pm.Order))
{
//Type modelType = Model.GetType();
Type modelType = modelMetadata.ModelType;
System.Reflection.PropertyInfo pi = modelType.GetProperty(prop.PropertyName);
System.ComponentModel.DataAnnotations.DisplayAttribute attribute = pi.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false).FirstOrDefault() as System.ComponentModel.DataAnnotations.DisplayAttribute;
if (attribute != null
&& !string.IsNullOrWhiteSpace(attribute.GetGroupName()))
{
//builder.Append(string.Format("<div>{0}</div>", attribute.GetGroupName()));
builder.Append(Html.Partial("Partial-GroupName", attribute.GetGroupName()));
}
builder.Append(Html.Editor(prop.PropertyName, prop.TemplateHint ?? prop.DataTypeName).ToHtmlString());
}
result = builder.ToString();
}
#Html.Raw(result)
Example output:
My EditFor templates are versions of MacawNL BootstrapEditorTemplates (which I have no affiliation with).

Error while inserting floating point value in textboxfor MVC

I have a strange problem, I have a model definied like this:
public class AddEventModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Nazwa wydarzenia")]
public string EventName { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Rodzaj")]
public string EventType { get; set; }
[Required]
[DataType(DataType.DateTime)]
[Display(Name = "Data")]
public System.DateTime EventDate { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Widocznosc")]
public string IsPublic { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Minimalny wiek")]
public int MinimalAge { get; set; }
[Display(Name = "Cena")]
[DataType(DataType.Text)]
public decimal Payment { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Opis")]
public string Description { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Miasto")]
public string City { get; set; }
public SelectList EventTypeList { get; set; }
}
What is more i have a page writen in razor like this (i will post just part of it):
<div class="form-group">
#Html.LabelFor(m => m.EventName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.EventName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.EventType, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.EventType, Model.EventTypeList, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.EventDate, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.EventDate, new { #class = "form-control", id = "datepicker" })
</div>
<script type="text/javascript">
$(function () {
$('#datepicker').datetimepicker({
minDate: moment()
});
});
</script>
</div>
<div class="form-group">
#Html.LabelFor(m => m.IsPublic, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<table>
<tr>
<td><label>#Html.RadioButtonFor(m => m.IsPublic, "Prywatne") Prywatne</label></td>
</tr>
<tr>
<td><label>#Html.RadioButtonFor(m => m.IsPublic, "Publiczne") Publiczne</label></td>
</tr>
</table>
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.MinimalAge, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.MinimalAge, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Payment, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Payment, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Description, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextAreaFor(m => m.Description, new { #class = "form-control" })
</div>
</div>
</div>
Okay, so, when I am inserting my new event with a floating point number in field Cena/Payment im getting a strange error like this:
The ViewData item that has the key 'EventType' is of type 'System.String' but must be of type 'IEnumerable'.
It is pretty strange beacuse it is pointing on dropdownlist which is not connected with a Payment field. As I said, when I put a integer into Payment field - everything works fine.
Whats going on guys?
EDIT
Okey guys, i get it that i didnt reassing a SelectList in a post method. I fixed that already. However:
I want to understand why it was apearing only when i put a floating point number in a textbox
How to fix next problem: Value 'x.x' is not valid for Cena
The error is throw because the value of EventTypeList in
#Html.DropDownListFor(m => m.EventType, Model.EventTypeList, new { #class = "form-control" }
is null
This is happening because when you post back, ModelState is invalid and you return the view without re-assigning the SelectList (as you did in the GET method).
The reason ModelState is invalid is because the culture on the server does not accept the . (dot) character as a decimal separator (most likely it is a culture that uses , (comma) as the decimal separator). You need to change the culture in the web.config file, for example to <<globalization culture ="en-US" />

ASP.NET MVC: Values are null when they reach the Controller

So I have the following Controller:
[HttpPost]
public ActionResult CreateSupport(CreateSupport model)
{
if (ModelState.IsValid && (model.Description != null))
{
model.CreatedById = UserId;
model.ModifiedById = UserId;
}
return View(model);
}
I have the following view:
#using (Html.BeginForm("CreateSupport", "Support", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="editor-label">
#Html.LabelFor(model => model.Subject, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Subject)
#Html.ValidationMessageFor(model => model.Subject)
</div>
<div class="support-form-left">
<div class="editor-label">
#Html.LabelFor(model => model.BrowserInfo, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.BrowserInfo)
#Html.ValidationMessageFor(model => model.BrowserInfo)
</div>
</div>
<div class="support-form-right">
<div class="editor-label">
#Html.LabelFor(model => model.DatabaseVersion, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.DatabaseVersion)
#Html.ValidationMessageFor(model => model.DatabaseVersion)
</div>
</div>
<div class="clearFloat"></div>
<div class="editor-label">
#Html.LabelFor(model => model.Description, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="actionButtons">
<button id="btnCancel" class="myButtonCancel">Cancel</button>
<input type="submit" value="Submit" class="myButton" />
</div>
#if (ViewBag.SuccessMessage != null)
{
<div>
<label style="color: red;">#ViewBag.SuccessMessage</label>
</div>
}
</fieldset>
}
Here's the Model:
public class CreateSupport : SupportTicket
{
public CreateSupport()
{
ProductList = new List<Product>();
ProductVersionsList = new List<ProductVersion>();
EnviromentList = new List<InstallationEnvironment>();
content = new Content();
}
[Required]
[UIHint("tinymce_jquery_full"), AllowHtml]
public string Description { get; set; }
[Required]
[DisplayName("Browser version Info.")]
public string BrowserInfo { get; set; }
[Required]
[DisplayName("Database Version")]
public string DatabaseVersion { get; set; }
public Content content { get; set; }
}
The problem is that the values that reach the Controller are NULL even if you enter some value in them.
You should check your browser's developer tools to see if the form is properly posting its values. If it isn't, you should do two things:
A) Disabled javascript to see if there is a script that is interfering with the POST (typically either by disabling or clearing fields)
B) Ensuring your markup is valid using the W3C markup validation service
For input fields use
#Html.EditorFor(x => x.Subject)
For display fields use
#Html.DisplayFor(x => x.Subject)

Resources