I have a form which has 2 buttons "Delete" and "Edit".. on Post to check which button submitted the request... which of the 2 approaches is better and is there is any difference or both are same?
#if(isPost){
if(!Request["buttonDelete"].IsEmpty()){
//Do something
}
if(Request["btn"].Equals("buttonDelete")){
//do something
}
}
lets say you have html like this
<form method="post">
<input type="text" name="txtName" value="Vishal" />
<input type="submit" name="btnEdit" value="Edit" />
<input type="submit" name="btnDelete" value="Delete" />
</form>
here two buttons are there having edit and delete action. now when you submit a form on a server side you can have clicked element under form collection paramerters that you can access like
Request.Params["btnDelete"]
Request.Params["btnEdit"]
whichever element is clicked , the other element will be null
Request.Params["btnEdit"] will become null when you click on Delete button
same way Request.Params["btnDelete"] will become null when you click on Edit button
you can check like
if (!string.IsNullOrEmpty(Request.QueryString["Edit"]))
{
// do edit
}
hope that helps ! :)
If this is ASP MVC then both of this approaches are not correct. You must have separated actions for Save and Edit:
public ActionResult Edit(Entity item){
// do stuff
return RedirectToAction('Index', new { lastAction = 'Edit' });
}
public ActionResult Save(Entity item){
// do stuff
return RedirectToAction('Index', new { lastAction = 'Delete' });
}
Then, on Index view you may check 'lastAction' parameter:
#{
if(!Request.Params["lastAction"] == "Delete"){
//do something
} else if(Request["lastAction"] == "Edit"){
//do something
}
}
Related
I have a view which has a registration form. If the registration form is submitted I want to return to the same view and display a temporary Bootstrap Well and then fade it out. Check my idea out in my controller
Controller
// Insert User
[HttpPost]
public void AddUser(ResourceViewModel resourceInfo)
{
// Fetch data from ViewModel as parameters Execute Stored Procedure
db_RIRO.sp_InsertNewUser(resourceInfo.Username, resourceInfo.Password);
db_RIRO.SaveChanges()
// My Idea
if (storedProcedure succesful)
{ // display success ViewBag in view }
else
{
// display failed ViewBag in view
}
}
View
<div class="form-group">
<label class="col-sm-3 control-label lb-sm" for="textinput">Password</label>
<div class="col-sm-5">
#Html.TextBoxFor(a => a.Password, new { #class = "form-control input-sm" })
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label lb-sm" for="textinput">Username</label>
<div class="col-sm-5">
#Html.TextBoxFor(a => a.Username, new { #class = "form-control input-sm" })
</div>
</div>
How would I achieve this using ViewBag?
You haven't said whether you return to the same view or not, but in whatever view you do return to, you can print the TempData out.
Here's a basic example:
Controller:
[HttpPost]
public void AddUser(ResourceViewModel resourceInfo)
{
// Fetch data from ViewModel as parameters Execute Stored Procedure
db_RIRO.sp_InsertNewUser(resourceInfo.Username, resourceInfo.Password);
db_RIRO.SaveChanges()
// My Idea
if (storedProcedure succesful)
{
// display success tempdata in view
TempData["Message"] = "Data saved successfully";
}
else
{
// display failed tempdata in view
TempData["Message"] = "Sorry, an error has occurred";
}
//...etc
}
View (place this anywhere you like in the view):
#if (TempData["Message"] != null)
{
#Html.Raw(TempData["Message"].ToString())
}
This example just uses a simple string, but you can use a more complex data structure if required (e.g. I imagine you might want to set colour schemes / CSS classes for success / failure, for instance, or add Javascript to get things like fade effects - you can place that script within your if statement. Maybe consider creating a re-usable partial view and a "Message" object to use as the model for it which can convey all that kind thing, and you can use it throughout your application.
N.B. If you're returning to the same view, you can always just use the ViewBag instead of TempData - TempData can be useful because will persist across requests, e.g. if you redirect to another action at the end of your current action, instead of directly returning a view.
I'm working on a ASP.NET web shop but I'm stuck on a particular part. I'm trying to create a session whenever a user (not logged in) adds a CD or a DVD to their shopping cart. That way they can go to their cart (/shopping_cart) and see all of the products they have added.
However, I can't make it work. Whenever I click on 'add product' nothing really happens.
Here's my code:
ProductsController:
// POST: Producten/Details/9
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddToCart(int id)
{
Product p = db.Producten.Find(id);
if (p != null)
{
Models.Winkelkar kar;
if(Session["kar"] == null)
{
kar = new Models.Winkelkar();
}else
{
kar = (Models.Winkelkar)Session["winkelkar"];
}
kar.AddProduct(p);
Session["winkelkar"] = kar;
//var products = Session["kar"] as List<Product>;
}
return View();
}
My Models.Winkelkar looks like this:
public partial class Winkelkar
{
private List<Product> _List = new List<Product>();
public List<Product> GetList()
{
// geeft een lijst van pbjecten terug
return _List;
}
public void AddProduct(Product p)
{
_List.Add(p);
}
}
And my view form looks like this:
<form action="" method="post>
#Html.AntiForgeryToken()
<input type="hidden" name="id" value="#Model.ID" />
<input type="submit" name="toevoegen" value="Toevoegen" />
</form>
Now I'm trying to display the session on the same page, I'll move it to /shopping_cart once I know it works, but I'm trying to display it like this:
#Session["winkelkar"]
I've looked through many other solutions here, but nothing really seems to work.
EDIT
As pointed out below, I didn't have an action in my form. I changed it to:
<form action="#Url.Action("AddToCart", "Producten")" method="POST">
...
</form>
Getting one step closer.
You used the name "winkelkar" as your session key everywhere, expect on this line:
if(Session["kar"] == null)
That means you will never find your object, and always recreate a new one.
For this reason, you should avoid using a string literal for session keys, and declare a constant instead.
And as mason pointed out in his comment, your form has an empty action attribute. You should have something like that:
<form action="#Url.Action("AddToCart", "Products")" method="post">
....
</form>
I currently have a form with a submit and cancel button. Depending on some logic, each time the page loads, I want that same cancel button to redirect to different other pages in the application. This is the code I have at the moment in my aspx view that changes the location.href based on my property
<% if (Model.MyProperty.Equals("something"))
{ %>
<input class="btnCancel" type="button" value="" onclick="location.href='<%: Url.Action("MyAction","MyController", new {Area="MyArea"},null)%>'" />
<% } %>
<% else if (Model.MyProperty.Equals("somethingelse"))
{ %>
<input class="btnCancel" type="button" value="" onclick="location.href='<%: Url.Action("MyOtherAction","MyOtherController", new {Area="SomeOtherArea"},null)%>'" />
<% } %>
Is this the correct and elegant way to do this? I would rather reduce the multiple IF-ELSE conditions if there was a way to do it.
Thanks for your time.
The way I've always handled multiple redirect options is by setting the href value in the controller action.
The View is generic, but the controller action is specific to the context of the page your rendering. So in your model, make a property called CancelUrl. Now, in the controller action, set it to the link you want it to go to.
model.CancelUrl = Url.Action("action", "controller");
This way, all you have to do in your View is say
Text
You can create a cancel method that takes your property as a parameter and redirect appropriately within the controller. This logic should probably not be in your view anyway as views should have almost 0 logic anyway
I would put the property that will be used to decide the cancel action in the view model (as you already have), alongside any other required properties.
For example:
public class IndexModel
{
//any other properties you need to define here
public string MyProperty { get; set; }
}
Then your view would look similar to:
#model IndexModel
#using (Html.BeginForm())
{
//other information you may want to submit would go here and in the model.
#Html.HiddenFor(m => m.MyProperty)
<button type="submit" name="submit" value="submit">submit</button>
<button type="submit" name="cancel" value="cancel">cancel</button>
}
And finally, your post action should decide the next action that should be returned:
[HttpPost]
public ActionResult Index(IndexModel model)
{
if (!string.IsNullOrEmpty(Request["submit"]))
{
if (ModelState.IsValid)
{
//any processing of the model here
return RedirectToAction("TheNextAction");
}
return View();
}
if (model.MyProperty.Equals("something"))
{
return RedirectToAction("MyAction", "MyController", new { area = "MyArea" });
}
else //assumes the only other option is "somethingelse"
{
return RedirectToAction("MyOtherAction", "MyOtherController", new { area = "SomeOtherArea" });
}
}
I have a Create-View like this ...
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(null, new { #class = "validation" })
...
<input class="cancel" type="submit" value="OK" />
...
<input name="submit" type="submit" value="Save" />
}
... and a corresponding controller action:
[HttpPost]
public ActionResult Create(string submit, MyViewModel myViewModel)
{
if (submit != null) // true, if "Save" button has been clicked
{
if (ModelState.IsValid)
{
// save model data
return RedirectToAction("Index");
}
}
else // if "OK" button has been clicked
{
// Disable somehow validation here so that
// no validation errors are displayed in ValidationSummary
}
// prepare some data in myViewModel ...
return View(myViewModel); // ... and display page again
}
I have found that I can disable client-side validation by setting class="cancel" on the "OK" button. This works fine.
However, server-side validation still happens. Is there a way to disable it in a controller action (see the else-block in the Create action above)?
Thank you for help!
I recently had a similar problem. I wanted to exclude some properties from validation and used the following code:
ModelState.Remove("Propertyname");
To hide the errormessages you can use
ModelState.Clear();
But the question is why you submit the values if you do not use them? Would you not better use a reset button to reset the values in the form:
<input type="reset" value="Reset Form">
So if there is nothing in your submit string you want it to ignore checking if the model state is valid and assume that it is.
However it still is going ahead and check your validation and showing up on the client side through the validation summary.
If you really don't care about the errors in this case try
ModelState.Clear()
and remove all the errors out of it.
The server-side validation must be in your MyViewModel class. Can you use a different class that does not implement the validation? The data annotations in the ViewModel is responsible for setting ModelState.IsValid to false.
Now, I just had this idea:
...
else // if "OK" button has been clicked
{
ModelState.Clear();
}
...
Indeed I don't get messages in ValidationSummary anymore. Does this have any downside or undesirable side effect? At least I cannot see an issue at the moment...
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.