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"]) %>
Related
I'm making the transition from webforms to MVC (I know, 3 years late) and I "get it" for the most part, but there's a few things I'd like advice and clarification on:
First off, what happens if you want to dynamically add inputs to a view? for example, in an old webform for generating invoices I had a button with a server-side click event handler that added an extra 5 invoice item rows. The stateful nature of webforms meant the server handled the POST event "safely" without altering the rest of the page.
In MVC I can't think how I'd do this without using client-side scripting (not a showstopper, but I would like to support clients that don't have scripting enabled).
The second problem relates to the invoices example. If my Model has a List, how should I be generating inputs for it?
I know data binding is a possible solution, but I dint like surrendering control.
Finally, back to the "stateful pages" concept - say I've got a Dashboard page that has a calendar on it (I wrote my own calendar Control class, the control itself is stateless, but can use the webform viewstate to store paging information) - how could a user page through the calendar months? Obviously POST is inappropriate, so it would have to be with GET with a querystring parameter - how can I do this in MVC? (don't say AJAX).
Thanks!
In MVC you design your actions to accommodate your needs. For example, if you wanted to be able to add 5 rows to an invoice NOT using client-side scripting, you'd probably have your GET action for the invoice generation take a nullable int parameter for the number of rows. Store the current number of rows in the view model for the page and generate a link on the page to your GET action that has the parameter value set to 5 more than the current value. The user clicks the link and the GET view generates the page with the requested number of rows.
Controller
[HttpGet]
public ActionResult Invoice( int? rows )
{
rows = rows ?? 5; // probably you'd pull the default from a configuration
...
viewModel.CurrentRows = rows;
return View( viewModel );
}
View
#Html.ActionLink( "Add 5 Lines", "invoice", new { rows = Model.CurrentRows + 5 }, new { #class = "add-rows" } )
You would probably also add some script to the page that intercepts the click handler and does the same thing via the script that your action would do so that in the general case the user doesn't have to do a round trip to the server.
<script type="text/javascript">
$(function() {
$('.add-rows').click( function() {
...add additional inputs to the invoice...
return false; // abort the request
});
});
</script>
Likewise for your calendar. The general idea is you put enough information in your view model to generate all the actions that you want to perform from your view. Construct the links or forms (yes you can have multiple forms!) in your view to do the action. Use parameters to communicate to the controller/action what needs to be done. In the rare case where you need to retain state between actions, say when performing a wizard that takes multiple actions, you can store the information in the session or use TempData (which uses the session).
For things like a calendar you'd need the current date and the current view type (month/day/year). From that you can construct an action that takes you to the next month/day/year. For a paged list you need the current page, the current sort column and direction, the number of items per page, and the number of pages. Using this information you can construct your paging links that call back to actions expecting those parameters which simply do the right thing for the parameters with which they are called.
Lastly, don't fear AJAX, embrace it. It's not always appropriate (you can't upload files with it, for example), but your users will appreciate an AJAX-enabled interface.
In MVC you can store application state in various ways. In your controller you have direct access to the Session object and you can also store state to the database.
your view can contain basic control flow logic, so, if your model has a list you can iterate over it in the view and, for example, render an input control for each item in the list. you could also set a variable in a model to be the maximum number of rows on the viewpage and then render a row in a table for the number specified by the model.
paging is basically the same thing. you can create a partial view (user control in the webform world) that shows page numbers as links, where each link calls an action that fetches the data for that page of results.
i'm not sure what your beef is with ajax or javascript
I have a Product edit screen. The user can select a Vendor for the Product. To do this, I display a jQueryUI dialog box which allows them to browse for and select a Vendor. When the user selects the Vendor, I update a hidden VendorID input on the page, which is part of my page's model. I also update several divs with details about the Vendor they have selected. These are for display purposes only--only the id is needed to persist the selected Vendor.
This all works fine and dandy except when there is an error on postback, in which case I redisplay the same view. ModelState takes care of preserving all my form fields (including the hidden VendorID). However, my divs with the Vendor text are (of course) empty since they're not posted to the server.
I first went down the path of creating hidden fields for each of my Vendor display fields and putting them on the model. Then the hidden fields survive the postback, but that doesn't solve the problem of actually redisplaying the text on the screen.
The three options I can think of are:
On postback, if there is an error, go to the database, fetch the Vendor using the supplied VendorID and re-populate the model with the text I want to display.
Use RenderAction and have an action which renders the details of the selected Vendor.
Use readonly textboxes instead of divs to display the Vendor details.
None of these feel very satisfactory to me. I feel like I might be missing an obvious solution. Are there any better solutions?
I would suggest you not have the extra Vendor information come down as part of the main page. Create a javascript function showVendorInfo(). When called, if the VendorID hidden input has a value, it gets the relevant Vendor information via AJAX and displays it, using an AjaxGetVendorInfo action method. Call this function from two places:
In document.ready()
after a Vendor is selected with jQueryUI display.
Now, this would be in an action method. You could, if you expect your users to have latency issues, do the following to avoid some ajax calls: In the view check if you know the VendorID; if so, call Html.RenderAction call the same AjaxGetVendorInfo action method from the view.
A bonus to this is that it avoids what I have found to be a big no-no: Including both display-only values and model-binding values in your ViewModel. This makes for a very confusing ViewModel, especially when there are validation errors. [Getting on soap box] It's best to have your ViewModel to just have properties intended for modelbinding, for your state. Put list values, extra display information, etc., into ViewData or have them show up via AJAX.
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
What is the best ASP.NET MVC pattern for paging data when the data is filtered by form criteria?
This question is similar to: Preserve data in .net mvc but surely there is a better answer?
Currently, when I click the search button this action is called:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Search(MemberSearchForm formSp, int? pageIndex, string sortExpression)
{}
That is perfect for the initial display of the results in the table.
But I want to have page number links or sort expression links re-post the current form data (the user entered it the first time - persisted because it is returned as viewdata), along with extra route params 'pageIndex' or 'sortExpression',
Can an ActionLink or RouteLink (which I would use for page numbers) post the form to the url they specify?
<%= Html.RouteLink("page 2", "MemberSearch", new { pageIndex = 1 })%>
At the moment they just do a basic redirect and do not post the form values so the search page loads fresh.
In regular old web forms I used to persist the search params (MemberSearchForm) in the ViewState and have a GridView paging or sorting event reuse it.
One possible solution is to attach a javascript click handler to the pager links that will submit the form by updating a hidden field containing the page number. This way you will get all the search criteria in the controller action.
Another possibility is to transform those pager links into submit buttons and place them inside the form.
A third possibility is to use the Session to persist search criteria.
You could perform a GET instead of a POST. if your request is to return search results, a GET might make more sense anyway. The benifit would be that all of your search fields are encoded into the URL. So, when you perform a page or sort on th exisiting URL, your data is perserved.
I have an example that using the MvcContrib Grid and Pager here:
http://weblogs.asp.net/rajbk/archive/2010/05/08/asp-net-mvc-paging-sorting-filtering-using-the-mvccontrib-grid-and-pager.aspx
I am new to ASP.NET MVC, particularly ajax operations. I have a form with a jquery dialog for adding items to a drop-down list. This posts to the controller action.
If nothing (ie void method) is returned from the Controller Action the page returns having updated the database, but obviously there no chnage to the form. What would be the best practice in updating the drop down list with the added id/value and selecting the item.
I think my options are:
1) Construct and return the html manually that makes up the new <select> tag
[this would be easy enough and work, but seems like I am missing something]
2) Use some kind of "helper" to construct the new html
[This seems to make sense]
3) Only return the id/value and add this to the list and select the item
[This seems like an overkill considering the item needs to be placed in the correct order etc]
4) Use some kind of Partial View
[Does this mean creating additional forms within ascx controls? not sure how this would effect submitting the main form its on? Also unless this is reusable by passing in parameters(not sure how thats done) maybe 2 is the option?]
UPDATE:
Having looked around a bit, it seems that generating html withing the controller is not a good idea. I have seen other posts that render partialviews to strings which I guess is what I need and separates concerns (since the html bits are in the ascx). Any comments on whether that is good practice.
look at the ContentResult you can specify the mime type of what you return (text/html)
You could alternatively make a control that take a IEnumerable of whatever you put in the selectlist, and build it using the view engine. That way you keep the formatting of the html (in this case a list of options) into a view, and not in your code.
<%# Control Language="C#"Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Article>>"%>
<%foreach (var article in Model){%>
<option><%:article.Title %></option>
<%} %>
I think I would go for that second one
From what I understood, the jQuery dialog contains a form that, when submitted, will post to an action which updates the database with some information. You want to get the newly added database information and update the same form that was used to trigger the database update.
If that is the case, then I think the best clean and logical option is to return JSON serialization of the items to be put in the drop down right after you update the database. Then, using jQuery, you would clear the drop down and append option tags into it.
You can also write a new, seperate action that returns the JSON serialization of the database objects you need. You would have jQuery call another post to this action as a callback to your first ajax post (the one used to update the database).
Here is a quick snippet
public ActionResult UpdateDatabase(string something)
{
/// update the database
IEnumerable<Items> items = getItemsFromDatabase(); // or w/e
var vals = items.Select(x=> new { value = x.ID, text = x.Name }); // something similar
return Json(vals);
}
Personally, I would write a separate function that returns JSON. This ensure separation of concerns, and gives me a function I can use in many different places.
Returning a JsonResult with all the items is the most versatile and least-bandwidth intensive solution as long as you are happy to iterate through the list in jQuery and update your drop-down list.
Using a partial view is nice for HTML that you can .load(...) directly into your select, but less versatile.
I would go with the JsonResult.
In your Controller:
public JsonResult UpdateItem(string sItem)
{
// 1. Insert new item into database if not exist...
// {update code here}
// 2. retrieve items from database:
IEnumerable<Item> Items = GetItems();
// 3. return enumerable list in JSON format:
return new JsonResult{ Data = new {Items = Items, Result = "OK" }};
}
On client-side:
Iterate through Items array and add the items to your list.