Mapping individual buttons on ASP.NET MVC View to controller actions - asp.net-mvc

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.

Related

MVC 5 - Returning an unordered list to the server, along with a form

I have an MVC 5 / Bootstrap application. On one of the pages, I have a number of fields all bound to the model associated with the page. However, I also have a simple unordered list which always starts out empty and the user can then add items to it. They do this by entering some text into a type ahead field. Once the user finds what he/she is looking for, he/she can click a button and have it added to the unordered list. Any number of items can be added to the list. All of this works fine.
My question is how I can get the contents of the unordered list posted back to the server along with the rest of the form contents, since the unordered list isn't part of the model?
Here is one way to skin this cat:
A) Add a collection to your model (which really should be a ViewModel, and not a domain model) to hold those items
B) In your button's click handler, create a hidden input field that conforms to the ASP.Net Wire Format: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
If you had a collection of orders, you should end up generating controls like this:
<input type="hidden" name="Orders[0].Id" value="1" />
<input type="hidden" name="Orders[1].Id" value="2" />
Note sequential ordering is important, if you start removing items, you'll need to re-sequence your name values.
There couple of ways to work it out.
If you don't want to add to model, what I would prefer to do you can:
Directly access item that were posted via Controller.Request property;
You can post this items separately via Ajax request, and handle them in different controller action.

Multiple input fields for the same model data in ASP.NET MVC

I have a very large model for user profiles. There are about 15 different new user categories (e.g., young traveler, experienced traveler, captain, helmsman, ...). Depending on the user category, some data fields of the model are not applicable and remain null in the database.
I'm trying to populate the model through one big sign-up form. JavaScript determines the user category based on the user's previous answers and shows/hides applicable/non-applicable input fields in the form.
The following problem arose:
Depending on the user category, I need to show some input fields in a slightly different order in the form. I have solved this by creating multiple input fields for the same data field in the view (e.g., #Html.TextBoxFor(x => x.Age)) and then using JavaScript to show only one depending on the user category.
My plan was to submit only the visible input fields so that the server is not confused about which input fields to use to create the model.
Unfortunately, Visual Studio doesn't quite play along: First, there's there problem of having multiple input field with the same id; I could maybe live with that. But then, validation attributes in the HTML-source are generated only for the first occurrence of each data field in the view (e.g., #Html.TextBoxFor(x => x.Age)), not for any later occurrences.
E.g., the first occurrence generates
<input data-val="true" data-val-number="The field Age must be a number."
data-val-range="The field Age must be between 10 and 99." data-val-range-max="99"
data-val-range-min="10" id="Age" name="Age" type="text" value="">
The second occurrence generates merely
<input id="Age" name="Age" type="text" value="">
How can I solve this? My thinking:
Create all input fields hidden once at the beginning of the view and use Javascript to generate at the form dynamically. Downside: A lot of work, all page structure is now in JavaScript instead of view.
Use partial views for different user categories. Downside: When the form page is opened (in the controller), I retrieve several lists from the database and put them in the model in order to populate the form DropDown input fields and RadioButton input lists in the view. Is there any way to reuse this information in the partial views so that there are not many repetitive database queries?
Any thoughts on the situation are very much appreciated. I'm somewhat new to ASP.NET MVC.
Your view model should include a property (pehaps an enum) that defines you user category,
then in your view just render the html applicable to that category
/// Some common html
#if (model.UserCategory == "captain")
{
// controls specific to captain
}
else if (model.UserCategory = "helmsman")
{
// controls specific to helmsman
}
If you need to include this html on the same page you are determining the 'previous answers', put this in a partial view and all it using ajax (passing the user category).
Another option would be to create only one control for each property and then in the javascript that hides the control, also disable the control - disabled controls don't post back so the property will remain null
$('#MyProperty').prop('disabled', true);
Honestly, I would drop the idea of building such a beast in server technology in favor of using plain JS, KnockoutJS or AngularJS which seem to be built exact for this needs.
However, answering your questions:
You have to remember, that browser submits to the server only those fileds which are enabled. If you don wan't to "confuse" server just disable unneeded fields before submit.
Model binder of asp.net mvc finds values by name attribute not by id. You can have form with couple fields with different id attributes and one name for all of them. It will be just a bit more complex to declare them in the Razor View, but it should work.

ASP.NET MVC3 Client side validation

I am building a search form for my web application using MVC3. My form is basically divided in two sections.
1st Section has 3 search criteria. First Name, Last Name and Zip code and beneath that section there is a "Search" button which I can click and it should do a client side validation and give me an error message if any of the fields are blank.
2nd section on the same page has just one textbox - to search by "Quote Number". So that section has one textbox to enter quote number and beneath there is another button called "Search". When I click on this search button it should only validate that the Quote Number field is not empty.
I have a viewmodel which has all 4 properties (FName,LName,Zip,Quote Number) and I am binding that on the page. Both the button will post back the page (I know that there is a way to identify which button was clicked on postback). The problem I am facing is on postback everything is posting back and if I use datannotations to do RequiredField check, it does validation on all the 4 fields but I should check for which button is clicked and based on that only fire validation on either 3 fields or only on 1 fields. How do I achieve this functionality? I hope I clearly explained the issue.
Thanks
Since this is MVC, don't think of these as postbacks, think of them as submits. As they are searching by different criteria, they should really be two different forms submitting to two different actions. As they are separate actions, each can have it's own view with it's own ViewModel and validation. Then to combine them into one physical page to present to the user just use partial rendering to put them both into the same view.
Basically the view you present to the user would have something like:
#{
Html.RenderAction("SearchByName");
}
<!-- maybe some markup to visually separate them -->
#{
Html.RenderAction("SearchByQuote");
}
Also gives you the added benefit of having each action be responsible for a single task and you don't have to put in code to figure out which button was clicked, etc.
And just in case you think to yourself "Hey, since both are search, just with different number of parameters, can't I overload the Search action?" No.
Kevin,
Change your page so that you have two different forms, one for each search type. When you click submit in one form, only that form's child fields will be validated.
Then, as R0MANARMY suggested, have two separate actions, one for each search form.
counsellorben

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%.

