I'm trying to use the same view to display different lists based on the param I'm passing from the button in JSP.
what I need is: when user click on button, will call the same view but data should be display from the second list .(getPreviousYearReports())
here's my code:
#RequestMapping(value = "/viewReports.do", method = {RequestMethod.GET , RequestMethod.POST})
public ModelAndView viewReports( #RequestParam final Integer storeNumber,
final HttpServletRequest request) {
ModelAndView view = getModelAndView("viewReports");
List<StoreReport> reportsList;
if(request.getParameter("previous") != null ) {
reportsList = storeReportService.getPreviousYearReports(storeNumber);
} else {
reportsList = storeReportService.getReportsByStore(storeNumber);
}
....
}
in my JSP.
I have added submit button to call the same view mapping
<button id="previous" name="previous" type="submit" class="btnFwd gradient smButtonStyling"
onClick="window.location.href='/viewReports.do?storeNumber=${storeNumber}'">
View Previous Year Reports
I was missing to pass the "&previous" param
<button id="previous" name="previous" type="submit" class="btnFwd gradient smButtonStyling"
onClick="window.location.href='/StoreReports/viewReports.do?storeNumber=${storeNumber}&previous'">
View Previous Year Reports
</button>
Related
I am a beginner and currently started a small project for my study purpose. A mini DB search portal. I created front end View. It has one search box and a button. Now what I need is, I have to fetch the data from DB, related to the user entered search term on clicking on the button.
How to proceed to get the user entered data from view to controller and process it for further operations.
General code:
view:
#using(Html.BeginForm("Search","Test"))
{
<input type="text" name="txtName"/>
<input type="submit" value="Generate report" />
}
Controller
[HTTPPost]
public ActionResult Seacrh(FormCollection form) // "Search" is action name specified in view for controller "TestController"
{
string text = form["txtName"]; // txtName is name of input in view.
}
However to take full advantage to MVC have a look at Model Binding in MVC
I recommend reading this article, it has an example different ways of adding search.
You can update the Index method in your Controller and View as follows:
1)Add a form to the view that will post to the view itself
View
#using (Html.BeginForm()){
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
2)Add a parameter to the Index method to filter the content based on the parameter passed. public ActionResult Index(string searchString)
Controller
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
I have search flight page, when finish searching I send list of flights to jsp page,
flights = getAllFlight();
ModelAndView mav = new ModelAndView("search_result");
mav.addObject("flights", flights);
return mav;
Here my jsp page to display flight result
<c:forEach var="flight" items="${flights}">
<!-- display a little information, not all flight information -->
....
<button type="submit" class="btn btn-success">Add</button>
</c:forEach>
In each flight row, have Add button to add flight information to db, how could I send back the flight object when Add button click?
There should be some unique key to identify each flight uniquely it can be an attribute like flight id or combmination of some attribute.
if you want to send this value with form submition you can keep a hidden input with value flight id and once you submit the form you will get it in controller.
if you want to send this with ajax you can add this flight id in list as data attribute like this
and on click of add button get this value with jquery data function and make the ajax call.
I solved this problem,
By create ArrayList attribute of class controller,
When button Subscribe click, send index of list back to controller
<c:forEach var="flight" items="${flights}" varStatus="theCount">
.....
<td>
<form:form action="${pageContext.request.contextPath}/flight/subcribe.html" method="POST">
<button class="btn btn-success" value="${theCount.index}" name="index">Subcribe</button>
</form:form>
</td>
</c:forEach>
Class controller handle search flight post and subscribe flight post, same class with ArrayList
#Controller
#RequestMapping(value = "/flight")
public class FlightController {
private ArrayList<Flight> flights;
#RequestMapping(value = { "/search" }, method = RequestMethod.POST)
public ModelAndView getFlightFromSearch(#ModelAttribute Parameters param
// ,Map<String, Object> model
) throws IOException, Exception {
// send arraylist of light to jsp
}
#RequestMapping(value = "/subcribe", method = RequestMethod.POST)
public ModelAndView subcribeFlight(#RequestParam("index") int index, #ModelAttribute Parameters param) throws ParseException
{
Flight flight = flights.get(i);
.......
}
}
This is a follow up to a question that was asked yesterday.
I have a viewmodel, which shows a list of objectives. Using jquery I can add a new objectives line to the screen (the ID is set to 0 for any new objectives listed). When I click on the Save button to Post the objective list back to the controller, the controller loops through the objective list, and checks the ID against the database. If the ID is NOT found, it creates a new objective, adds this to the DB context, and saves te changes. It then retreives the ID, and returns the View(model) to the View.
The problem is, although the ID in the model, is updated to the database ID - when the model is rendered in the View again, it's ID is still 0. So if I click Save again, it again, re-adds the "new objective added previously" to the database again.
My controller is shown below:
//
// POST: /Objective/Edit/model
[HttpPost]
public ActionResult Edit(ObjectivesEdit model)
{
if (model.Objectives != null)
{
foreach (var item in model.Objectives)
{
// find the database row
Objective objective = db.objectives.Find(item.ID);
if (objective != null) // if database row is found...
{
objective.objective = item.objective;
objective.score = item.score;
objective.possscore = item.possscore;
objective.comments = item.comments;
db.SaveChanges();
}
else // database row not found, so create a new objective
{
Objective obj = new Objective();
obj.comments=item.comments;
obj.objective = item.objective;
obj.possscore = item.possscore;
obj.score = item.score;
db.objectives.Add(obj);
db.SaveChanges();
// now get the newly created ID
item.ID = obj.ID;
}
}
}
return View(model);
}
My ID is being set in the controller:
EDIT: Another example here, showing model.Objectives1.ID being updated:
However when the view renders it, it reverts to 0:
The Objectives list is determined as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcObjectives2.Models
{
public class ObjectivesEdit
{
public IEnumerable<Objective> Objectives { set; get; }
public ObjectivesEdit()
{
if (Objectives == null)
Objectives = new List<Objective>();
}
}
}
The View has:
#model MvcObjectives2.Models.ObjectivesEdit
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x.Objectives)
<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> Save</button>
}
and in my EditorTemplate (objective.cshtml):
#model MvcObjectives2.Models.Objective
<div class="objec">
<div>
#Html.TextBoxFor(x => x.objective})
</div>
<div>
#Html.TextBoxFor(x => x.score})
</div>
<div>
#Html.TextBoxFor(x => x.possscore})
</div>
<div>
#Html.TextBoxFor(x => x.comments})
#Html.HiddenFor(x => x.ID) // This is the ID where it should now show the new ID from the database, but shows 0
</div>
</div>
I suspect the issue is somewhere in my controller - but I would appreciate any advise on how to get my View to render the new ID of the added objective.
After rewording my search, I came across several posts which say this is by design. A Posted form expects to display what it sent to the controller, if the same page is shown again.
However, you can add this, which will flush ModelState, and apparantly show the updated values from the model, updated in the controller:
ModelState.Clear();
return View(model);
I'm not certain if this has any other effect yet - but for now, it appears to work ok.
Thanks, Mark
The Html.HiddenFor has bitten me before in a similar scenario. The problem is when using this Html helper the hidden value is not updated on the re-post.
If you post something from the form and change it inside your controller, when you re-render the page using it will use the value which was originally posted to the action.
Instead use
<input type="hidden" name="ID" id="ID" value="#Html.Encode(Model.ID)" />
I'm using MVC3 Razor. I have 2 submit buttons setup on my view but the problem I'm having is that both submit buttons cause the validation of the model. I want to hook up individual submit buttons with specific input controls for validation.
I know this is a few months old but the solutions here seemed needlessly complex and there's no accepted answer yet. If you name your inputs the same but give them different values, you can get that value in your controller just by including a string with the name of the input as a variable. This is how I solved this problem:
View:
<input type="submit" id="EnterprisePush" name="btnSubmit" value="Push" />
<input type="submit" id="EnterprisePull" name="btnSubmit" value="Pull" />
Controller:
[HttpPost]
public ActionResult EnterpriseAdmin(int id, string btnSubmit, FormCollection collection)
{
switch (btnSubmit) {
case "Push":
/* Do Something here */
break;
case "Pull":
/* Do Something else here */
break;
}
The browser is always going to submit the entire form regardless of which submit button you press.
The best solution would be to have two submit buttons with the same value for the name attribute and different values for the value attributes.
When you submit the form, the value of the button will be submitted as well. In your action which handles that form submission, you check to see the value of the button and perform the correct validation based on that.
In your form you would have something like this:
<button type="submit" name="Command" value="command1">Do Command #1</button>
<button type="submit" name="Command" value="command2">Do Command #2</button>
Your Form Model would look like this:
public class MyFormModel() {
public string Command {get;set;}
public string SomeOtherVal {get;set;}
}
Your controller\action would look like this:
public ActionResult HandleFormSubmit(MyFormModel model) {
if (model.Command == "command1") {
// do something
} else if (model.Command == "command2") {
// do something else
}
}
Firstly, you can disable client validation on your cancel button simply by adding the CSS class 'cancel' to it. See: Disable client-side validation in MVC 3 "cancel" submit button
Secondly, as well testing the submit element's form name as described above, you can use a custom action selector. Here's mine, which I originally took from the blog post shown in the comment:
/// <summary>
/// Used to vary an action method based on which button in a form was pressed. This
/// is useful but is an anti-pattern because it couples the controller to names
/// used in the form elements.
/// </summary>
/// <remarks>
/// See the example at http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx
/// </remarks>
public class AcceptButtonAttribute : ActionMethodSelectorAttribute
{
public string ButtonName { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var req = controllerContext.RequestContext.HttpContext.Request;
return !string.IsNullOrEmpty(req.Form[this.ButtonName]);
}
}
In your controller:
[HttpPost]
[ActionName("Edit")]
[AcceptButton(ButtonName = "Cancel")]
public ActionResult Edit_Cancel(MyModel model)
{
return RedirectToAction("Index");
}
[HttpPost]
[AcceptButton(ButtonName = "Save")]
public ActionResult Edit(MyModel model)
{
// do real work here
}
Note that you need the [ActionName("Edit")] attribute to tell MVC that although using a different method name, it is for the Edit action.
And in your View:
<input type="submit" name="Save" value="Save" />
<input type="submit" name="Cancel" value="Cancel" class="cancel" />
My solution was to do two things. Say we have a Save button and another Add Something button. When user clicks on Save we want client validation and server validation to be performed. For later button we don't want any validation to take place.
Temporarily disable client validation for second button (on click):
<input type="submit" name="submit-button" value="Save" />
<input type="submit" name="submit-button" value="Add Something" onclick="document.forms[0].noValidate = true; document.forms[0].submit();" />
Good thing about it is when JavaScript is disabled the client validation would never have taken place anyway.
Take care of server side
Similar to what Bryan is saying when you click any submit button within a form, the entire form and the clicked submit button value is posted. You can differentiate which button was clicked by the name posted. In example above when user clicks on Save button and we read Request.Form["submit-button"] in controller post action we get "Save". If user clicked on Add Something we would get "Add Something". This is the way HTML is supposed to work.
Now to get around having magic strings all over the place I usually have a public static class within the controller, like so:
public class HomeController
{
public static class Buttons
{
public const string Save = "Save";
public const string AddSomething = "Add something";
}
// Action methods
}
So you can use these for rendering form:
<input type="submit" name="submit-button" value="#HomeController.Buttons.Save" />
And you can easily read the button clicked in controller:
[HttpPost]
public ActionResult Index(Model viewModel)
{
var buttonClicked = Request.Form["submit-button"];
switch (buttonClicked) {
case HomeController.Buttons.Save:
return Save(viewModel);
case HomeController.Buttons.AddSomething:
return AddSOmething(viewModel);
}
return View();
}
In Save method you first ask if ModelState.IsValid and return view model if not but in AddSomething method we will clear any errors:
public ActionResult AddSomething(Model viewModel)
{
ModelState.Clear();
// your code to add something to model
return View(viewModel);
}
This was you keep everything clean, tidy and testable. And you can introduce a constant for submit-button html name attribute. It might be possible to do all the constants with T4MVC too. A similar solution applies to when you need a "auto postback" combo box, except you need a hidden field that is set via onchange event of the select element.
Hope this helps.
Just use this code as a template:
#{
var nextButtonVal = "Next >>";
var backButtonVal = "<< Back";
if (IsPost) {
if(Request["navigate"].Equals(backButtonVal)){Response.Redirect("~/pageFoo");}
if(Request["navigate"].Equals(nextButtonVal)){Response.Redirect("~/pagebar");}
}
}
<input type="submit" value="#backButtonVal" title="Back" name="navigate"/>
<input type="submit" value="#nextButtonVal" title="Next" name="navigate"/>
One final thing I would do is instead of using intelligent strings, use an enum to determine the value for each input tag. Using razor syntax:
#Enum.GetName(typeof(YourEnumType), yourEnum.WhateverValue)
then in your controller:
public ActionResult DoSomethingBasedOnEnumValue(string enumValue)
{
YourEnumType localVar = (YourEnumType)Enum.Parse(typeof(YourEnumType), enumValue);
switch(localVar)
{
case YourEnumType.Action1:
//do something
break;
case YourEnumType.Action2:
//do something else
break;
}
return View();
}
If you want to have separate action for delete, try this.
add a delete action in the controller and mark it as HttpDelete,
[HttpDelete]
public ActionResult Edit(int id, string foo) {
...
}
And in the view,
button name should be X-HTTP-Method-Override and value should be DELETE
<button name="X-HTTP-Method-Override" value="DELETE" formnovalidate="formnovalidate" class="cancel">Delete</button>
note: all most all the browsers don't allow for other HTTP methods, like HEAD, PUT, or DELETE. but by add a header to the HTTP request, X-HTTP-Method-Override, that is supposed to be interpreted by the service and acted upon regardless of the actual HTTP method used. So above code will add a header to the request like X-HTTP-Method-Override: DELETE. and .net framework will do the rest of the things and direct you to delete action.
Submit button name don't come to server side if in all from this situation you are will be use [Remote] attribute for validation model property.
I have the following problem:
I have a form in site/banen (currently local running webserver) which is using a SQL database. The link is made using ADO.net and is instantiated in the controller in the following way:
DBModelEntities _entities;
_entities = new DBModelEntities(); // this part is in the constructor of the controller.
Next, I use this database to fill a Html.DropDownList() in my view. This is done in two steps. At the controller side we have in the constructor:
ViewData["EducationLevels"] = this.GetAllEducationLevels();
and a helper method:
public SelectList GetAllEducationLevels()
{
List<EducationLevels> lstEducationLevels = _entities.EducationLevels.ToList();
SelectList slist = new SelectList(lstEducationLevels, "ID", "Name");
return slist;
}
In the view I have the following:
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<!-- various textfields here -->
<p>
<label for="EducationLevels">EducationLevels:</label>
<!-- <%= Html.DropDownList("EducationLevels", ViewData["EducationLevels"] as SelectList)%> -->
<%= Html.DropDownList("EducationLevels", "..select option..")%>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Now, the form is rendered correctly when I browse to the create page. I can select etc. But when selected I have to use that value to save in my new model to upload to the database. This is where it goes wrong. I have the following code to do this in my controller:
//
// POST: /Banen/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection form)
{
// set rest of information which has to be set automatically
var vacatureToAdd = new Vacatures();
//vacatureToAdd.EducationLevels = form["EducationLevels"];
// Deserialize (Include white list!)
TryUpdateModel(vacatureToAdd);
// Validate
if (String.IsNullOrEmpty(vacatureToAdd.Title))
ModelState.AddModelError("Title", "Title is required!");
if (String.IsNullOrEmpty(vacatureToAdd.Content))
ModelState.AddModelError("Content", "Content is required!");
// Update the variables not set in the form
vacatureToAdd.CreatedAt = DateTime.Now; // Just created.
vacatureToAdd.UpdatedAt = DateTime.Now; // Just created, so also modified now.
vacatureToAdd.ViewCount = 0; // We have just created it, so no views
vacatureToAdd.ID = GetGuid(); // Generate uniqueidentifier
try
{
// TODO: Add insert logic here
_entities.AddToVacatures(vacatureToAdd);
_entities.SaveChanges();
// Return to listing page if succesful
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
#endregion
It gives the error:
alt text http://www.bastijn.nl/zooi/error_dropdown.png
I have found various topics on this but all say you can retrieve by just using:
vacatureToAdd.EducationLevels = form["EducationLevels"];
Though this returns a string for me. Since I'm new to ASP.net I think I am forgetting to tell to select the object to return and not a string. Maybe this is the selectedValue in the part where I make my SelectList but I can't figure out how to set this correctly. Of course I can also be complete on a sidetrack.
Sidenote: currently I'm thinking about having a seperate model like here.
Any help is appreciated.
You can't return an object from usual <SELECT> tag wich is rendered by Html.DropDownList() method, but only string variable could be returned. In your case ID of EducationLevels object will be send to the server. You should define and use one more custom helper method to reconstruct this object by ID.