Ajax postback partialview not getting loaded with updated image - c#

I am trying to create a sample MVC4 webpage with partialViews
on my parent page ,eg., Index.cshtml page I am displaying a partialView page which will allow the user to view/update profile photo
When the index page loads ,I need this partial page to show up the photo if photo is available
once the page is loaded ,when the user uploads a new photo,I need only the partialView page to do an ajax postback and show up the new photo .
I am able to load the page with photo fetched from DB,
I am able to Save new photo to db by clicking "#btnPhotoUpload" button.
But after saving the photo ,the partialview is not getting refreshed automatically.Please help me how to get my partialview page to refesh and display the updated photo.
Here is my index page ie., "Index.cshtml"
#model MvcSamples.Models.ViewModels.UserInfoViewModel
#{
ViewBag.Title = "Ajax Partial Postback demo";
ViewBag.UserId = 1;
}
<h2>PersonalInfo example</h2>
<div id="photoForm">
#Html.Partial("_UserPhoto")
</div>
<div id="OtherDetails">
#Html.Partial("_UserDetails")
</div>
Here is my PartialView, i.e. _UserPhoto.cshtml
#model MvcSamples.Models.ViewModels.UserInfoViewModel
#using (Ajax.BeginForm("SaveProfilePhoto", "Example", new { id = "1" }, new AjaxOptions { UpdateTargetId = "photoForm", OnSuccess = "onSuccess" }, new { encType = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<a>
<img id="imgPhoto" width="100px" height="100px"/>
<label for="photo">Photo:</label>
<input type="file" name="photo" id="photo" />
<input id="btnPhotoUpload" type="button" value="Apply" />
</a>
<script type="text/javascript">
$(document).ready(function () {
$("#imgPhoto").attr('src', "#Url.Action("GetProfileImage", "Example", new { id = ViewBag.UserId })");
$("#btnPhotoUpload").click(function (event) {
//on-click code goes in here.
event.preventDefault();
SavePhotoToDb();
});
function SavePhotoToDb() {
var json;
var data;
$.ajax({
type: "POST",
url: "/Example/SaveProfilePhoto",
data: new FormData($("#form0").get(0)),
dataType: "html",
contentType: false,
processData: false,
success: saveItemCompleted(data),
error: saveItemFailed
});
}
function saveItemCompleted(data) {
$("#photoForm").html(data);
}
function saveItemFailed(request, status, error) {
}
});
</script>
}
Here is my controller ExampleController:
namespace MvcSamples.Controllers
{
public class ExampleController : Controller
{
IUserDetails usr = new UserDetails();
// GET: /Example/
[HttpGet]
public ActionResult Index()
{
//usr.GetProfilePhoto(WebSecurity.GetUserId(User.Identity.Name));
if (!string.IsNullOrWhiteSpace(User.Identity.Name))
{
ViewBag.UserId = WebSecurity.GetUserId(User.Identity.Name);
}
UserInfoViewModel model = new UserInfoViewModel();
model.GenderList = usr.FillGenderTypesDropDownList();
return View(model);
}
[HttpPost]
public ActionResult SaveProfilePhoto(HttpPostedFileBase photo, UserInfoViewModel model)
{
string path = #"C:\Temp\";
if (photo != null)
{
model.UserId = 1;//WebSecurity.GetUserId(User.Identity.Name);
ViewBag.UserId = model.UserId;
var binary = new byte[photo.ContentLength];
photo.InputStream.Read(binary, 0, photo.ContentLength);
UserPicModel upModel = new UserPicModel();
upModel.UserPhoto = binary;
upModel.UserId = model.UserId;
usr.InsertProfilePhoto(upModel);
}
return PartialView("_UserPhoto", model);
}
public FileResult GetProfileImage(int id)
{
byte[] barrImg = usr.GetProfilePhoto(id);
return File(barrImg, "image/png");
}
}
}
Update:
As #David Tansey suggested ,I added code to refresh image inside SaveCompleted(data).
function RefreshImage() {
$("#imgPhoto").attr('src', function () {
// the datetime portion appended to the url avoids caching issues
// and ensures that a fresh image will be loaded every time
var d = new Date();
return this.src + '?' + d.getTime();
});
}
But the above code is refreshing the image only after I click the upload button twice .
Actually I need this to refresh the image immediately after the $("#btnPhotoUpload").click. Any suggestions?
I also tried disabling cache at the controller but no luck:
[OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)]

