Here's my code:
#using (Html.BeginForm("AddMCondition", "Admin"))
{
<td class="admin-textbox">
#Html.TextBox("txtMCondition")
</td>
<td>
#foreach (var exerType in Model.AllExerciseTypes)
{
<label>
#Html.CheckBox("RestrictedType")
#exerType.Name
</label>
}
<input type="submit" value="Add Medical Condition" />
</td>
}
and this is how I'm retrieving values in the controllers
public ActionResult AddMCondition(string txtMCondition, string[] RestrictedType)
{
//Code here...
}
There are only 3 item in the AllExerciseTypes collections. I've notice that each checkbox is sending at least a false value to the controller whether it's selected or not. If I don't check any checkbox, I get 3 false in the collection. If I check 1 checkbox, I get 4 values, i.e. 1 true and 3 falses, and so on. When I check all of them, I get 3 values, i.e. True, False, True, False, True, and False.
Is there any reason, checkboxes are sending at least false in each situation?
This is because the Html Helper for a checkbox adds an extra hidden field with a value of false to ensure that something is posted back to the server even if the checkbox is not checked. If you add <input type="checkbox" name="cbSomething" value="true"/> to your form and post it back without checking the checkbox then you won't get a value sent back to the server. If you use the strongly typed helper extension (Html.CheckBoxFor(m => m.RestrictedType) for example) and bind a checkbox to a property on your model, the model binder will correctly bind the value to the model property. Here's the code snippet from the input extensions within MVC:
if (inputType != InputType.CheckBox)
return TagBuilderExtensions.ToMvcHtmlString(tagBuilder1, TagRenderMode.SelfClosing);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(tagBuilder1.ToString(TagRenderMode.SelfClosing));
TagBuilder tagBuilder2 = new TagBuilder("input");
tagBuilder2.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
tagBuilder2.MergeAttribute("name", fullHtmlFieldName);
tagBuilder2.MergeAttribute("value", "false");
stringBuilder.Append(tagBuilder2.ToString(TagRenderMode.SelfClosing));
return MvcHtmlString.Create(((object) stringBuilder).ToString());
Related
I try to pass some hidden data to my controller by using the hiddenFor, I know the value I want gets to the view, but after submiting the form the value stays null when it arrives in the controller. The data in EditorFor is passed correctly to the controller.
// View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
// Some working editorFor fields. Data from these gets successfully received
// The name is correctly displayed in the paragraph
<p>#Model.name</p>
// This data is not received in the controller
#Html.HiddenFor(x => x.name)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
// Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product, HttpPostedFileBase image)
{
product.name = "a name";
return View(product);
}
I also tried using a normal named hidden, but this also didn't return a value.
Someone an idea what I missed?
You can pass the hidden fields automatically, if you have a form, using for example the razor helper
#using (Html.BeginForm("CreateTable", "Home", FormMethod.Post, null){ #HiddenFor(i => i.PropertyName) }
and the hidden fields must be inside of form, otherwise you will "lost" them.
Update following your updated question: Try remove the HiddenField and change <p>#Model.name</p>
to
#Html.LabelFor(i => i.Name)
I did focus on the incorrect thing, the problem was that I changed the model in the controller after the postback. But this only changes the model en does not changes the ModelState, which the form data uses.
//This is updated after model changes.
<p>#Model.name</p>
//For this you need to update the ModelState
#Html.HiddenFor(x => x.name)
In the controller you need to use ModelState.Remove(property name). (Or clear the complete ModelState)
//After removal of the property the ModelState will update to the new model value.
product.name = "a name";
ModelState.Remove("name");
return View(product);
In this article it's explained, https://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes.
I am using MVC and have an Index View. One of the fields that is displayed is type Bool. Currently, it will display a True/False for its value however is there a way to display a check mark or a checked checkbox when True and nothing if False?
My Field -
public bool PrimaryContact { get; set; }
My View -
#foreach (var item in Model.Contacts)
{
<tr>
<td>#item.PrimaryContact</td>
<td>#item.ContactType</td>
<td>#item.Contact1</td>
</tr>
}
If you absolutely want to show a checkbox, you can do this.
<td> <input type="checkbox" checked="#item.PrimaryContact" /> </td>
But a checkbox is a form input control. So if this is a view only screen, why not simply print "Yes" or "No" instead of the input control.
<td>#(item.PrimaryContact?"Yes":"No")</td>
I'm trying to implement a button to adding and removing a row using static HTML. I saw this question that seems to be what I want, but I found that the example doesn't work for drop-down lists. None of the drop-down lists' options ever are marked as selected. If I don't clear the model state, all of the old values are kept. How can I keep my changes to the view-model?
// Controller action
[HttpPost]
public virtual ActionResult DoSomething(DoSomethingViewModel viewModel)
{
if (viewModel != null)
{
if (viewModel.ButtonPressed != null)
{
if (viewModel.ButtonPressed.Trim() == "Cancel")
{
return Redirect(ApplicationUtilities.CancelRequestUrl);
}
else if (viewModel.ButtonPressed.Trim() == "AddRow")
{
ModelState.Clear();
// This only covers non-JavaScript users.
// One for the newest one.
viewModel.FieldOneValues.Add(String.Empty);
viewModel.FieldTwoValues.Add(String.Empty);
viewModel.FieldThreeValues.Add(null);
return View(viewModel);
}
else if (viewModel.ButtonPressed.Trim().StartsWith("Remove"))
{
ModelState.Clear();
String[] split = viewModel.ButtonPressed.Split('-');
if (split.Length == 2)
{
Int32 indexToRemove;
Regex regex = new Regex(#"\[([0-9]+)\]");
Match match = regex.Match(split[1]);
if (match.Success && Int32.TryParse(match.Groups[1].Value, out indexToRemove))
{
viewModel.FieldOneValues.RemoveAt(indexToRemove);
viewModel.FieldTwoValues.RemoveAt(indexToRemove);
viewModel.FieldThreeValues.RemoveAt(indexToRemove);
}
}
return View(viewModel);
}
}
}
if (ModelState.IsValid)
{
return WhateverIsDoneOnSuccess(viewModel);
}
else
{
return View(viewModel);
}
}
// View Model
public class DoSomethingViewModel
{
public DoSomethingViewModel()
{
this.FieldOneValues = new List<String>();
this.FieldTwoValues = new List<String>();
this.FieldThreeValues = new List<Int32?>();
}
public virtual IList<String> FieldOneValues { get; set; }
public virtual IList<String> FieldTwoValues { get; set; }
public virtual IList<Int32?> FieldThreeValues { get; set; }
public virtual String ButtonPressed { get; set; }
}
<!-- Spark View -->
<tr each="var fieldOneValue in Model.FieldOneValues">
<td headers="FieldOneTh">${Html.TextAreaFor(m => m.FieldOneValues[fieldOneValueIndex])}</td>
<td headers="FieldTwoTh">${Html.TextAreaFor(m => m.FieldTwoValues[fieldOneValueIndex])}</td>
<td headers="FieldThreeTh">
${Html.TextBoxFor(m => m.fieldOneValueIndex], new { disabled="disabled", #readonly="readonly" })}
${Html.DropDownListFor(
m => m.FieldThreeValues[fieldOneValueIndex]
, ApplicationUtilities.FieldThreeSelectListItems
, " "
)}
</td>
<td headers="AddRemoveTh">
<button name="${Html.NameFor(m => m.ButtonPressed)}" class="Remove" type="submit" value="Remove-[${fieldOneValueIndex}]">Remove</button>
<button if="fieldOneValueIsLast" name="${Html.NameFor(m => m.ButtonPressed)}" class="Add" type="submit" value="AddRow">Add</button>
</td>
</tr>
<!-- HTML Output -->
<tr>
<td headers="FieldOneTh"><textarea cols="20" id="FieldOneValues_0_" name="FieldOneValues[0]" rows="2">
</textarea></td>
<td headers="FieldTwoTh"><textarea cols="20" id="FieldTwoValues_0_" name="FieldTwoValues[0]" rows="2">
</textarea></td>
<td headers="FieldThreeTh">
<input data-val="true" data-val-number="The field Nullable`1 must be a number." disabled="disabled" id="FieldThreeValues_0_" name="FieldThreeValues[0]" readonly="readonly" type="text" value="0" />
<select id="FieldThreeValues_0_" name="FieldThreeValues[0]"><option value=""> </option>
<option value="0">Option 1</option>
<option value="1">Option 2</option>
<option value="2">Option 3option>
</select>
</td>
<td headers="AddRemoveTh">
<button name="ButtonPressed" class="Remove" type="submit" value="Remove-[0]">Remove</button>
<button name="ButtonPressed" class="Add" type="submit" value="AddRow">Add</button>
</td>
</tr>
Plus I'm curious; I think there should be a way to do this.
There is, but you have to handle the post correctly. It's situations like these why PRG (Post-Redirect-Get) is recommended. When you click something like a remove button for a particular item, it's not appropriate to save all the other fields and do whatever else would happen when the whole form is actually submitted. All the user indicated was that they wanted to remove this one item.
Therefore, when you get the post, you remove that item from the database or wherever it's persisted and then you redirect back to the original form if that's what you want. The redirect process updates the page state so that the item is now gone and the rest of the form can then be edited without carrying around stale data. What you're trying to do is remove the item, but then just return the view directly which still has the posted item in the data backing it. That's where your problem is.
I think you went down this path because you're trying to maintain any edits the user made to other areas of the form, but that's simply not going to be possible. However, you do have some options:
Don't actually have a button that removes the item right this minute. Instead, provide a checkbox or something that indicates the item should be deleted when the user posts the entire form. Then you can save the all the form data, remove the indicated items, and redirect afterwards like you should.
Use local storage to save the user's edits on the client-side, and then read them back from local storage after the page loads again, following the redirect. However, this requires JS.
Use AJAX to submit the request to remove the item, and then remove the row from the DOM. However, this requires JS.
Also, remember that it's entirely possible to progressively enhance your form. So, you can implement #1 and #3, and then if JS isn't available, #1 still serves as a fallback.
I have 2 text box and one submit button and one grid in my app. Now i what to add the textbox values to grid while i click the submit button. How to do that?
`
[HttpPost]
public ActionResult AddToCart(string ItemID, string ItemName, CartToCart cart)
{
ViewBag.Message = "This is a partial view.";
List<Models.CartToCart> lst = new List<Models.CartToCart>();
CartToCart ct = new CartToCart();
cart.ItemID = Convert.ToInt32(ItemID);
cart.ItemName = ItemName;
lst.Add(cart);
return View(lst);
}`
View
<% using (Html.BeginForm())
{ %>
<%: Html.ValidationSummary(true)%>
<input id="ItemID" name="ItemID" type="text" />
<input id="ItemName" name="ItemName" type="text" />
<input type="submit" value="Save" />
<%= Html.Telerik().Grid(Model)
.Name("Cart")%>
<% } %>
But while i adding 2nd value 1st value replaced by 2nd value. How to append the 2nd value in grid?
Where do you store the values? Currently you're creating a new empty list on each POST and adding a single value to that list:
List<Models.CartToCart> lst = new List<Models.CartToCart>();
CartToCart ct = new CartToCart();
cart.ItemID = Convert.ToInt32(ItemID);
cart.ItemName = ItemName;
lst.Add(cart);
return View(lst);
So you only ever populate the view with the most recent value, throwing away any previous values. If you want to keep more than just the currently-POSTed value then you'll need to persist them somewhere more permanent, such as a database. So in your controller action you would insert the new POSTed value into the database. Then you'd fetch all of the records from the database as your list of values and return that list to the view.
Can someone please help me understand this. I have the following code:
The controller
public ActionResult Stuff(PersonModel model)
{
model.Address = "Some Address";
return PartialView("_Registration", model);
}
The View
#Ajax.BeginForm("Stuff", new AjaxOptions(){ HttpMethod="POST", UpdateTargetId="the_form", InsertionMode=InsertionMode.Replace, OnSuccess="Stuff" })
{
<div id="the_form">
#{Html.RenderPartial("_Registration", new TypeScriptTest.Models.PersonModel());}
</div>
<input type="submit" value="Get Addreess" />
}
The Partial View
#model TypeScriptTest.Models.PersonModel
<table>
<tr>
<td>
Name:
</td>
<td>
#Html.TextBoxFor(c => c.Name)
</td>
</tr>
<tr>
<td>
Address:
</td>
<td>
#Html.TextBoxFor(c => c.Address)
</td>
</tr>
</table>
The data is posted with whatever I type in the two fields. But then I replace the Address property value with "Some Address" and return a PartialView I would expect to get a new view that replaces the old. This probably happens, but the view that replaces the old does not include the new "Some Address" value. It just looks exactly like the view I posted.
I have a workaround, but I want to understand what is happening here. My workaround is as follows:
public ActionResult Stuff(PersonModel model)
{
model.Address = "Some Address";
var v = PartialView("_Registration");
var view = v.ViewEngineCollection.OfType<System.Web.Mvc.RazorViewEngine>().Single().FindPartialView(new ControllerContext(HttpContext, RouteData, this), "_Registration", false);
var result = new PartialViewResult() { View = view.View };
result.ViewData.Model = model;
return result;
}
Here I can bypass cache using false as the last parameter in FindPartialView. Setting this parameter to true results in the same problem as the one in discussion.
Why is this happening and what is the correct way to get around this problem? My workaround works fine, but I would like to understand the problem.
In case you are still interested, this is because the values submitted from the form are stored in the ModelState and when rendering the HtmlHelpers, any value in the ModelState takes precedence over your current model object values.
If you want to change values submitted by the user then you could either clear the modelState or clear the entry for the single property that you want to change. (Take care when clearing the model state as that will also clear the errors for any submitted value, at least you may want to make sure the ModelState is valid before manually modifying it)
I guess that with your work around you will end up having a clean ModelState without the submitted values.
You might also find useful the answers to this similar question: Asp.net MVC ModelState.Clear