Form Submits same checkbox values on subsequent submit action in MVC - asp.net-mvc

I have followed the suggestion in this question...
[How to handle checkboxes in ASP.NET MVC forms?
...to setup multiple checkboxes with the same name="..." attribute and the form behaves as expected the FIRST time its submitted. Subsequent submissions of the form use the original array of Guid values instead of properly sending the new array of checked item values.
Relevant code in the view...
<% foreach (ItemType itemType in ViewData.Model.ItemTypes) %>
<%{ %>
<li>
<input id="selectedItems" name="selectedItems" type="checkbox" value="<%= itemType.Id%>" />
<%= itemType.Description %></li>
<%} %>
This produces a series of checkboxes, one each for each item with the value="..." attribute set to the Id of the item.
Then in my controller action, the method signature is...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SelectItems(Guid[] selectedItems)
{...}
The first time thru the method, the selectedItems array properly holds the Guid of each item selected. But subsequent submits of the form will always still contain whatever was first selected in the initial submit action, no matter what changes you make to what it checked before you submit the form. This doesn't seem to have anything to do with my code, as inspecting the selectedItems array that the MVC framework passes to the method evidences that the framework seems to always be submitting the same value over and over again.
Close browser, start again, selecet different initial checkbox on submit and the process starts all over again (the initially-selected checkbox ids are always what's in the selectedItems argument).
Assume I must be thick and overlooking some kind of caching of form values by the framework, but I would swear this didn't behave this way in Preview 5.
Driving me nuts and probably simple issue; any ideas????

FWIW, here is what I do (not sure if it related):
// please MS, stop screwing around!!!!!!!!!!!!!!!
string r = Request.Form["r"];
Then proceed to extract the values manually from 'r'. I still use Preview 4, as they have really broken too many existing features, and not fixed reported bugs.

I'm not sure what is causing your issue, but I have a WAG...
Do you RedirectToAction in your controller's Post method?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SelectItems(Guid[] selectedItems)
{
/* lol snip */
return RedirectToAction("WhateverActionIsTheGetVersionOfThisPostAction");
}
It might serve to reset anything going on in the background... Again, wild-ass guess...

Related

Maintain state of a dynamic list of checkboxes in ASP.NET MVC

I have a class called "PropertyFeature" which simply contains PropertyFeatureID and Description. It's a proper model created through LINQ to SQL mapped to an SQL Server database table. An example instance/row would be:
PropertyFeatureID: 2
Description: "Swimming Pool"
The number of rows (PropertyFeatures) can of course grow and shrink, and so I want to dynamically render a list of checkboxes so that the user can select any of them.
I can dynamically render the Checkboxes easily enough, with something like:
<%foreach (var Feature in (ViewData["Features"] as IEnumerable<MySolution.Models.PropertyFeature>)) { %>
<%=Html.CheckBox("Features", new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%><label for="Feature<%=Feature.PropertyFeatureID%>"><%=Feature.Description%></label>
<%}%>
I specify the ID for each checkbox and render the matching label so that the user can intuitively click the label and toggle the checkbox - that works great.
I set the CheckBox's "name" to "Features" so all the checkboxes render with the same name, and the MVC Model Binder piles them into a single collection called "Features" when the form is posted. This works nicely.
Once the form is submitted, I use the checked values and store them, so I need the actual integer values so I know which PropertyFeature is selected, not just a pile of Booleans and field names. So ideally, I want it as an array or a collection that's easy to work with.
I am able to retrieve the selected values from within my Controller method when the button is clicked because I have specified the parameter as int[] Features.
But the problem is that it doesn't maintain state. That is, when I click the submit button and the page reloads (with the form again displayed) I want all of the dynamic checkboxes to retain their checked status (or not). All of the other fields that I've created with Html.DropDownList and Html.TextBox all maintain their states successfully no problems at all on the same page in the same form.
I have spent hours reading all of the other threads and articles on similar issues and there is a lot of talk about using ICollection and IDictionary to bundle things up and include a Boolean value for each item so that it can maintain the checkbox state. But I don't 100% grasp how to use that in the context of my own personal example. I would like to keep the solution really simple and not have to code up pages of new classes just to maintain my checkbox state.
What is the cleanest and proper way to do this?
I got it working after much playing around with the various different approaches.
In the view:
<%string[] PostFeatures = Request.Form.GetValues("Features");%>
<% foreach (var Feature in (ViewData["AllPropertyFeatures"] as
IEnumerable<MySolution.Models.PropertyFeature>))
{ %>
<input type="checkbox" name="Features"
id="Feature<%=Feature.PropertyFeatureID.ToString()%>"
value="<%=Feature.PropertyFeatureID%>"
<%if(PostFeatures!=null)
{
if(PostFeatures.Contains(Feature.PropertyFeatureID.ToString()))
{
Response.Write("checked=\"checked\"");
}
}
%> />
<label for="Feature<%=Feature.PropertyFeatureID%>">
<%=Feature.Description%></label> <%
} %>
In the receiving controller method:
public ActionResult SearchResults(int[] Features)
This method has a number of advantages:
Allows labels to be clicked to toggle the corresponding checkboxes (usability).
Allows the Controller method to receive a super tidy array of ints, which ONLY contains the ints that have been selected - and not a whole other pile of items which were unselected or containing false/null/blank/0 etc.
Retains the checkbox's checked state when the page reloads containing the form, i.e. the user's selection is retained.
No random/stray type=hidden input fields created from the default ASP.Net MVC Html.CheckBox helper - I know it does those for a good reason, but in this instance, I don't require them as I only want to know about which IDs have been selected and for those to be in a single, tidy int[].
No masses of additional server side bloated classes, helpers and other happy mess required to achieve such a simple thing.
I would recommend this approach for anyone wanting the cleanest / bloat-free solution for a dynamic checkbox list where you need the IDs and you just want to get down to business!
The problem is that when you are rendering your list of checkboxes, you aren't setting any of them as selected. You will need to set your int[] Features in ViewData, and then in your foreach loop, check to see if the ID of that Feature is in the array in ViewData.
something like:
<%=Html.CheckBox("Features",
((int[])ViewData["SelectedFeatures"]).Contains(Feature.PropertyFeatureID),
new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%
although I didn't test it, so it might not be 100%.

.net MVC RenderPartial renders information that is not in the model

I have a usercontrol that is rendering a list of items. Each row contains a unique id in a hidden field, a text and a delete button. When clicking on the delete button I use jquery ajax to call the controller method DeleteCA (seen below). DeleteCA returns a new list of items that replaces the old list.
[HttpPost]
public PartialViewResult DeleteCA(CAsViewModel CAs, Guid CAIdToDelete)
{
int indexToRemove = CAs.CAList.IndexOf(CAs.CAList.Single(m => m.Id == CAIdToDelete));
CAs.CAList.RemoveAt(indexToRemove);
return PartialView("EditorTemplates/CAs", CAs);
}
I have checked that DeleteCA is really removing the correct item. The modified list of CAs passed to PartialView no longer contains the deleted item.
Something weird happens when the partial view is rendered. The number of items in the list is reduced but it is always the last element that is removed from the list. The rendered items does not correspond to the items in the list/model sent to PartialView.
In the usercontrol file (ascx) I'm using both Model.CAList and lambda expression m => m.CAList.
How is it possible for the usercontrol to render stuff that is not in the model sent to PartialView?
Thanx
Andreas
It sounds like the ModelState is the trouble here, as you bind to CAs the ModelState save this values in the background as Attempted Values, so its true the object is no longer present at the Model, but the ModelSate still have the values of the deleted object. You can try a:
ModelState.Clear();
To remove all those old values.
Check in firebug what the response realy is. This way you can see if you have a serverside problem or it is a jquery issue.

ASP.NET MVC - How to get checkbox values on post

Hey all, this is a newbie ASP.NET MVC question.
I have a view that has a list of checkboxes and a submit button. On submit it posts to a controller method but I can't figure out how to get the values of the checkboxes. Also, I can't figure out how to get model data that I passed into the view when I'm in the post method, I tried using Html.Hidden but that didn't seem to work.
Here's the code:
http://pastebin.com/m2efe8a94 (View)
http://pastebin.com/m39ebc6b9 (Controller)
Thanks for any input received,
Justin
First thing I noticed is that your hidden fields need to be inside your form. Currently in your view, they are above the BeginForm, so they won't be included in the form submission.
To get the values of the selected check boxes, add an IsOffered parameter to your OfferTrade Action method.
public ActionResult OfferTrade(FormCollection result, List<string> IsOffered)
That parameter will contain a list of the ItemId's for all the checked IsOffered boxes.
The HtmlHelper's CheckBox works differently and I don't like the way it works, so I don't use it.
Making the IsOffered parameter type List<int> should also work if your ItemId field is an integer.
First of all your ItemId and UserId is outside your form:
<%= Html.Hidden("ItemId", Model.ItemIWant.ItemId) %>
<%= Html.Hidden("UserId", Model.ItemIWant.UserId) %>
//...
<% using (Html.BeginForm()) {%>
Secondly you could try to make your Controller action method use "model binding" (if this is also called model binding)
public ActionResult OfferTrade(int ItemId, int UserId, IList<string> IsOfferred)
Edit Just noticed you are not using the HtmlHelper CheckBox so your list will contain only selected items, but a point still:
You might want to look into Phil Haacks post on Model Binding To A List, but there is a small change to this in the RTM version of MVC:
You dont need the ".Index" hidden fields, but then the indexes in the Name fields must be zero-indexed and increasing (by 1).

How to get the selected value of a dropdown in the MVC View itself

I have a drop down in a MVC View, which is some thing like this:
Html.DropDownList(id, Range(0,10)
.Select(x => new SelectListItem {Text = x, Value = x}))
In the view itself, I need the selected value of this drop down. I know that I can access that in a JavaScript, but I am looking for a way to get it in the view itself from the drop down properties or something like that.
How can I access it? I tried to figure out some thing from intellisense but nothing relavant showed up, help is much appreciated.
Edit: I want the value after a few lines after the declaration of the drop down, I know that I can access it from JavaScript and by posting the form, Is there noway to access it on the view itself ?
Edit2: If its not possible to access it in view, please explain the reason, I am more interested in knowing it.
Thanks.
After reading at your question, it sounds like you want to have the drop down list supply a value for a lower section of the same page.
First and foremost, you will need to place the DropDownList within a form construct, as in:
<% using (Html.BeginForm("ProcessValue", "ThisPage")) { %>
<%= Html.DropDownList("DropID", Range(0, 10).Select(a=>new SelectListItem { Text = x, Value = x }) %>
<input type=submit value="Submit" />
<% } %>
You need to set up a few things ahead of time:
You have to have a submit button, or a similar construct, in order to retrieve the value of the DropID variable.
You need to set up an controller method that will handle the processing of the value, then redirect back to the original page with the selected value in the page's ViewData.
Finally, you need to set up the view so that the post-processing section will only display if you have a valid value in the DropID variable.
It's not as simple as just placing a DropDownList in a view and then using that value later on in the page. You have to get the controller involved to manage the data transport and you have to set up the single view to handle multiple states (ie. before the value is selected and after the selection takes place).
To get the selected value you could use either javascript or a controller action to which the form containing the select box is submitted.
The selected value will be in the FormCollection or QueryString collection with the name of the DropDown's ID in the controller action that receives the form submission from this view. You can either submit the values with a classic POST or GET or via AJAX, but you have to wire up a server-side action that processes that input.
There is a SelectList class as well wich allows you to define wich item will be selected.. and knowing that - you will know selected value..
Also.. if no value is selected explicitly, dropdowns tend to select the first item in the list.

Mapping individual buttons on ASP.NET MVC View to controller actions

I have an application where I need the user to be able to update or delete rows of data from the database. The rows are displayed to the user using a foreach loop in the .aspx file of my view. Each row will have two text fields (txtName, txtDesc), an update button, and a delete button. What I'm not sure of, is how do I have the update button send the message to the controller for which row to update? I can see a couple way of doing this:
Put each row within it's own form tag, then when the update button is clicked, it will submit the values for that row only (there will also be a hidden field with the rowId) and the controller class would take all the post values as parameters to the Update method on the controller.
Somehow, have the button be scripted in a way to send back only the values for that row with a POST to the controller.
Is there a way of doing this? One thing I am concerned about is if each row has different names for it's controls assigned by ASP.NET (txtName1, txtDesc1, txtName2, txtDesc2), then how will their values get mapped to the correct parameters of the Controller method?
You can use multiple forms, and set the action on the form to be like this:
<form method="post" action="/YourController/YourAction/<%=rowId%>">
So you will have YourController/YourAction/1, YourController/YourAction/2 and so on.
There is no need to give different names to the different textboxes, just call them txtName, txtDesc etc (or even better, get rid of those txt prefixes). Since they are in different forms, they won't mix up.
Then on the action you do something like
public ActionResult YourAction(int id, string username, string description)
Where username, description are the same names that you used on the form controls (so they are mapped automatically). The id parameter will be automatically mapped to the number you put on the form action.
You can also have multiple "valid-named" buttons on the form like:
<input type="submit" value="Save" name="btnSave" id="btnSave"/>
<input type="submit" value="Delete" name="btnDelete" id="btnDelete" /
and than check to see what submit you have received. There can be only one submit action sent per form, so it is like all the other submit buttons did not actually existed in the first place:
if ( HttpContext.Request.Form["btnDelete"] != null ) {
//Delete stuff
} elseif ( HttpContext.Request.Form["btnSave"] != null ) {
//Update stuff
}
I also think that you can implement a custom ActionMethodSelectorAttribute like here http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx (also listed above) to have cleaner separated code.
As rodbv said you want to use seperate <form> elements.
When you are using Asp.Net MVC or classic html (php, classic asp, etc) you have to forget the Asp.Net way of handling button presses. When a form is posted back to the webserver all the server knows about is simply "the form was sent, and contained the following input elements".
Asp.net (standard) adds a wrapper round many of the standard html postback actions using javascript (the __doPostback javascript function is used almost everywhere) and this adds information about which input element of the form caused the postback and delivers it to the server in a hidden form variable. You could mimic this behavior if you really so desired, but I would recomend against it.
It may seem strange 'littering' a page with many <form>'s, however it will mean that the postback to the server will be lighter weight and should make everything run that little bit faster for the user.

Resources