I am pretty sure the problem is that the browser is caching the image file and does not 'perceive' the need to bring it across the wire again after you upload a new one.
Look at the following post for a description of how to attach a dummy (yet dynamic) query string value to prevent the caching from occuring. I think this approach will solve your problem.
asp.net mvc jquery filling image
Hope that helps.

Related

Image list not displaying with ajax call in asp.net mvc

My images list not displaying when i call images with ajax call . on page load code work perfectly but on ajax call it did not display any images list my code is.
The view markup is:
<script>
$(document).ready(function () {
$(".yesterdayfilter").click(function () {
var username = $("#users-ddl option:selected").text();
alert(username);
$.ajax({
type: 'Post',
url: "/Home/index",
dataType: 'image/jpg',
data: {
Username: username,
},
success: function (status) {
console.log('Send');
},
error: function () {
console.log('something went wrong - debug it!');
}
});
});
});
</script>
The code where image display in view:
<div class="flex items-center" data-toggle="modal" data-target="#superlarge-modal-size-
preview">
#{
if (Session["recentscreenshotss"] != null)
{
var screenshotslist =
(List<ezilineezitaskernew.Models.sp_getrecentscreenshots_Result>)Session["recentscreenshotss"];
if (screenshotslist != null)
{
foreach (var item in screenshotslist)
{
<div class="col-span-6 sm:col-span-12 xl:col-span-12 mt-8">
<div class="flex-justifiy flex-wrap">
<div class="w-24 h-24 relative image-fit mb-5 mr-5 cursor-pointer zoom-in">
<button value="#item.Image" data-toggle="modal" data-target="#basic-modal-preview" class="testing"><img class="rounded-md" alt="Midone Tailwind HTML Admin Template" src="#item.Image"></button>
<div class="tooltip w-5 h-5 flex items-center justify-center absolute rounded-full text-white bg-theme-6 right-0 top-0 -mr-2 -mt-2 tooltipstered"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x w-4 h-4"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> </div>
</div>
</div>
</div>
}
}
}
}
</div>
The controller:
[HttpPost]
public ActionResult Index(string Username)
{
GetOffices();
SetLogo();
var Userid = dbcontext.USERS.Where(x => x.Username == Username).FirstOrDefault();
int UID = Userid.Id;
int companyid = Convert.ToInt32(Session["company-id"]);
var screenshotlist = dbcontext.sp_getrecentscreenshots(null, null, companyid).ToList();
int s_startId = screenshotlist[0].Id;
int s_lastId = s_startId + 6;
if (screenshotlist != null)
{
var screenshots = dbcontext.sp_getrecentscreenshots(s_startId, s_lastId,
companyid).ToList();
Session["recentscreenshotss"] = screenshots;
}
}
The code is fine . only issue is that it is not displaying images . the same code work when page load but not working with ajax call can anyone help me?
there is different solution to your problem:
solution 1:
On ajax success you can reload page with the Username as parameter
solution 2 :
.empty() and .append your <div> on ajax success with your <div> content
solution 3 :
The cleanest solution:
Put your <div> content in a partial view named for example Searchpartial.cshtml
Add an empty in your current view and give it the an id for example <div id="divSearch"></div>
and load it with your button as follow with GET:
$("#divSearch").empty()
$.get("/Home/Searchpartial", { Username: 'username' },
function (result) {
$("#divSearch").append(result);
}
);
In your controller need to call your partial view as follow:
[HttpGet]
public PartialViewResult Searchpartial(string Username)
{
// your code
return PartialView("Searchpartial");
}
I see that your controller HTTP Post is pointing to Index in your ajax I notice it was index be careful for case sensitive.

Ajax form in partial view validation

