How to use ajax link instead of submit button for form? - ajax

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;
}

Related

partial views to get data and then post the results to save in database

I am very new to MVC, let me try to explain my scenario in plain simple English:
I have an strongly typed mvc form/page (Product.cshtml) with a model, say ProductViewModel.
This page has got two search buttons, one to search and bring the items to be added to the Product and other to bring in the location, most probably partial views.
Now, what I want is that these search results work in ajax form without complete post back, and then the results of these searches (items and location) should be posted back using model binding to the form when user clicks on the submit button.
What could be the best way of achieving this functionality?
Immediate responses will be well appreciated.
I thought, its good to share the complete code for clarity:
I have one form(Service1.chtml) that has a partial view to display users(_TestUser a partial view:read only), then another partial view(_PlotServiceRequestData) that should have a field to search the plot and bring back the details lke its owner name and landuser etc.
Then when I click on submit button of the main form, I should be able to read all data(main form) + new data from _PlotServiceRequestData partial view and save all data to database.
I was trying one more option, that is, to use #Ajax.ActionLink on Service1.cshtml to call the _GetPlotDetails method and then store partial view data in TempData, so that it is available to the form when users clicks on "Submit" button of Service1.cshtml, is this a right approach?, if I use ajax.BeginForm inside partial view then the data is posted to the
Service1 controller method which is actually to save the form data and not to update the partialview and in this method even I am not getting model data of the partial view.
Sevice1.cshtml:
#model ViewModels.TestViewModel
#{
ViewBag.Title =
"Service1";
}
#
using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.EditorFor(m => m.Title)
#Html.Partial(
"_TestUser", Model)
<div id="RequestPlotData">
#Html.Partial(
"_PlotServiceRequestData", Model.requestData)
</div>
<button type="submit">Save Form</button>
}
#section Scripts {
}
_PlotServiceRequestData.cshtml:
===============================
#model ViewModels.PlotServicesRequestDataViewModel
<
div id="RequestPlotData">
#
using (Ajax.BeginForm("_GetPlotDetails", "Test", new AjaxOptions { UpdateTargetId = "RequestPlotData", Url = Url.Action("_GetPlotDetails","Test") }))
{
<h1>Request Details</h1>
 
<div>
#Html.LabelFor(m => m.plotAddress)
#Html.EditorFor(m => m.plotAddress)
<input type="submit" name="submit" value="Ajax Post" />
</div>
<div>
#Html.LabelFor(m => m.LandUser)
#Html.EditorFor(m => m.LandUser)
</div>
<div>
#Html.LabelFor(m => m.OwnerName)
#Html.EditorFor(m => m.OwnerName)
</div>
}
</
div>
CONTROLLER:
==========
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.Mvc;
namespace
TestNameSpace
{
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Service1()
{
Injazat.AM.mServices.
LocalDBEntities context = new Injazat.AM.mServices.LocalDBEntities();
TestViewModel model =
new TestViewModel() { user = context.Users.First(), Title = "Land Setting Out",
requestData =
new PlotServicesRequestDataViewModel() { ServiceNumber ="122345", TransactionDate="10/10/2033" } };
return View(model);
}
[
HttpPost()]
public ActionResult Service1(TestViewModel model)
{
PlotServicesRequestDataViewModel s = (PlotServicesRequestDataViewModel)TempData[
"Data"];
TestViewModel vm =
new TestViewModel() { user = model.user, requestData = s, Title = model.Title };
return View(vm);
 
}
[
HttpGet()]
//public PartialViewResult _GetPlotDetails(string add)
public PartialViewResult _GetPlotDetails(PlotServicesRequestDataViewModel requestData)
{
//PlotServicesRequestDataViewModel requestData = new PlotServicesRequestDataViewModel() { plotAddress = add};
requestData.OwnerName =
"owner";
requestData.LandUser =
"landuser";
TempData[
"Data"] = requestData;
return PartialView("_PlotServiceRequestData", requestData);
}
}
}
You can probably use the jQuery Form plugin for this. This makes the process of posting the data from your form back to the server very easy. The form would post to an action that would return a partial view that you can then push into your UI.
To make this easier, jQuery form actually has a "target" option where it will automatically update with the server response (ie. the partial view returned from your search action).
View
<form id="searchForm" action="#(Url.Action("Search"))" method="POST">
<input name="query" type="text" /> <!-- order use Html.TextBoxFor() here -->
<input type="submit" />
</form>
<div id="result"><!--result here--></div>
Javascript
$('#searchForm').ajaxForm({
target: '#result'
});
Controller
public ActionResult Search(string query)
{
// Do something with query
var model = GetSearchResults(query);
return Partial("SearchResults", model)
}
This should hopefully help you to get on the right track. jQuery Form is a good plugin and is the main thing you should look into for ajaxifying your form posts back to the server. You might also want to look into using jQuery's $.post and $.ajax functions, but these require slightly more work.

