PartialView redirects rather than inserts - asp.net-mvc

I have a form and a partial view on my razor page, the idea being that if I change the dropdownlist, the Controller does some work and sets a ViewBag.ShowAlert (bool) that triggers the partial view to be displayed.
While this works, instead of just showing the code within the partial view, the partial view shows as a new view rather than on the same view.
Any idea why?
The view looks like this
#using (Html.BeginForm("AlterVote", "ChangeVoteType"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h1>New voting preference</h1>
<hr />
<p>Please select the type of vote you wish to change to #Html.DropDownListFor(model=>model.SelectedType, ViewBag.myList as SelectList, "Voting type", new { onchange = "this.form.submit();"})</p>
<div id="partialDiv">
#if (ViewBag.ShowAlert)
{
#Html.Partial("VotingChange")
}
</div>
</div>
}
The controller handling the HttpPost is this
[HttpPost]
public PartialViewResult AlterVote(Dropdown dropType)
{
ChangeBoilerPlate(dropType.SelectedType);
dropType.CurrentType = VoteTypeNames[(int)HomeController.VoterModel.CurrentVoteType];
return PartialView("VotingChange", dropType);
}
I'm guessing that this is down to the initial view being a form, so the partial gets confused as to where to insert the view.

If I understand correctly, by the partial view shows as a new view you mean it comes with a html tag, body and the full layout again. To solve this, you need to set up the layout to null inside your partial view, like so:
#model YourNamespace.Dropdown
#{
Layout = null;
}
<!-- partial view html below -->
<div>
</div>
The div tag is just to illustrate.
While this might solve your problem, you might want to load the partial view without reloading the whole page again. This is possible using ajax, like so:
Main View
#using (Html.BeginForm("AlterVote", "ChangeVoteType"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h1>New voting preference</h1>
<hr />
<p>Please select the type of vote you wish to change to #Html.DropDownListFor(model=>model.SelectedType, ViewBag.myList as SelectList, "Voting type", new { id = "vote"})</p>
<div id="partialDiv">
</div>
</div>
}
<script type="text/javascript">
$(document).ready(function () {
$('#vote').change(function() {
var selectedType = $(this).val();
$.post('yourserver/YourController/AlterVote', { "SelectedType": selectedType })
.done(function (data) {
$('#partialDiv').html(data);
})
.fail(function () {
console.log('Whoops, something went wrong!!!');
});
});
});
</script>
So I just added a javascript to listen to that same change event on your dropdrown, but instead of submitting the form, I just use ajax to load the partial view html without reloading the entire page.
Just fix the URL and remember to set up layout to null in your partial view. Also, you might want this javascript in a separate file, thus loading it with bundles.

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.

MVC 4 - Unable to update a partial view via an Ajax call