Persisting data from multiple forms in ASP.NET MVC

I have two forms on one page: a results form and a search form. The search form uses a partial view because it is displayed on several different pages. I want to be able to persist the data in the search form regardles of which button on which form the user clicks. The problem is that when the user clicks on a link or button from the results form, only the form values from the results form are posted, the values from the search form are not included. How can I maintain the values in the search form even when it is not the form that is submitted? I do not want to use any type of session state to maintain the form and I dont want to write the search values in hidden fields in the results form. I just want to be able to post them with the form values of the results form so that the users search criteria can be maintained accross any page that displays the search partial view. What am I missing?
The first thought that occured to me is to remove the form wrapping the search control and just let it be rendered into the form with the results data. I worry here about naming conflicts. What happens when the search from has a control with the same name as the results form, wouldn't this cause a naming conflict? I suppose that this could just be managed manually to ensure that there are unique names whenever rendering partial views into other views, perhaps even going so far as to prefix values with the partial view name, but that reminds me of the ugliness that is INamingContainer in web forms - plus makes for cumbersome field names in your model.
Is there some sort of elegant solution that will allow a form to persist state that I am missing? Thanks!
Normally, I persist the search criteria on the server side when the search is performed. If the user changes the search criteria after performing the search, then posts the form any changes are, of course, lost but that's arguably correct behavior since the search wasn't invoked. This is true whether the search is performed from a full post or via ajax. Handling it this way keeps the actions cleaner, I think as you don't need to handle the search data in the other actions.
If you absolutely need to have the search parameters included, you could consider having the second form post via javascript, pick up the search field values dynamically and add them to the second form (as hidden fields) prior to posting the second form. You wouldn't have to maintain the values in two places in synchronization, but you would have to copy them to the second form before posting.
At the moment i got it like this:
Forms which has search box, posts query (and additional data if needed) to search controller which then renders search view. Search view is made from search box and search results partial views. During this - search box form is reconstructed by posted data.
If i need search results form to perform another search request (for example, with specified page index), it goes through ajax, which posts search box form + page index from search results form. Take a look here for ideas (update that JS method with targetId parameter for updating specified div/form and post additional data if needed here like this:
form.serialize()+"&pageIndex=5"
In short: if you need to maintain state of form + update another in one page - consider using partial updates, otherwise you will end up inventing ViewState 2.0.
One caveat with this way - it's tricky to make search box contain something what is related with search results (i.e. - total count of found items). Before i managed to handle this, our designer did that - i just need to add div with appropriate class name ("sbsubst" or something) and it looks that it's inside search box. :)
When you have few forms at your page each form sends only its own data. In WebForms you had only one form (at least server-side) and each control was included into this form. In ASP.NET MVC you can use the same scenario and I'm afraid you will have to if you want to have the described behavior. Don't forget - partial forms don't have to be real forms. Moreover, RenderPartial is mostly used for "control-like" layout creation.
As for the second part of your question I would suggest naming your text boxes in search form with some normal prefix like "search" or something like that. For instance, if you have text box "text" and "language" in the form, you will have "searchText" and "searchLanguage". These names are quite unique and you will have normal names in your parameters.
I am not suggesting you populating the hidden values in your results form on POST event since you said it's not an option for you but still it may be the only way if you want to have two forms.
I think the best approach will be storing the text from search input when it changes in the query part of your second form action url. For example (not tested):
$('input#yourSearchInput').change(function()
{
var searchText = $(this).val();
// or? var searchText = encodeURIComponent($(this).val());
var secondForm = $('form#secondFormId');
var action = secondForm.attr('action');
var queryStart = action.lastIndexOf('?search=');
if(queryStart > -1) {
action = action.substring(1, queryStart);
}
action = action + "?search=" + searchText;
secondForm.attr('action', action);
});
In Controller (or custom filter):
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var search = Request.QueryString["search"];
if(!String.IsNullOrEmpty(search)) {
ViewData["SearchFromPOST"] = search;
}
base.OnActionExecuting(filterContext);
}
In your Search Control:
<%= TextBox("yourSearchInputId", ViewData["SearchFromPOST"]) %>

Resources