Asp.net MVC, 4.0 - Ajax, field value is overriden after update

I am playing around with some ajax, and have experienced a very odd and to me illogical bug.
I am displaying a list of events, wrapped in a form, in a table. Each event has a unique ID (EventID). This is submitted to the action when a button is pressed.
A div surrounding the table is now updated with the partialview that the action has returned.
The problem
When the view is reloaded, all the HiddenFields that contains the field EventID, now cointains the same EventID as. the one that was submitted to the action
I have tried placing a breakpoint in the view, to see what value is put in the HiddenField. Here is see that the correct id is actually set to the field. but when the page updates, all the hiddenfields contains the same eventid as the one originally submitted to the action.
The partialview: _Events
#model SeedSimple.Models.ViewModelTest
<table class="table table-striped table-bordered table-condensed">
#foreach (var item in Model.events)
{
#using (Ajax.BeginForm("AddAttendantToEvent", "MadklubEvents", new AjaxOptions()
{
HttpMethod = "post",
UpdateTargetId = "tableevents"
}))
{
#Html.Hidden("EventID", item.MadklubEventID);
<input type="submit" value="Join!" id="join" class="btn" />
}
#using (Ajax.BeginForm("RemoveAttendantFromEvent", "MadklubEvents", new AjaxOptions()
{
HttpMethod = "post",
UpdateTargetId = "tableevents"
}))
{
#Html.Hidden("EventID", item.MadklubEventID);
<input type="submit" value="Leave" class="btn" />
}
}
</table>
AddAttendantToEvent Action:
[HttpPost]
[Authorize]
public ActionResult AddAttendantToEvent(int EventID)
{
if (ModelState.IsValid)
{
var uow = new RsvpUnitofWork();
var currentUser = WebSecurity.CurrentUserName;
var Event = uow.EventRepo.Find(EventID);
var user = uow.UserRepo.All.SingleOrDefault(u => u.Profile.UserName.Equals(currentUser));
user.Events.Add(Event);
Event.Attendants.Add(user);
uow.Save();
ViewModelTest viewmodel = new ViewModelTest();
viewmodel.events = madklubeventRepository.AllIncluding(madklubevent => madklubevent.Attendants).Take(10);
viewmodel.users = kitchenuserRepository.All;
return PartialView("_Events", viewmodel);
}
else
{
return View();
}
}
How all the input fields look after having submitted EventID 4 to the action
<input id="EventID" name="EventID" type="hidden" value="4">
I am suspecting, this is due to some side-effects from the ajax call, that i am unknown to.
Any enlightentment on the subject would be much appreciated :)

Ajax.BeginForm not updating target div

Controller:
public ActionResult Edit(string temp)
{
ViewBag.Time = DateTime.Now.ToString("hh:mm:ss");
return PartialView("Edit");
}
Partial View:
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions{UpdateTargetId = "mydiv"}))
{
<input type="submit" value="Save" />
}
Index View (part of contents)
<div id="mydiv">
<span>The Time is: #ViewBag.Time</span>
</div>
#Html.Partial("Edit")
ClientValidationEnabled and UnobtrusiveJavaScriptEnabled are true
jquery.validate.min.js, jquery.validate.unobtrusive.min.js, jquery.unobtrusive-ajax.min.js, MicrosoftMvcAjax.js and MicrosoftAjax.js are added
At first, the time is shown correctly. When the Save button is clicked for the first time, time disappears and Save button is shown twice and then nothing happens except calling the Action on clicking on both buttons.
You have things kind of backwards. Try this:
Controller
public ActionResult Edit(string temp)
{
ViewBag.Time = DateTime.Now.ToString("hh:mm:ss");
return PartialView("Edit");
}
Index View
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions{UpdateTargetId = "mydiv"}))
{
<input type="submit" value="Save" />
}
#Html.Action("Edit")
Partial View (Edit)
<div id="mydiv">
<span>The Time is: #ViewBag.Time</span>
</div>
The ViewBag is only accessable at runtime (when the page initially loads) so this means if you fetch data via ajax, the viewbag in the controller action is only accessable to the partial view of that controller action (and not index.cshtml which called the action via ajax). In short (tl;dr) to be able to use the viewbag which you set in Edit action, you need to use it in the returning partialview. (and not anywhere else, because the content isnt re-rendered by the razor engine)
I set things up like so.
Very simple, but I noticed the person posting the
question and the answer had no insertion mode so I posted this lame bit of code :)
{
HttpMethod = "post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "the div youwant to update / replace"
}

Using two button in a form calling different actions

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;
}

MVC3 RemoteAttribute and muliple submit buttons

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.

Resources