i was looking for good trick to handle multiple submit button in form and then i got some advice from this url and i followed but fail.
How do you handle multiple submit buttons in ASP.NET MVC Framework?
posted by #Andrey Shchekin.
he just said create a class like below one so i did in same controller
public class HttpParamActionAttribute : ActionNameSelectorAttribute {
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
then multiple submit button in the view look like & also controller code look like below
<% using (Html.BeginForm("Action", "Post")) { %>
<!— …form fields… -->
<input type="submit" name="saveDraft" value="Save Draft" />
<input type="submit" name="publish" value="Publish" />
<% } %>
and controller with two methods
public class PostController : Controller {
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveDraft(…) {
//…
}
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Publish(…) {
//…
}
}
but when i test his code it never work. so any can tell me where i am making the mistake or code itself is wrong for handling the situation. thanks
View:
<input type="submit" name="mySubmit" value="Save Draft" />
<input type="submit" name="mySubmit" value="Publish" />
Controller Action:
[HttpPost]
public ActionResult ActionName(ModelType model, string mySubmit)
{
if(mySubmit == "Save Draft")
{
//save draft code here
} else if(mySubmit == "Publish")
{
//publish code here
}
}
I had to deal with the similar scenario when I had the requirement that Users can finalize or save progress of the hospital infant record - essentially both actions are submit but one validates the record for insertion into the main DB table and another one saves it into a temp table without any validation. I handled it like this:
I have 2 buttons both are type submit with different IDs (btnSave and btnFinalize). When btnSave is clicked I intercept that event with some JQuery code:
$("#btnSave").click(function () {
$("#SaveForm").validate().settings.rules = null;
$('#SaveForm').attr('action', '#(Url.Content("~/Home/EditCase?finalize=false"))');
});
As you can see I modify the action attribute of the form to point to a different URL with a querystring attribute of finalize = false. I also remove any validation present on the model. If the other button is clicked I do nothing - executes the default behavior.
And in my controller I have a single action that handles both submit actions:
public ActionResult EditCase(EditInfantModel model, bool finalize = true)
{
// Logic for handling submit in here...
}
I think you can apply the similar technique for your problem. I'm not sure if it's the answer you're looking for but I thought it was worth mentioning...
Related
so I recently started project in .net MVC and have been having issues preserving data. I read somewhere that in order to do so, you have to pass the model back and forth. I tried doing this, but it still runs into issues. In my project, I have two buttons right now, getData and getData2. My view prints their true/false values. When I press one, it turns true, but if I press the other, it turns true but the other one goes to false. I want them both to stay true if I press them both.
Controller:
[HttpPost]
public ActionResult GetData(TestSite.Models.FarModels theFars)
{
theFars.HasData = true;
return RedirectToAction("FarCalc",theFars);
}
[HttpPost]
public ActionResult GetData2(TestSite.Models.FarModels theFars)
{
theFars.HasData2 = true;
return RedirectToAction("FarCalc", theFars);
}
[HttpGet]
public ActionResult FarCalc(TestSite.Models.FarModels theFars)
{
return View(theFars);
}
View:
#using (Html.BeginForm("GetData", "Home", FormMethod.Post))
{
//#Html.TextBoxFor(model => model.FarValue)
<input type="submit" value="GetData" />
}
#using (Html.BeginForm("GetData2", "Home", FormMethod.Post))
{
//#Html.TextBoxFor(model => model.FarValue)
<input type="submit" value="GetData2" />
}
#Model.HasData
#Model.HasData2
Thanks
You would need to make use of TempData object because the use of RedirectToAction returns a status code of 302, and the model binding does not exist. A good example is below.
https://www.codeproject.com/Tips/842080/How-to-Persist-Data-with-TempData-in-MVC
I have a form on my page:
#using(Html.BeginForm("DoReservation","Reservation"))
{
...some inputs
<button id="recalculate">Recalculate price</button>
<button id="submit">Submit</button>
}
When I click the "Recalculate price" button I want the following action to be invoked:
public ActionResult Recalculate(FormCollection form)
{
var price = RecalculatePrice(form);
... do some price recalculation based on the inputs
return PartialView("PriceRecalculation",price);
}
When I click the "Submit" button I want the "DoReservation" action to be invoked (I want the form to be submitted).
How can I achieve something like that?
What I can suggest is , adding a new property to your view model and call it ActionType.
public string ActionType { get; set; }
and then change your cshtml file like below
#using (Html.BeginForm())
{
<div id="mytargetid">
...some inputs*#
</div>
<button type="submit" name="actionType" value="Recalculate" >Recalculate price</button>
<button type="submit" name="actionType" value="DoReservation" >Submit</button>
}
in post action method based on ActionType value you can decide what to do !
I noticed that in your comments you mentioned you need to return partial and replace if with returning partial , no problem , you can use
#using (Ajax.BeginForm("DoProcess", new AjaxOptions { UpdateTargetId = "mytargetid", InsertionMode = InsertionMode.Replace }))
and in controller change your action to return partial view or java script code to redirect page
public ActionResult DoProcess(FormModel model)
{
if (model.ActionType == "Recalculate")
{
return PartialView("Test");
}
else if (model.ActionType == "DoReservation")
{
return JavaScript(string.Format("document.location.href='{0}';",Url.Action("OtherAction")));
}
return null;
}
I'm attempting to add a classic Accept Terms and Conditions checkbox on the log on page of an MVC application.
If the user accepts the Terms and Conditions, but fails to log on for some other reason (bad password etc), then I want the Accept T&Cs checkbox not to be checked, so the user is forced to accept the T&Cs on every log on attempt.
The problem is that using Html.CheckboxFor(), after a postback the checkbox retains its previous value, despite the value of the bound Model property.
Here's the code, stripped down to essentials. If you run this code up, check the checkbox, and click the button, you'll be returned to the form with the checkbox still checked, even though the bound model property is false.
The Model:
namespace Namespace.Web.ViewModels.Account
{
public class LogOnInputViewModel
{
[IsTrue("You must agree to the Terms and Conditions.")]
public bool AcceptTermsAndConditions { get; set; }
}
}
The validation attribute:
public class IsTrueAttribute : ValidationAttribute
{
public IsTrueAttribute(string errorMessage) : base(errorMessage)
{
}
public override bool IsValid(object value)
{
if (value == null) return false;
if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
return (bool)value;
}
}
The View:
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions)
<input type="submit" value="Log On" />
}
The Controller:
[HttpGet]
public ActionResult LogOn(string returnUrl)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
[HttpPost]
public ActionResult LogOn(LogOnInputViewModel input)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
I saw the suggestion on asp.net to add a #checked attribute to the CheckboxFor. I tried this, making the view
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions, new { #checked = Model.AcceptTermsAndConditions })
<input type="submit" value="Log On" />
}
And I saw the same behaviour.
Thanks for any help/insights!
Edit: Although I want to override the posted back value, I wish to retain the message if validation of AcceptTermsAndConditions fails (there is a validation attribute on AcceptTermsAndConditions requiring it to be true), so I can't use ModelState.Remove("AcceptTermsAndConditions") which was the otherwise sound answer #counsellorben gave me. I've edited the code above to include the validation attribute - apologies to #counsellorben for not being clearer originally.
You need to clear the ModelState for AcceptTermsAndConditions. By design, CheckBoxFor and other data-bound helpers are bound first against the ModelState, and then against the model if there is no ModelState for the element. Add the following to your POST action:
ModelState.Remove("AcceptTermsAndConditions");
i have designed a view in asp .net mvc3 off course registration form. This is very simple form having name ,father name , qualification and a submit button , after pressing submit button i want to display information by using another view. please suggest me how can i send information from one view to another view.
my controller class is :
namespace RegistrationForm.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
// ViewBag.Message = "Welcome to ASP.NET MVC!";
//return View();
return RedirectToAction("registrationView");
}
public ActionResult About()
{
return View();
}
public ActionResult registrationView()
{
return View();
}
}
}
my view is :
#{
Layout = null;
}
registrationView
Enter Name
</td>
<tr>
<td>
Enter Father Name
</td>
<td>
<input type="text" name="fname" id="fname" />
</td>
<tr>
<td>
Enter Qualification
</td>
<td>
<input type="text" name="qly" id="qly" />
</td>
</tr>
</table>
<input type="submit" value="submit" />
</div>
well, we faced this problem before, and the best way to get this to work was to define a model that this page will work with, then use this model object when posting back, or redirecting to another view.
for your case, you can simply define this model in your Models folder
ex: RegistrationModel.cs file, and define your required properties inside.
after doing so, you will need to do 2 more steps:
1- in your GET action method, create a new RegistrationModel object, and provide it to your view, so instead of:
return View();
you will need something like:
var registrationModel = new registrationModel();
return View(registrationModel);
2- Use this model as a parameter in your POST Action method, something like
[HttpPost]
public ActionResult registrationView(RegistrationModel model)
{
// your code goes here
}
but don't forget to modify the current view to make use of the provided model. a time-saver way would be to create a new dummy View, and use the pre-defined template "Create" to generate your View, MVC will generate the properties with everything hooked up. then copy the generated code into your desired view, and omit any unneeded code.
this is a Pseudo reply. if you need more code, let me know
<% using Html.Form("<ActionName>") { %>
// utilize this HtmlHelper action to redirect this form to a different Action other than controller that called it.
<% } %>
use ViewData to store the value.
just remember that it will only last per one trip so if you try to call it again, the value would have been cleared.
namespace RegistrationForm.Controllers { public class HomeController : Controller { public ActionResult Index() { // ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewData["myData"] = "hello world";
//return View();
return RedirectToAction("registrationView");
}
public ActionResult About()
{
return View();
}
public ActionResult registrationView()
{
// get back my data
string data = ViewData["myData"] != null ? ViewData["myData"].ToString() : "";
return View();
}
}
And you can actually usethe ViewData value on the html/aspx/ascx after redirect to the registrationView.
For example on the registrationView.aspx:
<div id="myDiv">
my data was: <%= ViewData["myData"] %>
</div>
You could simply in you method parameter list declare the parameters with the name of the controls. For example:
The control here has an id "qly"
<input type="text" name="qly" id="qly" />
Define your method parameter list as following:
public ActionResult YourMethod(string qly)
{
//simply pass your qly to another view using ViewData, TempData, or ViewBag, and use it in the desired view
}
You should use TempData which was made exactly for it, to persist values between actions.
This example is from MSDN (link above):
public ActionResult InsertCustomer(string firstName, string lastName)
{
// Check for input errors.
if (String.IsNullOrEmpty(firstName) ||
String.IsNullOrEmpty(lastName))
{
InsertError error = new InsertError();
error.ErrorMessage = "Both names are required.";
error.OriginalFirstName = firstName;
error.OriginalLastName = lastName;
TempData["error"] = error; // sending data to the other action
return RedirectToAction("NewCustomer");
}
// No errors
// ...
return View();
}
And to send data to the view you can use the model or the ViewBag.
I have discovered what appears to be a bug using MVC 3 with the RemoteAttibute and the ActionNameSelectorAttribute.
I have implemented a solution to support multiple submit buttons on the same view similar to this post: http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/
The solution works however, when I introduce the RemoteAttribute in my model, the controllerContext.RequestContext.HttpContext.Request no longer contains any of my submit buttons which causes the the "multi-submit-button" solution to fail.
Has anyone else experienced this scenario?
I know this is not a direct answer to your question, but I would propose an alternative solution to the multiple submit-buttons using clientside JQuery and markup instead:
Javascript
<script type="text/javascript">
$(document).ready(function () {
$("input[type=submit][data-action]").click(function (e) {
var $this = $(this);
var form = $this.parents("form");
var action = $this.attr('data-action');
var controller = $this.attr('data-controller');
form.attr('action', "/" + controller + "/" + action);
form.submit();
e.preventDefault();
});
});
</script>
Html
#using (Html.BeginForm())
{
<input type="text" name="name" id="name" />
<input type="submit" value="Save draft" data-action="SaveDraft" data-controller="Home" />
<input type="submit" value="Publish" data-action="Publish" data-controller="Home" />
}
It might not be as elegant as a code-solution, but it offers somewhat less hassle in that the only thing that actually changes is the action-attribute of the form when a submitbutton is clicked.
Basically what it does is that whenever a submit-button with the attribute data-action set is clicked, it replaces its parent forms action-attribute with a combination of the attributes data-controller and data-action on the clicked button, and then fires the submit-event of the form.
Of course, this particular example is poorly generic and it will always create /Controller/Action url, but this could easily be extended with some more logic in the click-action.
Just a tip :)
i'm not sure that its a bug in mvc 3 as it's not something that you were expecting. the RemoteAttribute causes javascript to intercept and validate the form with an ajax post. to do that, the form post is probably canceled, and when the validation is complete, the form's submit event is probably called directly, rather than using the actual button clicked. i can see where that would be problematic in your scenario, but it makes sense. my suggestion, either don't use the RemoteAttributeand validate things yourself, or don't have multiple form actions.
The problem manifests itself when the RemoteAttribute is used on a model in a view where mutliple submit buttons are used. Regardless of what "multi-button" solution you use, the POST no longer contains any submit inputs.
I managed to solve the problem with a few tweeks to the ActionMethodSelectorAttribute and the addition of a hidden view field and some javascript to help wire up the pieces.
ViewModel
public class NomineeViewModel
{
[Remote("UserAlreadyRegistered", "Nominee", AdditionalFields="Version", ErrorMessage="This Username is already registered with the agency.")]
public string UserName { get; set; }
public int Version {get; set;}
public string SubmitButtonName{ get; set; }
}
ActionMethodSelectorAttribute
public class OnlyIfPostedFromButtonAttribute : ActionMethodSelectorAttribute
{
public String SubmitButton { get; set; }
public String ViewModelSubmitButton { get; set; }
public override Boolean IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var buttonName = controllerContext.HttpContext.Request[SubmitButton];
if (buttonName == null)
{
//This is neccessary to support the RemoteAttribute that appears to intercepted the form post
//and removes the submit button from the Request (normally detected in the code above)
var viewModelSubmitButton = controllerContext.HttpContext.Request[ViewModelSubmitButton];
if ((viewModelSubmitButton == null) || (viewModelSubmitButton != SubmitButton))
return false;
}
// Modify the requested action to the name of the method the attribute is attached to
controllerContext.RouteData.Values["action"] = methodInfo.Name;
return true;
}
}
View
<script type="text/javascript" language="javascript">
$(function () {
$("input[type=submit][data-action]").click(function (e) {
var action = $(this).attr('data-action');
$("#SubmitButtonName").val(action);
});
});
</script>
<% using (Html.BeginForm())
{%>
<p>
<%= Html.LabelFor(m => m.UserName)%>
<%= Html.DisplayFor(m => m.UserName)%>
</p>
<input type="submit" name="editNominee" value="Edit" data-action="editNominee" />
<input type="submit" name="sendActivationEmail" value="SendActivationEmail" data-action="sendActivationEmail" />
<%=Html.HiddenFor(m=>m.SubmitButtonName) %>
<% } %>
Controller
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "editNominee", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsEditNominee(NomineeViewModel nom)
{
return RedirectToAction("Edit", "Nominee", new { id = nom.UserName });
}
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "sendActivationEmail", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsSendActivationEmail(NomineeViewModel nom)
{
return RedirectToAction("SendActivationEmail", "Nominee", new { id = nom.UserName });
}
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public ActionResult UserAlreadyRegistered(string UserName, int Version)
{
//Only validate this property for new records (i.e. Version != zero)
return Version != 0 ? Json(true, JsonRequestBehavior.AllowGet)
: Json(! nomineeService.UserNameAlreadyRegistered(CurrentLogonDetails.TaxAgentId, UserName), JsonRequestBehavior.AllowGet);
}
I encountered the same issue.
I also attached an on submit event to prepare the form before submit. Interestingly, when I insert a break point in the on submit function, and then continue, the problem has disappeared.
I ended up with an Ajax form by removing the Remote attribute and validate the field using the ModelState.