ASP.net MVC Routing on Postback - asp.net-mvc

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.

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 Error in HttpPost Create - Null Values Passed in Model after POST - Model Binding Issue?

I have a problem with what should be a very simple ASP.NET MVC Create method.
After I populate data and submit the form in the Create view (and all the validation rules are passed), the instance of the model passed to the POST method will contain either null or 0 values for every field and ModelState.IsValid will return false.
I use Firebug to check the POST form parameters, and the submit action obviously has posted all the parameters from the fields. However, these parameters are not bound to the model passed to the POST method in the Controller, hence, the model (namely material in the code) contains null values in all field (as checked using Watch and breakpoints placed at the beginning of the POST method).
POST parameters from Firebug:
Content-Type: application/x-www-form-urlencoded Content-Length: 228 MaterialNoMass.MaterialNoMassID=9mmPlasterboard&MaterialNoMass.RoughnessTypeID=3&MaterialNoMass.Resistance=0.0560&MaterialNoMass.ThermalAbsorptance=0.09&MaterialNoMass.SolarAbsorptance=0.07&MaterialNoMass.VisibleAbsorptance=0.07
Create View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Database.Master"
Inherits="System.Web.Mvc.ViewPage<MvcDeepaWebApp.ViewModels.DatabaseSimpleMaterialViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Create MaterialNoMass</legend>
<%: Html.EditorFor(model => model.MaterialNoMass, new { RoughnessTypes = Model.RoughnessTypes })%>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
GET and POST methods of Create in Controller:
//
// GET: /DatabaseSimpleMaterial/Create
public ActionResult Create()
{
var viewModel = new DatabaseSimpleMaterialViewModel
{
MaterialNoMass = new MaterialNoMass(),
RoughnessTypes = deepaDB.RoughnessTypes.ToList()
};
return View(viewModel);
}
//
// POST: /DatabaseSimpleMaterial/Create
[HttpPost]
public ActionResult Create(MaterialNoMass material)
{
if (ModelState.IsValid)
{
MaterialAllType mt = new MaterialAllType();
mt.MatID = material.MaterialNoMassID;
mt.MatTypeID = deepaDB.MaterialTypes.Single(type => type.MatType == "SimpleMaterial").MatTypeID;
material.MatTypeID = mt.MatTypeID;
deepaDB.AddToMaterialAllTypes(mt);
deepaDB.AddToMaterialNoMasses(material);
deepaDB.SaveChanges();
return RedirectToAction("Index");
}
//Error occurred
var viewModel = new DatabaseSimpleMaterialViewModel
{
MaterialNoMass = material,
RoughnessTypes = deepaDB.RoughnessTypes.ToList()
};
return View(viewModel);
}
FYI, whenever a new MaterialNoMass is created, a new instance of MaterialAllType should also be created using the same ID of MaterialNoMass. I haven't even reached the stage of modifying the database yet, since the model binding seems to be not working.
I've used similar Create approach in other Controllers in the application and they all work fine except in this Controller. I've been having a hard time in debugging this for the past two days. Please help!
The problem comes from the fact that the model you are passing to the view is DatabaseSimpleMaterialViewModel and inside the POST action you expect MaterialNoMass. So make sure you use the correct prefix:
[HttpPost]
public ActionResult Create([Bind(Prefix="MaterialNoMass")]MaterialNoMass material)
I suppose that you editor template generates fields like this:
<input type="text" name="MaterialNoMass.MaterialNoMassID" value="" />
<input type="text" name="MaterialNoMass.RoughnessTypeID" value="" />
...
So you need to indicate the model binder that there's such prefix.

asp.net mvc Ajax.BeginForm clone

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

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