Delay ajax OnBegin until fadeOut of DOM - MVC5 - ajax

I am trying to fade out a div containing html from a partial view when a user clicks on a link and fade in the newly fetched partial view. The problem I am having is that sometimes my view is fetched before the fadeout of the original partial view is finished so I end up seeing the partial views switch out, fade out, then fade back in again. Is there a way to delay the ajax request until the fade out is complete?
Here is the order of operations I am trying to achieve.
User clicks link > partial view A fades out > new partial view is fetched via ajax > partial view B fades in.
This is what is happening at times
User clicks link > partial view A begins to fade out but partial view B comes in before it finishes fading > partial view B fades out > partial view B fades back in again.
#Ajax.ActionLink("Me", "ManageUserAccount", null, new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "ajax-update",
InsertionMode = InsertionMode.Replace,
OnBegin = "ajaxBegin",
OnSuccess = "ajaxSuccess"
}, new { #class = "active" })
#Ajax.ActionLink("Alerts", "ManageAlerts", null, new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "ajax-update",
InsertionMode = InsertionMode.Replace,
OnBegin = "ajaxBegin",
OnSuccess = "ajaxSuccess"
}, new { #class = "active" })
<div id="ajax-update">
#Html.Action("ManageUserAccount")
</div>
<script type="text/javascript">
function ajaxSuccess() {
$('#ajax-update').fadeIn();
//sometimes the new partial view is returned before this even finishes its job
}
function ajaxBegin() {
$('#ajax-update').fadeOut();
}

i make a very similar function... but i use get, done and effect. and works! I use to make a complex website without postback, change partial is in the menu for simulate the change page
$(".menu-button").click(function (e) {
var id = e.target.id;
var urlID = document.URL.split('#')[1];
if (id != urlID) {
location.hash = id;
changepartial(id);
}
});
function changepartial(id)
{
var path = "../home/"+id;
getOut();
$.get(path).done(function (result) {
//done is for wait get result after make the next step.
$("#Contenant").html(result);
getIn();
});
}
function getOut() {
$("#Contenant").effect('drop', { direction: 'left' }, 100);
}
function getIn() {
$("#Contenant").effect('slide', { direction: 'right' }, 1000);
}

Related

mvc5 ajax not refreshing Partial View

I am having a View which contains a textbox, a button and a virtual book in a partial view. After Pressing the button, the text from the textbox is placed on the first page of the book and then the book (the partial view) should be refreshed.
With the Ajax I can call the method PlaceText (which place a text on the first page/image of the book) but it doesn't refresh the partial view _Book.
I tried
$("#services").load("_Book");
and
$("#services").load("PlaceText");
and many more things after reading these solutions for a similar problem, but nothing seems to work. The allert("ok") appears, but the partialView doesn't refresh
<script type ="text/javascript">
$(document).ready(function(){
$("#myLink").click(function (e) {
e.preventDefault();
var textboxvalue = $('input[name=your-name]').val();
$.ajax({
data: { name: textboxvalue },
type: 'POST',
url: '#Url.Action("PlaceText", "Home")',
success: function (result) {
$("#services").html("_Book");
alert("ok");
}
});
});
});
</script>
#Html.ActionLink("Mehet", "PlaceText", null, new { id = "myLink" })
This is my Controller:
[HttpPost]
[OutputCache(Duration = 0)]
public ActionResult PlaceText(string name)
{
PointF firstLocation = new PointF(700f, 80f);
string imageFilePath = Server.MapPath(#"~/images/demo1/elolap3.jpg");
string imageFilePath2 = Server.MapPath(#"~/images/demo1/elolap1.jpg");
Bitmap bitmap = (Bitmap)Image.FromFile(imageFilePath);//load the image file
using (Graphics graphics = Graphics.FromImage(bitmap))
{
using (Font arialFont = new Font("Bigfish", 40))
{
graphics.DrawString(name, arialFont, Brushes.Blue, firstLocation);
}
}
bitmap.Save(imageFilePath2);//save the image file
return PartialView("_Book", db.imageModel.ToList());
}
This is my view (Index):
<section id="services" class="services service-section">
#Html.Partial("_Book");
</section>
My partial view:
<div class="Heidelberg-Book with-Spreads" id="Heidelberg-example-1">
#foreach (var image in Model)
{
<div class="Heidelberg-Spread">
<img src="#Url.Content(image.FilePath)" />
</div>
}
</div>

how to load a partial view inside an anchor tag which has been generated via Ajax

I have a form with a dropdownlist. When selecting an option, I make an ajax call to dynamically add a list of links in the view. When I click on one of the links, I want to update the existing page with a partial view returned by the PostListSubCategory() method.
Currently, clicking on one of the links does a redirect and shows the partial view in a new page. How can I update the the existing page?
<script language="javascript" type="text/javascript">
function GetSubCategory(_categoryId) {
var procemessage = "<a='0'> Please wait...</a>";
$("#SubCategoryID").html(procemessage).show();
var url = "/Posts/GetSubCategoryById/";
$.ajax({
url: url,
data: { categoryid: _categoryId },
cache: false,
type: "POST",
success: function (data) {
var markup = "";
for (var x = 0; x < data.length; x++) {
var num = data[x].Text;
markup += "<a href='/posts/postlistsubcategory?subcategoryid=" + data[x].Text + "'>" + data[x].Text + "</a><br />";
// markup += "<a href=" + Url.Action("postlistsubcategory", new { subcategoryid = num });
}
$("#SubCategoryID").html(markup).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
$.ajax({
url: "/Posts/PostListCategory",
data: { categoryid: _categoryId },
cache: false,
type: "POST",
success: function (data) {
$("#postList").html(data).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
</script>
#using (Html.BeginForm())
{
#Html.ListBoxFor(m => m.CategoryModel, new SelectList(Model.CategoryModel, "CategoryId", "Name"), new { #id = "ddlcategory", #style = "width:200px;", #onchange = "javascript:GetSubCategory(this.value);" })
<br />
<br />
<div id="SubCategoryID" name="SubCategoryID" style="width: 200px"></div>
<br /><br />
}
In the controller
public PartialViewResult PostListSubCategory(string subcategoryid)
{
if (subcategoryid == null)
{
return PartialView(db.Posts.ToList());
}
return PartialView("PostList", db.Posts.Include(i => i.SubCategory).Where(p => p.SubCategory.Name == subcategoryid));
}
You currently dyamically generating links with an href attribute so clicking on them will do a redirect. You need to handle the click event of those links using event delegation and then use ajax to update the existing DOM. There a some other bad practices in your code and I suggest you use the following
#using (Html.BeginForm())
{
// no need to override the id attribute and use Unobtrusive Javascript (don't pollute markup with behavior)
#Html.ListBoxFor(m => m.CategoryModel, new SelectList(Model.CategoryModel,"CategoryId", "Name"))
}
<div id="SubCategoryID"></div> // no point adding a name attribute
<div id="postList"></div>
var subcategories = $('#SubCategoryID');
$('#CategoryModel').change(function() {
var url = '#Url.Action("GetSubCategoryById", "Posts")'; // don't hard code url's
var category = $(this).val();
subcategories.empty(); // clear any existing links
$.post(url, { categoryid: category }, function(data) { // this could be a GET?
$.each(data, function(index, item) {
subcategories.append($('<a></a>').text(item).attr('href','#').addClass('subcategory')); // see note below
});
});
});
Note: Since your ajax only needs one property to generate the links (the value to display in the link), then your GetSubCategoryById() should be returning IEnumerable<string> not a collection of complex objects (you current code suggest your returning other data which you never use). If you do need to return a collection of objects, then change the above to use .text(item.Text). The above code will generate
.....
for each item you return. Then add an additional script to handle the .click() event of the links (since the links are dynamically added, you need event delegation using the .on() method)
var posts = $('#postList');
$('#SubCategoryID').on('click', '.subcategory', function() {
var url = '#Url.Action("postlistsubcategory", "posts")';
var subcategory = $(this).text();
posts.load(url, { subcategoryid: subcategory });
});

partial view loaded by Ajax do not render code after #RenderBody

I have an application developped in C# / MVC4.
It has a top menu and bottom menu.
A partial view is loaded in the main view when I click on a link (banana or apple) using ajax:
#Ajax.ActionLink("Connection", "Details", "SourceConfiguration", new { id = "4505F2DE-91A2-496B-9BCB-BD1D3C2C3FB1" }, new AjaxOptions { UpdateTargetId = "result" })
When the link is clicked, it should also modify the layout in order to display different top and bottom menus: (similar to Windows Azure bottom menu which display contextual action depending on where you are).
How I can I achieve that? (see below for what has been tested).
What has been tried so far:
The default _layout.cshtml contains
#RenderBody()
#RenderSection("Bottom", false)
the home/index.cshtml contains this code:
#section Bottom
{
the code to create the bottom menu
[...]
}
=> This is correctly rendered.
Here comes the problem:
each page in the views contain the Bottom section.
The bottom section is not displayed in pages called by partial view (ex: views/apple/index.cshtml).
What's the best way when I click on Apple to display the partial view and to display a specific top and bottom bar?
Got it:
in HomeController:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MenuLayout(string name)
{
return PartialView("_MenuLayout", null);
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MenuBottomLayout(string name)
{
return PartialView("_MenuBottomLayout", null);
}
In _Layout.cshtml
<div class="navcontainer">
</div>
<div class="">
#RenderBody()
</div>
<div class="navcontainerbottom">
</div>
And javascript code:
<script type="text/javascript">
var menuLoaded = false;
$(document).ready(function () {
if($('.navcontainer')[0].innerHTML.trim() == "")
{
$.ajax({
url: "#Url.Content("~/Home/MenuLayout")",
type: "GET",
success: function (response, status, xhr)
{
var nvContainer = $('.navcontainer');
nvContainer.html(response);
menuLoaded = true;
},
error: function (XMLHttpRequest, textStatus, errorThrown)
{
var nvContainer = $('.navcontainer');
nvContainer.html(errorThrown);
}
});
}
if($('.navcontainerbottom')[0].innerHTML.trim() == "")
{
$.ajax({
url: "#Url.Content("~/Home/MenuBottomLayout")",
type: "GET",
success: function (response, status, xhr)
{
var nvContainer = $('.navcontainerbottom');
nvContainer.html(response);
menuLoaded = true;
},
error: function (XMLHttpRequest, textStatus, errorThrown)
{
var nvContainer = $('.navcontainerbottom');
nvContainer.html(errorThrown);
}
});
}
});
</script>
And finally _MenuLayout.cshtml and MenuBottomLayout.cshtml contain the code that create the top and bottom menu.

Get a reference to the anchor element of an Ajax.ActionLink at the OnSuccess handler

Basically my question is similar or even a duplicate of this one, except that I'm using MVC Razor. And I'm certain that the answers there are outdated since the client library currently used is jQuery / unobtrusive ajax.
So to sum up the question, I'm trying to access the anchor element that triggered the Ajax request in the handler specified at the OnSuccess property of the provided AjaxOptions.
Here is the ActionLink:
#Ajax.ActionLink("Add opening times entry", "AddOpeningTimes",
new { htmlPrefix = Html.HtmlPrefixFor(m => Model.OpeningTimes) },
new AjaxOptions { UpdateTargetId = "openingTimes",
InsertionMode = nsertionMode.InsertAfter,
OnSuccess = "updateHtmlPrefix" },
new { title = "Add opening times entry" })
JS:
function updateHtmlPrefix() {
this.href = this.href.replace(/\d(?=]$)/, function (i) { return ++i; });
}
here is a link to an answer that shows several solutions and a good mark explanation of the issue.
https://stackoverflow.com/a/1068946/1563373
you could always just write
OnBegin="function() { clickedLink = $(this); }"
You can then access the clickedLink variable in the success handler (remember to declare it with page scope).
EDIT:
After some playing around with the call stack, you could try something like this:
<script type="text/javascript">
function start(xhr) {
var stack = start.caller;
// walk the stack
do {
stack = stack.caller;
} while (stack.arguments != undefined && stack.arguments.length > 0 && (stack.arguments[0].tagName == undefined || stack.arguments[0].tagName != "A"))
//stack now points to the entry point into unobtrusive.ajax
if (stack.arguments != undefined)
xhr.originalElement = $(stack.arguments[0]);
//blech
}
function UpdateHrefText(result, status, xhr) {
debugger;
if(xhr.originalElement != undefined)
xhr.originalElement.text(result.Message);
}
</script>
#Ajax.ActionLink("Test", "Message", "Home", new AjaxOptions{ OnBegin = "start", OnSuccess = "UpdateHrefText"})
Not sure I would trust this in production though. I'd do something more like:
<script type="text/javascript">
var theLink;
function start(xhr) {
xhr.originalElement = theLink;
}
function UpdateHrefText(result, status, xhr) {
debugger;
if(xhr.originalElement != undefined)
xhr.originalElement.text(result.Message);
}
</script>
#Ajax.ActionLink("Test", "Message", "Home", null, new AjaxOptions{ OnBegin = "start", OnSuccess = "UpdateHrefText"}, new { onclick="theLink = $(this);"})

How do I process drop down list change event asynchronously?

I have a drop down list that I need to react to asynchronously. I cannot get the Ajax.BeginForm to actually do an asynchronous postback, it only does a full postback.
using (Ajax.BeginForm("EditStatus", new AjaxOptions { UpdateTargetId = "divSuccess"}))
{%>
<%=Html.DropDownList(
"ddlStatus",
Model.PartStatusList.OrderBy(wc => wc.SortOrder).Select(
wc => new SelectListItem
{
Text = wc.StatusDescription,
Value = wc.PartStatusId.ToString(),
Selected = wc.PartStatusId == Model.PartStatusId
}),
new { #class = "input-box", onchange = "this.form.submit();" }
)%>
<div id='divSuccess'></div>
<%
}
When the user selects an item from the list, it does a full postback and the controller method's return value is the only output on the screen. I am expecting the controller method's return value to be displayed in "divSuccess".
[AjaxAwareAuthorize(Roles = "Supplier_Administrator, Supplier_User")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditStatus(PartPropertiesViewModel partPropertiesViewModel)
{
var part = _repository.GetPart(partPropertiesViewModel.PartId);
part.PartStatusId = Convert.ToInt32(Request.Form["ddlStatus"]);
_repository.SavePart(part);
return Content("Successfully Updated Status.");
}
How about doing this the proper way using jQuery unobtrusively and getting rid of those Ajax.* helpers?
The first step is to use real view models and avoid the tag soup in your views. Views should not order data or whatever. It's not their responsibility. Views are there to display data that is being sent to them under the form of a view model from the controller. When I see this OrderBy in your view it's just making me sick. So define a clean view model and do the ordering in your controller so that in your view you simply have:
<% using (Html.BeginForm("EditStatus", "SomeControllerName", FormMethod.Post, new { id = "myForm" }) { %>
<%= Html.DropDownListFor(
x => x.Status,
Model.OrderedStatuses,
new {
#class = "input-box",
id = "myDDL"
}
) %>
<% } %>
<div id="divSuccess"></div>
and finally in a completely separate javascript file subscribe for the change event od this DDL and update the div:
$(function() {
$('#myDDL').change(function() {
var url = $('#myForm').attr('action');
var status = $(this).val();
$.post(url, { ddlStatus: status }, function(result) {
$('#divSuccess').html(result);
});
});
});
This assumes that in your controller action you would read the current status like this:
[AjaxAwareAuthorize(Roles = "Supplier_Administrator, Supplier_User")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditStatus(PartPropertiesViewModel partPropertiesViewModel, string ddlStatus)
{
var part = _repository.GetPart(partPropertiesViewModel.PartId);
part.PartStatusId = Convert.ToInt32(ddlStatus);
_repository.SavePart(part);
return Content("Successfully Updated Status.");
}
And when you see this you might ask yourself whether you really need a form in this case and what's its purpose where you could simply have a dropdownlist:
<%= Html.DropDownListFor(
x => x.Status,
Model.OrderedStatuses,
new Dictionary<string, object> {
{ "class", "input-box" },
{ "data-url", Url.Action("EditStatus", "SomeControllerName") },
{ "id", "myDDL" }
}
) %>
<div id="divSuccess"></div>
and then simply:
$(function() {
$('#myDDL').change(function() {
var url = $(this).data('url');
var status = $(this).val();
$.post(url, { ddlStatus: status }, function(result) {
$('#divSuccess').html(result);
});
});
});

Resources