asp.net mvc Ajax.BeginForm clone - asp.net-mvc

I'm using asp.net mvc ajax.
The partial view is using Ajax.BeginForm (just an example):
<div id="divPlaceholder">
<% using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "divPlaceholder" })) { %>
... asp.net mvc controls and validation messages
<input type="submit" value="Save" />
<% } %>
</div>
After update, if validation fails, the html is:
<div id="divPlaceholder">
<div id="divPlaceholder">
...form
</div>
</div>
I don't like that the returned html is inserted, instead it should replace original div.
Probably on POST I should not render <div> around form in partial view or render the div without id.
What else can I do in this situation?
I was thinking that maybe I should write a helper, something like Ajax.DivBeginForm, which will render form inside div on GET and hide the div on POST.
Can somebody provide a good advice how to write such helper (Ajax.DivBeginForm)?
I'd like it to work with using keyword:
<% using (Ajax.DivBeginForm(new AjaxOptions { UpdateTargetId = "myId" })) { ... }%>

My solution. Please comment if something is wrong.
public class DivMvcForm : MvcForm
{
private bool _disposed;
private MvcForm mvcForm;
private ViewContext viewContext;
public DivMvcForm(MvcForm mvcForm, ViewContext viewContext) : base(viewContext)
{
this.mvcForm = mvcForm;
this.viewContext = viewContext;
}
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
mvcForm.EndForm();
viewContext.Writer.Write("</div>");
}
}
}
Helper
public static class AjaxHelperExtensions
{
public static MvcForm DivBeginForm(this AjaxHelper ajaxHelper, AjaxOptions ajaxOptions)
{
var tagBuilder = new TagBuilder("div");
if (ajaxHelper.ViewContext.HttpContext.Request.RequestType == "GET"
&& string.IsNullOrWhiteSpace(ajaxOptions.UpdateTargetId) != true)
{
tagBuilder.MergeAttribute("id", ajaxOptions.UpdateTargetId);
}
ajaxHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
var theForm = ajaxHelper.BeginForm(ajaxOptions);
return new DivMvcForm(theForm, ajaxHelper.ViewContext);
}
}
And how it works
<% using (Ajax.DivBeginForm(new AjaxOptions { UpdateTargetId = "divPlaceholder" })) { %>
... controls
<% } %>
Result - when ModelState is invalid the partial view returns div without id.

