MVC includes remnant of partial view when full view is requested - asp.net-mvc

In my project I have one page that I render as a partial via an ajax call. The target for this page is a div in my _Layout page with an id of "bodyContent" like this:
<div id="bodyContent">
#RenderBody()
</div>
Note this is the same div where .net renders the document body.
I load my page with this small script:
this.loadBodyContent = function (uri) {
return $.get(uri).done(function (data) {
$('#bodyContent').html(data);
});
}
The uri in the script points to a method on my controller that looks like this:
public ActionResult Index()
{
vm.Initialize();
return PartialView(vm);
}
All of this works fine until I want to navigate back to the home page. In my page I have a button that calls a method on my controller that contains this line:
return RedirectToAction("Index", "Home", new { noCache = DateTime.Now.Ticks.ToString() });
This method works fine and in fact calls the Index action on the Home controller every time (i.e. it is not being cached). Index on the Home controller looks like this:
public ActionResult Index()
{
ProcedureTreeView procedureTreeView = new ProcedureTreeView("treeview1", "");
ViewBag.data = procedureTreeView.Render();
return View();
}
Note that Index returns a view which I believe means it should render the entire page - however when the page is rendered my partial page is rendered again in the bodyContent div and the home page is rendered after it. So it looks like this
<div id="bodyContent">
partial page content here (this is the problem - it should not render again)
home page content here
</div>
My question is - why is the View returned from Index on the home controller not re-rendering the entire page from scratch? How do I fix it?
This behavior is observed in both IE and Chrome.
Using MVC 5.1, .net 4.5
Edit: additional code per request. Here is how the partial page is posted back:
#using (Ajax.BeginForm("Search", "EndUserSearch", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "searchResults" }, new { id="searchForm" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Search" name="submitButton" class="btn btn-default" />
<input type="submit" value="Cancel" name="submitButton" class="btn btn-default" />
</div>
</div>
</div>
}

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.

How to set an action for a MVC forum?

I want to set an action of a form when the user submits it. Instead of it reloading the current page I want to direct it to a controller to handle the request. How would I do this using the Razor engine within the the view.
<h2>#Html.DisplayFor(model => model.Title)</h2>
<p>#Html.Markdown(Model.Body)</p>
#if (Request.IsAuthenticated)
{
using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
{
Model.ReplyId = ViewBag.Thread;
Model.Body = "";
}
#Html.HiddenFor(m => m.ReplyId);
<div class="form-horizontal">
<h4>Reply</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(m => m.Body, "Markdown")
<div id="mdFormatted"></div>
</div>
</div>
<div><input type="submit" value="Reply" class="btn btn-default" /></div>
</div>
}
}
else
{
Response.Redirect(Url.Action("Index"));
}
You can do that like so:
#using (Html.BeginForm("action", "controller"))
{
}
There is also an overload to specify whether the form should be sent via GET or POST:
#using (Html.BeginForm("action", "controller", FormMethod.Post))
{
}
As a separate aside, you're not really following the MVC pattern with the flow of the rest of your code. Specifically, you shouldn't be doing any redirecting to different views from within a view itself, because that's exactly what a controller is responsible for. In your example, it would be better to do something like this:
public ActionResult YourAction()
{
if (!Request.IsAuthenticated)
return RedirectToAction("Index");
return View();
}
This allows you to move logic, which shouldn't be in the view, to the controller, which both simplifies your view and enforces the MVC pattern. One other note would be to look into using AuthorizeAttribute if you're redirecting the user to a login action.

What to do if action button and form fields are in different sections?

Let's say we have an edit form to create a new user. Now the save button is placed in a different section, the footer.
My problem is that I can't get the edit fields and the save button in one form, because the button is in a different section.
Because of that, I can't submit the form.
What is the best approach to this problem?
_Layout.cshtml
<div class="content">
#RenderBody()
</div>
<div class="footer">
#RenderSection("Footer")
</div>
Index.cshtml
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section Footer
{
<input type="submit" value="Save" />
}
#using(Html.BeginForm())
{
<h2>New User</h2>
#Html.EditorForModel()
}
You could call form.Dispose() explicitly, instead of the using statement:
#{ var form = Html.BeginForm() }
<h2>New User</h2>
#Html.EditorForModel()
#section Footer
{
<input type="submit" value="Save" />
#{ form.Dispose(); }
}
Edit
But you have to at least make sure the Body and Footer section are in the same container, for example:
<div class="content">
#RenderBody()
<div class="footer">
#RenderSection("Footer")
</div>
</div>
With the layout as written in the question, the content div (and therefore the form tag) must close before the submit button can ever appear. There's no way this can work logically:
<div class="content">
#RenderBody() ## form opens, and therefore must close here
</div>
<div class="footer">
#RenderSection("Footer") ## submit button is here -- can never be inside the form
</div>
Editorial aside: It seems like a very bad idea to have a form split across multiple partial views. You might call it a code smell -- I'd try to avoid it if possible.
You found a pretty awkward work around. I suggest doing it this way:
In order to distinguish actions of different buttons clicked, create a new property in your model: public string Action { get; set; }
Give you form an id and include a hidden input for your new model property.
<form id="my-form">
#Html.HiddenFor(x => x.Action)
...
</form>
Create buttons in the footer, with the same class, but different values:
<button class="btn-submit" value="action1">Submit</button>
<button class="btn-submit" value="action2">Submit</button>
Use the following JavaScript:
$('.btn-submit').live('click', function() {
// update value of hidden input inside the form
$('#Action').val($(this.val()));
// submit the form
$('#my-form').submit();
});
In your ActionResult perform different actions based on the value of Action property:
public ActionResult WahteverAction(WhateverModel model)
{
if(ModelState.IsValid)
{
if(model.Action == "action1")
{
// do whatever needs to be done for action1
}
if(model.Action == "action2")
{
// do whatever needs to be done for action2
}
}
return View();
}
I found a workaround for my problem. It's not nice, but it works.
I replaced the submit button with an anchor. When the anchor is clicked, a javascript function gets called.
<a name="save" onclick="submitAction(this)"></a>
The javascript function creates a hidden submit button in the form and clicks it.
function submitAction(sender) {
var action = $(sender).attr('name');
$('form').append('<input type="submit" style="display:none" id="tmpSubmit" />');
$('#tmpSubmit').attr('name', action);
$('#tmpSubmit').click();
}

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