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;
}
Related
I have Ajax Form in my view:
#using (Ajax.BeginForm("SearchHuman", "Search", new AjaxOptions(){
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result" }))
{
<div class="editor-field">
#DescriptionStrings.Lastname:
#Html.TextBox("LastName")
</div>
<div class="editor-field">
#DescriptionStrings.Firstname:
#Html.TextBox("Name")
</div>
//submit button
<input type="submit" value='Start Searching' />
//submit link
#Ajax.ActionLink("search", "OtherSearch", new{lastName ="",...}, new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "tab"
})
}
I want to have submit button and the link for 2 different searches (in different databases) using only one form. But how to pass route values from the textboxes of the form into Ajax.ActionLink?
Thanks in advance!
But how to pass route values from the textboxes of the form into Ajax.ActionLink?
You can't. You should use a submit button if you want to send the values to the server. You could have 2 submit buttons in the same form which both submit to the same controller action. Then inside this action you can test which button was clicked and based on its value perform one or the other search.
Example:
<button type="submit" name="btn" value="search1">Start Searching</button>
<button type="submit" name="btn" value="search2">Some other search</button>
and then inside your controller action:
[HttpPost]
public ActionResult SomeAction(string btn, MyViewModel model)
{
if (btn == "search1")
{
// the first search button was clicked
}
else if (btn == "search2")
{
// the second search button was clicked
}
...
}
The solution we opted for was to implement a custom ActionMethodSelectorAttribute which allowed us to differentiate which button was pressed based on its name property. We then decorated many methods with the ActionName decorator giving them all the same action name (the one specified in the BeginFrom helper), and then we used our custom ActionMethodSelector decorator to differentiate which method is to be called based on the name of the button clicked. The net result is that each submit button leads to a separate method being called.
Some code to illustrate:
In controller:
[ActionName("RequestSubmit")]
[MyctionSelector(name = "Btn_First")]
public ActionResult FirstMethod(MyModel modelToAdd)
{
//Do whatever FirstMethod is supposed to do here
}
[ActionName("RequestSubmit")]
[MyctionSelector(name = "Btn_Second")]
public ActionResult SecondMethod(MyModel modelToAdd)
{
//Do whatever SecondMethod is supposed to do here
}
In view:
#using (Ajax.BeginForm("RequestSubmit",.....
<input type="submit" id="Btn_First" name="Btn_First" value="First"/>
<input type="submit" id="Btn_Second" name="Btn_Second" value="Second"/>
As for the custom attribute:
public string name { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var btnName = controllerContext.Controller.ValueProvider.GetValue(name);
return btnName != null;
}
I have a newsletter text box that renders in a PartialView. This is the get action:
[ChildActionOnly]
public PartialViewResult NewsLetterSidebar()
{
return PartialView("_NewsLetterSidebar");
}
And this is the Razor view:
model Blog.Web.UI.ViewModels.NewsLetterViewModel
#{
ViewBag.Title = "_NewsLetterSidebar";
}
#using (Html.BeginForm("NewsLetter", "Home", FormMethod.Post))
{
<h3>News Letter</h3>
<div>
#Html.TextBoxFor(news => news.EmailAddress)
#Html.ValidationMessageFor(news => news.EmailAddress)
</div>
<div>
<input type="submit" value="Verify">
</div>
}
I want the success message to appear under the verify button in case of valid input. This is my post action:
[HttpPost]
public JsonResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
}
return Json("Done!");
}
How can I show the JSON message under the View?
Not tested but will help you to complete what you want.
[HTTPGET]
public JsonResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
}
return Json("Done!", JsonRequestBehavior.AllowGet);
}
replace this
#using (Html.BeginForm("NewsLetter", "Home", FormMethod.Post))
{}
with
#using (Ajax.BeginForm("NewsLetter", "Home", new AjaxOptions{ onsuccess:"ShowMessage"}))
{}
JS
function ShowMessage(data){
alert(data);
}
If you would like to use the first approach from my comment above, you need slightly modify your code. First of all, add Message property to your NewsLetterViewModel, then change the partial view:
#using (Ajax.BeginForm("NewsLetter", new AjaxOptions{UpdateTargetId = "newsletter-container"}))
{
<h3>News Letter</h3>
<span>#Model.Message</span>
<div>
#Html.TextBoxFor(news => news.EmailAddress)
#Html.ValidationMessageFor(news => news.EmailAddress)
</div>
<div>
<input type="submit" value="Verify">
</div>
}
Please noеe that your partial view should be wrapped into a html element with id="newsletter-container" on your page e.g:
<div id="newsletter-container">
#{Html.RenderPartial("_NewsLetterSidebar", new NewsLetterModel());}
</div>
Now, a small change in the controller:
[HttpPost]
public ActionResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
model.Message = "Done!";
}
return PartialView("_NewsLetterSidebar", model);
}
You also need to add jquery.unobtrusive-ajax.js to make it work.
I am new to asp .net mvc3. I am trying to create a failrly simple blog comments section.
I have a CommentsViewModel
public class CommentsViewModel
{
public CommentModel NewComment { get; set; }
public IList<CommentModel> CommentsList { get; set; }
}
The corresponding view is like
<div id="CommentsArea">
#Html.Partial("CommentsList", Model.CommentsList)
</div>
#using (Ajax.BeginForm("Create",
"Comment",
new { id = Model.NewComment.detailsId, comment = Model.NewComment },
new AjaxOptions { UpdateTargetId = "CommentsArea" ,
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace}))
{
<div class="display-label">Add Comment</div>
<div class="display-field">
#Html.EditorFor(model => Model.NewComment.Body)
</div>
<input type="submit" value="Post" />
}
Now when user enters Post button I want the "CommentsArea" to be updated with the new comments list and also the form values to be cleared with empty text.
Here is the Post method:
[HttpPost]
public ActionResult Create(int id, CommentModel newcomment)
{
var newsItem = m_db.NewsList.Single(news => news.Id == id);
if (!string.IsNullOrWhiteSpace(newcomment.Body))
{
newcomment.detailsId = id;
newsItem.Comments.Add(newcomment);
m_db.SaveChanges();
}
return PartialView("CommentsList", newsItem.Comments);
}
Now when user clicks Post button the list gets updated properly,
but the form values are not cleared. i.e. if I posted a comment "New Comments", the comments list gets updated with "New Comments", but that text remains inside the edit box of the form.
How do I clear that text?
just call a js function on success of form submit.
#using (Ajax.BeginForm("Create",
"Comment",
new { id = Model.NewComment.detailsId, comment = Model.NewComment },
new AjaxOptions { OnSuccess="ClearInput", HttpMethod = "Post",}))
on js function render create view again
function ClearInput(){
//call action for render create view
$.ajax({})
}
#using (Ajax.BeginForm("SaveItemAndProperties", "HomeBuilder",
new AjaxOptions
{
UpdateTargetId = "divSaveItemAndProps",
InsertionMode = InsertionMode.Replace
}))
{
#Html.EditorForModel()
<input type="submit" value="Submit" />
}
In Model which is called from EditorForModel
#Html.EditorFor(m => m.PropertyValues)
PropertyValues is a list of properties and is a calling a EditorTemplate.
From the Action I change the value and then try to update the data back to the View
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
//Change on property in modelValues
return PartialView("PropertyBuilderControl", modelmodelValues);
}
When i am debugging i see the data propertly but it does not display in the view.
Any idea why it is doing so.
What are you changing in your action? HTML helpers such as TextBoxFor, HiddenFor, DropDownListFor, CheckBoxFor, ... first look at ModelState when binding and after that in the model. So if in your controller action you intend to do something like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}
make sure you remove that value from the model state or you won't see any updates once you render the view again:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
ModelState.Remove("Foo");
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}
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.