I have an MVC 4 view that contains a partial view. The partial view is included in the main view as follows:
<div id="PartialView">
#Html.Partial("_PhotoList", #Model)
</div>
My partial view looks as follows:
#model ExchangeSite.Entities.EstateSaleSellerListing
<div style="width: 1300px; height: 300px; overflow: auto">
#foreach (var photo in #Model.ImageList)
{
<a href="javascript:DeletePhoto(#photo.ImageId);">
<img src="#Url.Action("GetPhoto", new { id = photo.ImageId })" alt="" title="Click on the image to remove it" width="250" height="190"/>
</a>
}
</div>
<script>
function DeletePhoto(imageId) {
var Url = "/EstateSaleSellerListing/DeletePhoto";
$.get(Url, { imageId: imageId }, function (data) {
$("#PartialView").html(data);
});
}
</script>
As you can see, when a user clicks on an image, the DeletePhoto() method is called. It makes a call to an action method named DeletePhoto on the named controller. The action method deletes the photo, generates a new list of photos and updates the partial view. Everything works except the partial view is never updated.
My controller code is as follows:
public ActionResult DeletePhoto(int imageId)
{
var photo = this._systemLogic.GetItem<Photo>(row => row.ImageId == imageId);
this._systemLogic.DeleteItem(photo);
EstateSaleSellerListing listing = new EstateSaleSellerListing();
GetPhotoList(listing);
return PartialView(listing);
}
The EstateSaleSellerListing entity has a list of photo objects that get displayed in the partial view.
I don't have enough experience to know why my partial view isn't updating when the action method returns.
Try to move your javascript to your main page and change
return PartialView(listing);
to
return PartialView("_PhotoList", listing);
Check your cache settings in jQuery (it looks like you're using jQuery by the syntax anyway). And I think your second parameter is a bit off (not assigning it to data)... Try this
<script>
function DeletePhoto(imageId) {
var Url = "/EstateSaleSellerListing/DeletePhoto";
$.get(Url, { cache: false, data: { imageId: imageId }}, function (data) {
$("#PartialView").html(data);
});
}
</script>
You could also have the controller method render the partial view to string if it is being loaded in a partial.
Controller...
model.PartialViewContent=PartialToString("Partials/SmallerPart",model);
return PartialView("Partials/LargerPart",model);
View
$("#PartialView").html(data.PartialViewContent);
Fyi, PartialToString is not built into mvc. I had to hack that together

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

Submit Data from partial view to a controller MVC

I have a list of employment records, you can also add an employment record from the same page using a partial view.
Heres employment.cshtml that has a partial view for the records list and a partial view to add a new record which appears in a modal pop up.
<h2>Employment Records</h2>
#{Html.RenderPartial("_employmentlist", Model);}
<p>
Add New Record
</p>
<div style="display:none">
<div id="regModal">
#{Html.RenderPartial("_AddEmployment", new ViewModelEmploymentRecord());}
</div>
</div>
Heres the partial view _AddEmployment.cshtml
#using (Html.BeginForm("AddEmployment, Application"))
{
#Html.ValidationSummary(true)
<div class="formEl_a">
<fieldset>
<legend></legend>
<div class="sepH_b">
<div class="editor-label">
#Html.LabelFor(model => model.employerName)
</div>
etc....etc....
</fieldset>
</div>
<p>
<input type="submit" class="btn btn_d" value="Add New Record" />
</p>
}
and heres my Application controller:
[HttpPost]
public ActionResult AddEmployment(ViewModelEmploymentRecord model)
{
try
{
if (ModelState.IsValid)
{
Add Data.....
}
}
catch
{
}
return View(model);
}
When compiling the following html is generated for the form:
<form action="/Application/Employment?Length=26" method="post">
It brings in a length string? and is invoking the Employment controller instead?
Hope all is clear....
QUESTION ONE: when I click the submit button from within the partial view it does not go to the controller specified to add the data. Can anyone see where im going wrong?
QUESTION TWO: When I get this working I would like to update the employment list with the new record....am I going about this the correct way? Any tips appreciated.
Answer 1: First try this and let me know if that hits your controller.
#using (Html.BeginForm("AddEmployment", "Application", FormMethod.Post))
Answer 2: To update the employment list, I would assume you would want to save the model to your database then have your employment list displayed on the same page or a different page calling the data from the DB into the the list or table to be displayed.
Edit:
It looks as though your form attributes are not being applied.
For your employment.cshtml, I personally don't use { } around my #Html statements.
You must not be doing what I stated above because your error occurs only when I write it as
#using (Html.BeginForm("AddEmployment, Application", FormMethod.Post))
missing those closing quotes is what is causing your problem.
jQuery code:
window.jQuery(document).ready(function () {
$('#btnsave').click(function () {
var frm = $("form");
var data = new FormData($("form")[0]);
debugger;
$.ajax({
url: '/Home/Update',
type: "POST",
processData: false,
data: data,
dataType: 'json',
contentType: false,
success: function (response) {
alert(response);
},
error: function (er) { }
});
return false;
});
});
Controller Code
[HttpPost]
public JsonResult Update(Generation obj)
{
if (ModelState.IsValid)
{
return Json("done");
}
else
{
return Json("error create");
}
}
Using those code you can post form using jquery and get response in jsonresult
I know this is very old Question
the reason it didn't work for you because your syntax
Here is your code
#using (Html.BeginForm("AddEmployment, Application"))
the fix
#using (Html.BeginForm("AddEmployment", "Application"))
Regards
you have put #using (Html.BeginForm("AddEmployment, Application")) what this is trying to do is invoke a action called "AddEmployment, Application" i think you meant #using (Html.BeginForm("AddEmployment", "Application"))

Resources