I'm going to take a little different approach here, rather than getting your original solution to work, I'd recommend using the pattern normally followed in this scenario and not using a helper. I realize this is a bit later than the original post, but for future use by anyone : )
If your partial view has a form, then you will keep posting, and returning a form in a form in a form in a form, etc. so you want to have your PARENT contain BeginForm, the div, and renderpartial
using (Ajax.BeginForm("Index", "ProjectManager", new AjaxOptions() ....
<div id="divPlaceholder">
Html.RenderPartial(....)
</div>
If you want to encapsulate this logic in say, an "Order" partial view that is displayed on a Customer screen, then you have two options.
1. Include the BeginForm on the parent Customer view (which reduces code reusability as any view that wants to include the "Order" partial view must include the ajax wiring.
Or
2. You have two partial views for order. One is OrderIndex.ascx (or cshtml if razor) and one is OrderIndexDetail.ascx (or whatever naming convention you decide)
OrderIndex contains your Ajax.beginform and OrderIndexDetail has no form, only the partial view details.
Option 2 is more code (ok, literally about 30 more seconds of coding to move the ajax.beginform into another view) but increases code reusability.

You can handle submit form yourself:
<div id="divPlaceholder">
<% using (Html.BeginForm("action", "controller", FormMethod.Post, new { id = "submitForm"})) { %>
... asp.net mvc controls and validation messages
<input type="submit" value="Save" />
<% } %>
</div>
and write some javascript as:
$('#submitForm').submit(function() {
$.post('post-to-this-url',
data: { foo: formvalue1, bar: formvalue2},
function(data) {
// update html here
});
return false;
})

Related

Is it possible to prevent parent view from reloading

I have two views, one master to the other. There are certain cases when I need the parent view to stay the same while the child view reloads. Is AJAX the only option, or is there another way of doing this?
P.S. Even with the only option being AJAX I'd really appreciate if someone could show the steps to take in ASP.NET MVC.
Yes, only an Ajax call will prevent you from loading the whole page.
Let's say this is your page scheme:
<div id="master">
<div id="section1">
// use render partial to render this
</div>
<div id="section2">
// use render partial to render this
</div>
</div>
In order to reload a section you can use JQuery.load to reload only it:
$("#section2").load('#Url.Action("Action", "Controller")');
Using Ajax forms is a way I like to do something similar as you can use the UpdateTargetId to render your partial view, and you can easily use the AntiForgeryToken features
View:
<div>
#using (Ajax.BeginForm("MyAction", new { id = #Model.MyData }, new AjaxOptions
{
InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "renderView"
}))
{
#Html.AntiForgeryToken()
<input type="submit" name="submit" value="Submit" />
}
</div>
// This will get populated with the partial
<div id="renderView" />
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> MyAction(int id)
{
var output = new MyModel{ .....};
return PartialView(output);
}

POST from partial view is hitting controller twice

I have a form in a view (Edit view), and a partial view inside that form on the Edit view. The partial view has its own form which performs a lookup. The lookup in the partial view is successfully returning the results to the Edit view. However, the POST from the partial view is then hitting the controller a second time (trying to submit the form in the Edit view). How do I stop the POST from hitting the controller a second time?
Here is where the partial view is called in the Edit view:
<div class="form-group" id="search-pac">
#Html.Action("PacSearch", "ItemRequest");
</div>
<div class="form-group" id="search-pac-results">
</div>
Here is where the controller gets the partial view:
[HttpGet]
public ActionResult PacSearch()
{
return PartialView("_PacSearchFormPartial");
}
Here is the form in the partial view:
#using (Ajax.BeginForm("PacSearch", "ItemRequest", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "search-pac-results"
}))
{
<div>
#Html.TextBox("pacupc")
<input type="submit" value="Find PAC" />
</div>
}
Which then hits the controller here:
[HttpPost]
public ActionResult PacSearch(string pacupc)
{
//do lookup stuff, and call a partial view to display the results
}
Once the results are displayed on the Edit view, POST then hits the controller here (which I don't want unless the submit button in the Edit view is clicked):
[HttpPost]
public ActionResult Edit(ItemRequest itemRequest, HttpPostedFileBase upcImage, Comment comment, String FinalApproval)
{
//handle form submission from Edit View
}
How do I keep the POST from the partial view from hitting the HttpPost for Edit view in the controller?
UPDATE:
Upon the suggestion to use a direct AJAX call, I ditched the partial views and changed my Edit view to:
<div class="form-group" id="search-pac">
#Html.TextBox("pacupc")
<input type="button" id="btn-pacupc" value="Find PAC" />
#* #Html.Action("PacSearch", "ItemRequest");*#
</div>
<div class="form-group" id="search-pac-results">
</div>
And AJAX call:
<script type="text/javascript">
$(document).ready(function () {
$(document).on('click', '#btn-pacupc', function () {
var pacupc = $("#pacupc").val();
$.ajax({
type: "POST",
url: "#Url.Action("PacSearch")",
data: { pacupc: pacupc },
success: function (result) { $('#search-pac-results').html(result); }
});
});
});
</script>
There's no support for forms within forms in HTML. A submission inside the innermost form will also submit any parent form. The solution then, is to not rely on Ajax.BeginForm, which will print a form element to the page, and instead, wire your AJAX manually. This is a prime example of why I encourage everyone to not use the Ajax family of helpers. They simply do too much, hidden to the developer, and often lead to confusion when things don't work as expected, which happens far more often than not.

ASP.net MVC Routing on Postback

In my ASP.net MVC View I have a dropdown that I want to get details on selection and asynchronously update a div. My aspx is as follows:
<% using (Html.BeginForm("Index", "Portal", FormMethod.Post, new { id = "TheForm" }))
{%>
<h2>Index</h2>
<% using (Ajax.BeginForm("Details", new AjaxOptions { UpdateTargetId = "mpkResults" }))
{ %>
<%=Html.DropDownList("Docs", (IEnumerable<SelectListItem>)ViewData["Docs"],
new { onchange = "document.getElementById('TheForm').submit();" })%>
<p><input type="submit" value="Details" /></p>
<% } %>
<div id="mpkResults" style="margin:10px 0px 0px 0px;"></div> ...
The onchange event fires correctly on selection of the dropdown, but instead of the Details method in my code behind firing, it hits my Index method. Why is the details method not getting hit on the onchange event? My Details() method in the controller is:
public ActionResult Details()
{
... < It never gets here, just goes to the index() method
}
It's a little frustrating right now since I'm sure it is a simple mistake but not sure what it could be. I looked at the Source of my page and sure enough, the form looks like it should be routing to the Details Action:
<form action="/Portal/Details" method="post" ...
Any help would be appreciated.
That's because in your onchange handler, you're calling the submit() method on the TheForm form instead of your AJAX form:
new { onchange = "document.getElementById('TheForm').submit();" })%>
^^ wrong form ID
Give your AJAX form another ID, and use that instead.

how to render a full view using Ajax.BeginForm

I have a partial view which has a Ajax.BeginForm, with a UpdateTargetID set. When the validation on the form fails the update target id is replaced with the validation errors, but when there are no validation errors users should be redirected to a new page.
The code in my Partial view is
<div id="div_UID">
<% using (Ajax.BeginForm("FindChildByUID", new AjaxOptions { UpdateTargetId = "div_UID" } ))
{%>
<p>
<label>UID:</label>
<%= Html.TextBox("UID") %>
</p>
<input type="submit" value="Continue" />
<% } %>
</div>
</pre>
The code in my controller is as follows
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult FindChildByUID(Student student)
{
Student matchingStudent = _studentService.FindChildByUID(student.UID);
if (matchingStudent == null)
{
ModelState.AddModelError("UID", String.Format("No matching child found for the entered UID: {0}", student.UID));
return PartialView();
}
else
{
// full view
return RedirectToAction("ConfirmChildDetails", matchingStudent);
}
}
So, for I have been unsuccessful to display the full view on it's own, as it always seems to dipslay the full view in the UpdateTargetID div specfied in the Ajax.BeginForm.
Any suggestions on how I can get this to work?
Thanks
What your AJAX post is doing is making a request and waiting on a response that contains html to input onto the page. The configuration is such that whatever html is returned will be injected into the div you've named "div_UID".
I typically avoid scenarios like this and use traditional posting if a redirect is required upon a successful outcome of the POST.
I imagine you could do it like this using jQuery to submit rather than the Ajax.BeginForm (or just set a callback function for your Ajax.BeginForm):
function SubmitForm(form) {
$(form).ajaxSubmit({ target: "#div_to_update", success: CheckValidity });
}
function CheckValidity(responseText) {
var value = $("#did_process_succeed").val();
if (value == "True") {
window.location.replace("url_of_new_action_here");
}
}
You just have to have a hidden field in your partial view called "did_process_succeed" and set the value of True or False based on some logic in your controller.
There are likely other ways as well. Perhaps someone else will chime in. I hope this helps for now.

ASP.NET MVC ActionLink and post method

Can anyone tell me how can I submit values to Controller using ActionLink and POST method?
I don't want to use buttons.
I guess it has something with jquery.
If you're using ASP MVC3 you could use an Ajax.ActionLink(), that allows you to specify a HTTP Method which you could set to "POST".
You can't use an ActionLink because that just renders an anchor <a> tag.
You can use a jQuery AJAX post.
Or just call the form's submit method with or without jQuery (which would be non-AJAX), perhaps in the onclick event of whatever control takes your fancy.
You can use jQuery to do a POST for all your buttons. Just give them the same CssClass name.
Use "return false;" at the end of your onclick javascript event if you want to do a server side RedirectToAction after the post otherwise just return the view.
Razor Code
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
#Html.ActionLink("Save", "SaveAction", "MainController", null, new { #class = "saveButton", onclick = "return false;" })
}
JQuery Code
$(document).ready(function () {
$('.saveButton').click(function () {
$(this).closest('form')[0].submit();
});
});
C#
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveAction(SaveViewModel model)
{
// Save code here...
return RedirectToAction("Index");
//return View(model);
}
#Aidos had the right answer just wanted to make it clear since it is hidden inside a comment on his post made by #CodingWithSpike.
#Ajax.ActionLink("Delete", "Delete", new { id = item.ApkModelId }, new AjaxOptions { HttpMethod = "POST" })
Here was an answer baked into the default ASP.NET MVC 5 project I believe that accomplishes my styling goals nicely in the UI. Form submit using pure javascript to some containing form.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
<a href="javascript:document.getElementById('logoutForm').submit()">
<span>Sign out</span>
</a>
}
The fully shown use case is a logout dropdown in the navigation bar of a web app.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<div class="dropdown">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="ma-nav-text ma-account-name">#User.Identity.Name</span>
<i class="material-icons md-36 text-inverse">person</i>
</button>
<ul class="dropdown-menu dropdown-menu-right ma-dropdown-tray">
<li>
<a href="javascript:document.getElementById('logoutForm').submit()">
<i class="material-icons">system_update_alt</i>
<span>Sign out</span>
</a>
</li>
</ul>
</div>
}
ActionLink will never fire post. It always trigger GET request.
Use the following the Call the Action Link:
<%= Html.ActionLink("Click Here" , "ActionName","ContorllerName" )%>
For submitting the form values use:
<% using (Html.BeginForm("CustomerSearchResults", "Customer"))
{ %>
<input type="text" id="Name" />
<input type="submit" class="dASButton" value="Submit" />
<% } %>
It will submit the Data to Customer Controller and CustomerSearchResults Action.
This is taken from the MVC sample project
#if (ViewBag.ShowRemoveButton)
{
using (Html.BeginForm("RemoveLogin", "Manage"))
{
#Html.AntiForgeryToken()
<div>
#Html.Hidden("company_name", account)
#Html.Hidden("returnUrl", Model.returnUrl)
<input type="submit" class="btn btn-default" value="Remove" title="Remove your email address from #account" />
</div>
}
}
Use this link inside Ajax.BeginForm
#Html.ActionLink(
"Save",
"SaveAction",
null,
null,
onclick = "$(this).parents('form').attr('action', $(this).attr('href'));$(this).parents('form').submit();return false;" })
;)
My Solution to this issue is a fairly simple one. I have a page that does a customer search one by the whole email and the other by a partial, the partial pulls and displays a list the list has an action link that points to a actionresult called GetByID and passes in the id
the GetByID pulls the data for the selected customer then returns
return View("Index", model);
which is the post method
This has been a difficult problem for me to solve. How can I build a dynamic link in razor and html that can call an action method and pass a value or values to a specific action method? I considered several options including a custom html helper. I just came up with a simple and elegant solution.
The view
#model IEnumerable<MyMvcApp.Models.Product>
#using (Html.BeginForm()) {
<table>
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
</tr>
</thead>
#foreach (Product p in Model.Products)
{
<tr>
<td>#p.Name</td>
<td>#p.Price.ToString()</td>
<td>#p.Quantity.ToString()</td>
</tr>
}
</table>
}
The action method
public ViewResult Edit(Product prod)
{
ContextDB contextDB = new ContextDB();
Product product = contextDB.Products.Single(p => p.ProductID == prod.ProductId);
product = prod;
contextDB.SaveChanges();
return View("Edit");
}
The point here is that Url.Action does not care whether the action method is a GET or a POST. It will access either type of method. You can pass your data to the action method using
#Url.Action(string actionName, string controllerName, object routeValues)
the routeValues object. I have tried this and it works. No, you are not technically doing a post or submitting the form but if the routeValues object contains your data, it doesnt matter if its a post or a get. You can use a particular action method signature to select the right method.
I have done the same issue using following code:
#using (Html.BeginForm("Delete", "Admin"))
{
#Html.Hidden("ProductID", item.ProductID)
<input type="submit" value="Delete" />
}
This is my solution for the problem.
This is controller with 2 action methods
public class FeedbackController : Controller
{
public ActionResult Index()
{
var feedbacks =dataFromSomeSource.getData;
return View(feedbacks);
}
[System.Web.Mvc.HttpDelete]
[System.Web.Mvc.Authorize(Roles = "admin")]
public ActionResult Delete([FromBody]int id)
{
return RedirectToAction("Index");
}
}
In View I render construct following structure.
<html>
..
<script src="~/Scripts/bootbox.min.js"></script>
<script>
function confirmDelete(id) {
bootbox.confirm('#Resources.Resource.AreYouSure', function(result) {
if (result) {
document.getElementById('idField').value = id;
document.getElementById('myForm').submit();
}
}.bind(this));
}
</script>
#using (Html.BeginForm("Delete", "Feedback", FormMethod.Post, new { id = "myForm" }))
{
#Html.HttpMethodOverride(HttpVerbs.Delete)
#Html.Hidden("id",null,new{id="idField"})
foreach (var feedback in #Model)
{
if (User.Identity.IsAuthenticated && User.IsInRole("admin"))
{
#Html.ActionLink("Delete Item", "", new { id = #feedback.Id }, new { onClick = "confirmDelete("+feedback.Id+");return false;" })
}
}
...
</html>
Point of interest in Razor View:
JavaScript function confirmDelete(id) which is called when the link generated with #Html.ActionLink is clicked;
confirmDelete() function required id of item being clicked. This item is passed from onClick handler confirmDelete("+feedback.Id+");return false; Pay attention handler returns false to prevent default action - which is get request to target. OnClick event for buttons could be attached with jQuery for all buttons in the list as alternative (probably it will be even better, as it will be less text in the HTML page and data could be passed via data- attribute).
Form has id=myForm, in order to find it in confirmDelete().
Form includes #Html.HttpMethodOverride(HttpVerbs.Delete) in order to use the HttpDelete verb, as action marked with the HttpDeleteAttribute.
In the JS function I do use action confirmation (with help of external plugin, but standard confirm works fine too. Don't forget to use bind() in call back or var that=this (whatever you prefer).
Form has a hidden element with id='idField' and name='id'. So before the form is submitted after confirmation (result==true), the value of the hidden element is set to value passed argument and browser will submit data to controller like this:
Request URL:http://localhost:38874/Feedback/Delete
Request Method:POST Status Code:302 Found
Response Headers
Location:/Feedback
Host:localhost:38874
Form Data X-HTTP-Method-Override:DELETE id:5
As you see it is POST request with X-HTTP-Method-Override:DELETE and data in body set to "id:5". Response has 302 code which redirect to Index action, by this you refresh your screen after delete.
I would recommend staying pure to REST principles and using an HTTP delete for your deletes. Unfortunately HTML Specs only has HTTP Get & Post. A tag only can a HTTP Get. A form tag can either do a HTTP Get or Post. Fortunately if you use ajax you can do a HTTP Delete and this is what i recommend. See the following post for details: Http Deletes
Calling $.post() won't work as it is Ajax based. So a hybrid method needs to be used for this purpose.
Following is the solution which is working for me.
Steps:
1. Create URL for href which calls the a method with url and parameter
2. Call normal POST using JavaScript method
Solution:
In .cshtml:
View
Note: the anonymous method should be wrapped in (....)()
i.e.
(function() {
//code...
})();
postGo is defined as below in JavaScript.
Rest are simple..
#Url.Action("View") creates url for the call
{ 'id': #receipt.ReceiptId } creates parameters as object which is in-turn converted to POST fields in postGo method. This can be any parameter as you require
In JavaScript:
(function ($) {
$.extend({
getGo: function (url, params) {
document.location = url + '?' + $.param(params);
},
postGo: function (url, params) {
var $form = $("<form>")
.attr("method", "post")
.attr("action", url);
$.each(params, function (name, value) {
$("<input type='hidden'>")
.attr("name", name)
.attr("value", value)
.appendTo($form);
});
$form.appendTo("body");
$form.submit();
}
});
})(jQuery);
Reference URLs which I have used for postGo
Non-ajax GET/POST using jQuery (plugin?)
http://nuonical.com/jquery-postgo-plugin/
jQuery.post() will work if you have custom data. If you want to post existing form, it's easier to use ajaxSubmit().
And you don't have to setup this code in the ActionLink itself, since you can attach link handler in the document.ready() event (which is a preferred method anyway), for example using $(function(){ ... }) jQuery trick.
Came across this needing to POST from a Search (Index) page to the Result page. I did not need as much as #Vitaliy stated but it pointed me in the right direction. All I had to do was this:
#using (Html.BeginForm("Result", "Search", FormMethod.Post)) {
<div class="row">
<div class="col-md-4">
<div class="field">Search Term:</div>
<input id="k" name="k" type="text" placeholder="Search" />
</div>
</div>
<br />
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-default">Search</button>
</div>
</div>
}
My Controller had the following signature method:
[HttpPost]
public async Task<ActionResult> Result(string k)

Resources