I have a form inside a partial view, it works but in case a server side validation error is raised it displays only the partial view. So I decided to use ajax for the submission (actually it makes sense as it is inserting a contact and there's a list in the main view).
Thing is, with the posted code if one of these errors comes it's properly displayed in the view (I'd need to make the partial visible again, but that's another thing), but if there's no error it'll display the list only in the partial view. I can do the other way round, displaying properly when there's no error but then not achieving the proper displaying of validation errors.
I would like to understand what's the best approach, or at least which are the possibilities: maybe change the code in the controller or do some kind of check in the success callback...
I edit what I had before, because when no error I should return the list, not the whole view as I posted before, but anyway I still have the doubt on how to tell one from another as both are succesfull calls to the post action
Thanks
The view is this one
#model ContactListViewModel
#{
ViewBag.Title = " My Contacts"
}
<div id="ContactList">
<h2>My Contacts</h2>
<hr />
<div id="addContainer">
#{ Html.RenderAction("AddContact"); }
</div>
<div id="editContainer" data-amp-url="#Url.Action("Edit", "Contacts")" class="initiallyHidden"></div>
#foreach (var group in Model.Contacts)
{
<div class="PlanContacts">
<div class="PlanName">#group.Key</div>
#foreach (var contact in group.Values)
{
<div class="Preview">
#Html.DisplayFor(m => contact, "Contact")
</div>
}
</div>
}
</div>
#section PageJavascript
{
<script src="~/Scripts/AMPContacts.js"></script>
}
The controller post action
[HttpPost]
public ActionResult AddContact(AddContactViewModel viewModel)
{
var partyId = (int) Session["PartyId"];
if (ModelState.IsValid)
{
_contactsManager.AddContact(viewModel, partyId);
// Here I should return the updated list
}
var newViewModel = _createBuilder.Rebuild(viewModel, partyId);
return PartialView("_AddContact", newViewModel);
}
And the ajax submission code inside the longer AMPContact.js
$('#addForm').submit(function (e) {
e.preventDefault();
var addContainer = $(document.getElementById('addContainer'));
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
addContainer.html(result);
}
});
});
I understand this answer it's far from being good but maybe it's useful for somebody in terms of narrowing a possible search. As it would ended up being huge I rather post this update as an answer to my own question, not sure if it complies with good manners.
The explained one wasn't the only problem I faced, hard times as well with the reset of the form and specially the successive error submissions (once I got an error trying to submit it with errors again), so I ended up making a mess out of different solutions for different problems. Hopefully I will be able to clean it up
Thanks
In the view I use now
<div id="myContacts">
<h2>My Contacts</h2>
<hr />
<div id="addContainer">
<div class="toggler">
Add Contact
</div>
<div id="addToggling" class="initiallyHidden">
#{ Html.RenderAction("AddContact"); }
</div>
</div>
<div id="editContainer" data-amp-url="#Url.Action("Edit", "Contacts")" class="initiallyHidden"></div>
<div id="list">
#{ Html.RenderPartial("_ContactList", Model); }
</div>
In the .js
$('#addContainer').on('submit', '#addForm', ajaxCall);
function ajaxCall(e) {
e.preventDefault();
var addToggling = $(document.getElementById('addToggling'));
var contactList = $(document.getElementById('contactList'));
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.passedValidation == true) {
// Json is returned with flag, we get the list from the server and update the list element
$.get(result.action, function (partial) {
contactList.html(partial);
});
// Add some effects and clear the form
$(document).scrollTop(0);
setTimeout(function () {
addToggling.slideUp(300, resetAddForm);
}, 500);
setTimeout(function () {
contactList.effect("highlight", {}, 3000);
}, 1000);
}
else {
// The form partial view is returned and displayed in the same element when there are validation errors
$(document).scrollTop(0);
addToggling.html(result);
$.validator.unobtrusive.parse('#addForm');
}
}
});
}
function resetAddForm() {
var addForm = $(document.getElementById('addForm'));
// Hhide the error messages
addForm.find("span.field-validation-error").hide();
addForm.find("div.validation-summary-errors").hide();
// Removes the class associated to errors
addForm[0].reset();
// Clear the inputs
addForm.find('input:text, input:password, input:file, select, textarea').val('');
addForm.find('input:radio, input:checkbox').removeAttr('checked').removeAttr('selected');
}
Controller with the existing action method slightly changed and a new one
public ActionResult ContactList()
{
var partyId = (int)Session["PartyId"];
var viewModel = _displayBuilder.Build(partyId);
return PartialView("_ContactList", viewModel);
}
[HttpGet]
public ActionResult AddContact()
{
var partyId = (int) Session["PartyId"];
var viewModel = _createBuilder.Build(partyId);
return PartialView("_AddContact", viewModel);
}
[HttpPost]
public ActionResult AddContact(AddContactViewModel viewModel)
{
var partyId = (int) Session["PartyId"];
if (ModelState.IsValid)
{
_contactsManager.AddContact(viewModel, partyId);
if (Request.IsAjaxRequest())
return Json(new { passedValidation = true, action = Url.Action("ContactList")});
return RedirectToAction("Index");
}
var newViewModel = _createBuilder.Rebuild(viewModel, partyId);
return PartialView("_AddContact", newViewModel);
}

