I cannot get the partial view to update. If I refresh the page manually, I do see the incremented count. I tried similar approach without partial view inside the countDiv with action returning a random integer and the countDiv was getting updated just fine, so its something about the partial view:
Main view:
#using (Ajax.BeginForm("AddPositive", new RouteValueDictionary { { "id", Model.Id } },
new AjaxOptions() { UpdateTargetId = "countDiv"}))
{
<div>
<input type="submit" value="For" />
</div>
}
<div id="countDiv">
#Html.Partial("PollCounts")
</div>
PollsCounts partial view:
#model MyProj.Models.Poll
<div>Positive: #Model.PositiveCount</div>
<div>Negative: #Model.NegativeCount</div>
Action:
public PartialViewResult AddPositive(int id)
{
Poll poll = db.Polls.Find(id);
db.Entry(poll).State = EntityState.Modified;
poll.PositiveCount++;
db.SaveChanges();
return PartialView("CountsPartial", poll);
}
look in your action, you're returning the countsPartial instead of the pollsCount partial view
Related
I have a sort of Master-Detail Edit form and I'm trying to follow this post: Using Ajax... to get the partial view to postback.
My Edit form has a partial view that has a list of sub items, and another partial create view in it to add new items. I'd like the partial create view to post back and update the list without refreshing the whole page if possible.
Here's what I have so far:
MyController.cs -
public ActionResult Edit(int? id)
{
//...
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
//...
return View(profileEdit);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomForm editForm)
{
//...
if (!ModelState.IsValid) return View(editForm);
using (var _db = new MkpContext())
{
var form = _db.CustomForms.Find(editForm.CustomFormId);
//...
_db.Entry(form).State = EntityState.Modified;
_db.SaveChanges(User.ProfileId);
return RedirectToAction("Index");
}
}
public ActionResult _CustomFieldList(int id)
{
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
var formCustomFields = (from cf in _db.CustomFields
where cf.CustomFormId == id
select cf);
return PartialView(formCustomFields.ToList());
}
}
// Nested in _CustomFieldList
public ActionResult _CustomFieldCreate(int id)
{
var newField = new CustomField
{
CustomFormId = id
};
return PartialView(newField);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _CustomFieldCreate(CustomField addField)
{
ViewBag.CustomFormId = addField.CustomFormId;
if (ModelState.IsValid)
{
using (var _db = new MkpContext())
{
_db.CustomFields.Add(addField);
_db.SaveChanges();
}
var newField = new CustomField
{
CustomFormId = addField.CustomFormId
};
return PartialView(newField); // Probably need to change this somehow
}
return PartialView(addField);
}
And the views:
Edit.cshtml -
#model PublicationSystem.Model.CustomForm
#{
ViewBag.Title = "Edit Custom Form";
Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}
<div class="form-horizontal">
<div class="row">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#* Fields for this form *#
}
<div id="CustomFields" class="col-md-6">
#Html.Action("_CustomFieldCreate", new { id = ViewBag.CustomFormId })
</div>
</div>
</div>
<script>
$(function () {
$("#createFieldForm").on("submit", function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$("#CustomFields").html(result);
}
});
return false;
});
});
</script>
_CustomFieldCreate.cshtml -
#model PublicationSystem.Model.CustomField
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="result"></div>
<div class="form-horizontal">
<h4>CustomField</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model =>model.CustomFormId)
<div class="row">
#* Fields for the form *#
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div id="customFieldList">
#Html.Action("_CustomFieldList", new { id = ViewBag.CustomFormId })
</div>
_CustomFieldList.cshtml
#model System.Collections.Generic.IEnumerable<PublicationSystem.Model.CustomField>
<table class="table">
#* List table code *#
</table>
Edit: I rewrote the pages so that the list is part of the create partial view. What happens now is, if you enter data for _CustomFieldCreate and press submit, the first time, it refreshes just that view (including the nested list view). However the second time, it redirects to the view, probably because the first refresh didn't rebind the javascript to the submit button. Also, the Create view doesn't clear out the fields, but persists the originally entered data.
You will need a form in your partial view whose submit action binds to a javascript function that posts to your controller.
For example if your form id is MyForm:
$('#MyForm').on('submit', function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: $(this).action, // This will submit the post to whatever action your form goes to
type: "POST", // This tells it that it is a post
data: $(this).serialize(), // This sends the data in the form to the controller
success: function (data) {
// do some javascript on success
},
error: function (xhr, ajaxOptions, thrownError) {
// do some javascript on error
}
});
});
This javascript overrides the default form submit and does an ajax post to your controller and then returns with success or error where you can do anything you want.
Here is some jquery ajax documentation:
http://api.jquery.com/jquery.ajax/
You should look into using AJAX. That should accomplish what I think you are describing. You'll want to create a javascript function that handles the submit event on the form, then post the form data to some create action in your MVC app using AJAX. If you are using jQuery, the library makes it pretty simple.
http://api.jquery.com/jquery.ajax/
I have
public PartialViewResult CodePartial(string code){
...
return PartialView("anotherpartial");
}
which on has submit button and I want that on post executed anotherpartial partialviewresult. but instead it returns this partial view inside of CodePartial view. And on debugging it's not going inside of anotherpartial action.
How can I improve that?
CodePartial.cshtml
#model Kubeti.Models.Codes
#using (Ajax.BeginForm("CodePartial", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "result", InsertionMode = InsertionMode.Replace }))
{
#Html.EditorFor(x => x.code)
#Html.ValidationMessageFor(x => x.code)
<input type="submit" value="OK" />
}
<div id="result" style="width: 500px; height:500px; border:1px solid red;">
index.cshtml
#Html.Partial("CodePartial")
#Html.Partial("anotherpartial")
Your method:
public PartialViewResult CodePartial(string code){
...
return PartialView("anotherpartial");
}
doesn't return 'to an action' as such. It simply returns the PartialView representation (meaning without the view being rendered with the layout, amongst other things) of the view that you specify.
If you want to return to another action, you need to post to that action or, alternatively, do something like this.
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);
}
On my edit view, the information doesn't seem to be displayed on the form in the textboxes. Any idea why this is happening? Any help would be greatly appreciated.
Here's how my edit functions inside the controller look like:
[HttpGet]
[Authorize(Roles = "Admin")]
public ActionResult Edit(int id)
{
var logic = new ContactBUS();
var user = logic.GetContact(id);
var mUser = Membership.GetUser(user.Username);
bool memUserExists = doesUserExist(mUser);
if (memUserExists)
{
var model = new RoleListViewModel
{
AllRoles = Roles.GetAllRoles().ToList()
};
return View(model);
}
return View(logic.GetContact(id));
}
[HttpPost]
[Authorize(Roles = "Admin")]
public ActionResult Edit(Contact contact)
{
var logic = new ContactBUS();
if (ModelState.IsValid)
{
logic.EditContact(contact);
return RedirectToAction("List");
}
else
return View(contact);
}
}
Edit.cshtml:
#model ContactWeb.Models.RoleListViewModel
<h2>Edit</h2>
<div style="float:left;width:350px;">
#{Html.RenderPartial("Form", new ContactWebLibrary.Contact());}
</div>
and Form.cshtml:
#model ContactWebLibrary.Contact
#using (Html.BeginForm()) {
<input type="hidden" value="#Model.Id" />
<fieldset id="ContactEditor">
<legend>Fields</legend>
<div>
#Html.LabelFor(c=>c.FirstName, "First Name")
#Html.TextBoxFor(c=>c.FirstName)
#Html.ValidationMessageFor(c=>c.FirstName)
</div>
<div>
#Html.LabelFor(c=>c.LastName, "Last Name")
#Html.TextBoxFor(c=>c.LastName)
#Html.ValidationMessageFor(c=>c.LastName)
</div>
...
<input type="submit" value="#(Model.Id == 0 ? "Create" : "Edit" )" />
</fieldset>
}
If memUserExists is true then a new RolesListViewModel is passed to the Edit view. This in turn passes a brand new Contact model to the partial view each time this partial view is rendered:
#{Html.RenderPartial("Form", new ContactWebLibrary.Contact());}
So the contact used in the partial will not contain any information to display, hence, no values are being displayed.
Does logic.GetContact(id) return a RoleListViewModel? Otherwise, when memUserExists is false, I don't think the following line would work when returning the Edit view:
return View(logic.GetContact(id));
And also, the following line in your [HttpPost]:
return View(contact);
This passes a Contact object to a view that is expecting a RoleListViewModel.
Hope this helps.
I've looked over a bunch of other reports of this, but mine seems to be behaving a bit differently. I am returning PartialViewResults for my child actions, so that's not the source of the recursion. Here's a dumbed down version of what I have.
// The Controller
[ChildActionOnly]
public ActionResult _EditBillingInfo()
{
// Generate model
return PartialView(model);
}
[HttpPost]
public ActionResult _EditBillingInfo(EditBillingInfoViewModel model)
{
// Update billing informatoin
var profileModel = new EditProfileViewModel()
{
PartialToLoad = "_EditBillingInfo"
};
return View("EditProfile", profileModel);
}
[ChildActionOnly]
public ActionResult _EditUserInfo()
{
// Generate model
return PartialView(model);
}
[HttpPost]
public ActionResult _EditUserInfo(EditUserInfoViewModel model)
{
// Update user informatoin
var profileModel = new EditProfileViewModel()
{
PartialToLoad = "_EditUserInfo"
};
return View("EditProfile", profileModel);
}
public ActionResult EditProfile(EditProfileViewModel model)
{
if (String.IsNullOrEmpty(model.PartialToLoad))
{
model.PartialToLoad = "_EditUserInfo";
}
return View(model);
}
// EditProfile View
#model UPLEX.Web.ViewModels.EditProfileViewModel
#{
ViewBag.Title = "Edit Profile";
Layout = "~/Views/Shared/_LoggedInLayout.cshtml";
}
<div>
<h2>Edit Profile</h2>
<ul>
<li class="up one"><span>#Ajax.ActionLink("Account Information", "_EditUserInfo",
new AjaxOptions { UpdateTargetId = "EditProfileDiv", LoadingElementId = "LoadingImage" })</span></li>
<li class="up two"><span>#Ajax.ActionLink("Billing Information", "_EditBillingInfo",
new AjaxOptions { UpdateTargetId = "EditProfileDiv", LoadingElementId = "LoadingImage" })</span></li>
</ul>
<img alt="Loading Image" id="LoadingImage" style="display: none;" src="../../Content/Images/Misc/ajax-loader.gif" />
<div id="EditProfileDiv">
#Html.Action(Model.PartialToLoad)
</div>
</div>
The partial views are both forms for updating either the user information or billing information.
I debugged through this and found what is happening, but cannot figure out why. When a user browses to EditProfile, it load up with the _EditUserInfo partial and the form is there for editing. When you change some info and submit the form it hangs and you get a StackOverflowException in the EditProfile view on the call to #Html.Action(). What happens is on the initial visit to EditProfile, the #Html.Action calls the HttpGet version of _EditUserInfo. You make some changes to the user info and click submit. Once the information is updated the EditProfile view is returned again, but this time #Html.Action calls the HttpPost version of _EditUserInfo which updates the user information again, returns the EditProfile view again and the #Html.Action calls the HttpPost version of _EditUserInfo... You get where this is going. Why after form submission does it call the post version and not the get version like it did for the initial visit to EditProfile?
Thanks for any help!
I might be getting this a bit wrong, it's been a long day so, but in EditProfile you set PartialToLoad (if it's empty) to "_EditUserInfo", then in _EditUserInfo you set it again to _EditUserInfo, won't this create a loop that behaves as what you are experiencing?