MVC4 - refreshing partial view after deleting object with Ajax.ActionLink acting unexpectedly

I have a DIV (tenant-reference-photos) that holds a partial view that display photos. Beside each photo is a delete button. When it's clicked, I want the photo to be removed from the list and only the DIV to be updated by Ajax.
I'm using an Ajax.ActionLink for the delete action:
<div>
#Ajax.ActionLink("Delete", "DeleteReference",
new { id = photo.ImageId },
new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST",
UpdateTargetId = "tenant-reference-photos" },
new { #class = "btn btn-primary" })
</div>
Controller actions:
[HttpGet]
public ActionResult DeleteReference(int id)
{
return View(refPhotoRepository.Find(id));
}
[HttpPost, ActionName("DeleteReference")]
public ActionResult DeleteReferenceConfirmed(int id)
{
try
{
refPhotoRepository.Delete(id);
refPhotoRepository.Save();
return PartialView("_TenantReferencePhotosPartial");
}
catch (Exception e)
{
// handle exception
}
return View();
}
When I click delete, the action fires and record is deleted from the database. The problem is with return PartialView("_TenantReferencePhotosPartial");. When the action tries to return the partial, I get a NullReferenceException at #if (Model.ReferencePhotos.Count == 0).
_TenantReferencePhotosPartials.cshtml
<div>
#if (Model.ReferencePhotos.Count == 0)
{
<h3>You haven't uploaded any references!
#Ajax.ActionLink("Upload now?",
"TenantUploadReference",
new AjaxOptions
{
UpdateTargetId = "tenant-reference-photos",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
LoadingElementId = "ajax-loader"
})</h3>
}
else
{
foreach (var photo in Model.ReferencePhotos)
{
<ul class="thumbnails">
<li class="span8">
<a href="#" class="thumbnail">
<img src="#Url.Action("GetReference", "Tenants", new { id = photo.ImageId })" alt="#photo.Name") />
</a>
</li>
<li class="span4 btn-group btn-group-vertical">
Edit
#Ajax.ActionLink("Delete", "DeleteReference",
new { id = photo.ImageId },
new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "tenant-reference-photos" },
new { #class = "btn btn-primary" })
</li>
</ul>
}
}
</div>
Even if there are several photos in the collection and one is deleted, the exception is still thrown at the line mentioned previously. Some help would be appreciated.
How do I resolve the above error?
I've had some trouble with jQuery $ not being defined in partials even
though it's there in the source
I believe this is in connection with your problem. When I encounter this on a partial view, what always works for me is to include it in a script section. So on your _Layout.cshtml where you write your scripts you can add this:
// Do this after you referenced jquery
#RenderSection("scripts", required: false)
<script>
// some script references
</script>
Then on your view you do this:
#section scripts {
$(function() {
<script>
function onDeleteReferenceSuccess(data, status, xhr) {
if (data.error) { /* handle error */ }
else {
window.location.reload(true);
}
}
</script>
});
}
Once above is resolved, how do I update the only the DIV?
Since you deleted the item I would assume you would want to delete the div - that represents the item.
If my assumption is correct then why not give it an Id so you can delete it afterwards in your else statement:
else {
// instead of this -> window.location.reload(true);
$("#id_of_the_div").remove();
}
UPDATE:
Your updated question throws off my original answer. But let me include the updated answer here first (and then clean-up afterwards). So the reason you are having a null reference error is because you are not returning the model:
refPhotoRepository.Delete(id);
refPhotoRepository.Save();
// build the model here and pass it to the partial view
// this will most probably involve querying your data store again
var model = goBuildTheModel();
return PartialView("_TenantReferencePhotosPartial", model;

Post a form with ajax and display error in a div in case of error

In the code below, I have a form with some informations about an employee. I'd like feel in the password and post. If the password is correct the user is redirected to /Dashboard that it's ok.
When the password is wrong, I'd like show the error (_ErrorPartial) in MyError div and still see the form. I'd like see the form and the message in the div (MyError). With the code below, when the password is wrong, I see the error message but on a blank page and not a page with the layout + the form + the error message
Any idea ?
Thanks,
My page :
#model MyApp.EmployeeModel
<script type="text/javascript">
$(document).ready(function () {
$(".").click(function () {
$.ajax({
url: '/Account/Login',
type: "Post",
data: $('#MyForm').serialize(),
success: function (result) {
alert('Im here');
$("#MyError").html(result);
//Here is code to detect error or not but removed
}
});
});
});
</script>
<div id="MyError"></div>
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "MyForm" }))
{
#Html.AntiForgeryToken()
<div>
#Html.LabelFor( m => m.FirstName)
<div>
#Html.HiddenFor(model => model.Username)
#Html.LabelFor( m => m.Password)
#Html.PasswordFor(m => m.Password)
<input type="submit" class="jqClick" />
</div>
</div>
}
Controller :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(EmployeeModel employeeModel)
{
var Errors = new List<string>();
var employee = .... //Get employee from DB
if (employee == null)
{
Errors.Add("Employee trouble");
return PartialView("_ErrorPartial", Errors.AsEnumerable());
}
return RedirectToAction("Index", "Dashboard");
}
My _ErrorPartial view :
#model IEnumerable<string>
<div id="DisplayError">
#{ var error = string.Empty; }
#foreach (var err in Model){
error += err + "<br>";
}
#Html.Raw(error)
</div>
You have to make some changes to your javascript code. First of all, replace jQuery selector $(".") with specific one, like $(".jqClick"). Second of all, never hardcode urls like /Account/Login in your code, use UrlHelper for that. And the third thing is, you have to prevent a default behavior of the submit button. See the changes:
$(document).ready(function () {
$(".jqClick").click(function (e) {
e.preventDefault();
$.ajax({
url: '#Url.Action("Login", "Account")',
type: "Post",
data: $('#MyForm').serialize(),
success: function (result) {
alert('Im here');
if (result.redirectUrl)
window.location.href = result.redirectUrl;
else
$("#MyError").html(result);
//Here is code to detect error or not but removed
}
});
});
});
Also you need to change the Login action a little in order to make a redirect if a login is success:
public ActionResult Login(EmployeeModel employeeModel)
{
var Errors = new List<string>();
var employee = .... //Get employee from DB
if (employee == null)
{
Errors.Add("Employee trouble");
return PartialView("_ErrorPartial", Errors.AsEnumerable());
}
return Json(new { redirectUrl = Url.Action("Index", "Dashboard") });
}
The probable error that you throw from the server side, won't appear in success function. Instead you should have below
error:function(response)
{
//your code here
}
Simply throw exception from the controller.

What should the MVC ActionResult return type be for an external HTML ajaxForm post

The idee is to post form data from a normal external Html page to another MVC site controller. Then the data is processed almost like using a webservice.
$(document).ready(function () {
var options = {
target: '#output',
success: function(data){ alert('test success'); },
url: http://localhost:57232/Services/SendFormData,
dataType: json
};
$('form').ajaxForm(options);
});
The ActionResult receives the data correctly in the FormCollection object.
[HttpPost]
public ActionResult SendFormData(FormCollection collection)
{
string s = string.Empty;
return Json(new { Success = true, Message = "Message!" }, JsonRequestBehavior.AllowGet);
}
At this point the success result is returned but when it gets to the external form my browser which is in this case IE tries to save or open the bytes returned instead of calling the success callback function.
Because this page is an external page, and not part of the MVC site I cannot use a View or Partial View. What should the return type be?
You need to return partialview result :
[HttpPost]
public ActionResult Form(Comment feedback)
{
if (feedback != null)
{
feedback.CommentedOn = DateTime.Now;
feedback.CommentId += 1;
if (ModelState.IsValid)
{
BlogPost blogpost = db.BlogPosts.Find(feedback.BlogId);
if (blogpost != null)
blogpost.NoofComments += 1;
db.Entry(blogpost).State = EntityState.Modified;
db.Entry(feedback).State = EntityState.Modified;
db.Comments.Add(feedback);
db.SaveChanges();
return PartialView("CommentSuccess", feedback);
}
}
return PartialView("Comment", feedback);
}
Also in the AjaxForm you need to set the UpdateTargetID:
#using (Ajax.BeginForm("Form", new AjaxOptions() { UpdateTargetId = "FormContainerdiv" , OnSuccess = "$.validator.unobtrusive.parse('form');", OnComplete = "OnComplete();" }))
in the targetId of the Ajax Form you need to mention the div id where you have to display the response data.
<div id="FormContainerdiv">.</div>
#Html.Partial("Comment", item);
</div>

Categories

Resources