I have a function inside my .aspx.cs code which takes wuite a long time to do the processing until when I want to display a cool loading animation. I looked some of the earlier posts but either these didn't work for me, or were having solution specific to Page loading scenario (not loading a while a function completes).
I guess the right approach would be to fire a Javascript startLoader() function just before the the main function starts (which takes a long time), and then call a stopLoader() from the .aspx.cs itself to stop the loader when the function ends. Any suggestions how to implement this?
Yes, I've done this in ASP.NET Web From (not a ASP.NET MVC solution). You need to provide OnSubmit client side event handler. It basically break down to three parts: Javascript, HTML Div, and one line code behind:
Javscript:
function ShowLoading(e) {
// var divBg = document.createElement('div');
var divBg = document.getElementById('blockScreen');
var divLoad = document.createElement('div');
var img = document.createElement('img');
img.src = 'images/ajax-loader.gif';
divLoad.setAttribute("class", "blockScreenLoader");
divLoad.appendChild(img);
divBg.appendChild(divLoad);
document.getElementById('blockScreen').style.display = 'block';
// These 2 lines cancel form submission, so only use if needed.
//window.event.cancelBubble= true;
//e.stopPropagation();
}
function HideLoading() {
//alert('hideloading');
document.getElementById("form1").onsubmit = null;
document.getElementById('blockScreen').style.display = 'none';
//alert('done');
}
Add following DIV
<div id="blockScreen" class="blockScreen" style="display:none"> </div>
Finally, add the following to Page_Load in code behind.
Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "submit", "ShowLoading()");
Now, all of your page postbacks are essentially have to call onsubmit event. It will display the animation before the page postback finishes.
if you really want to do, then the only way is webworkers. You've probably heard about them, or if not, i seriously recommend to have a look.
Yes, fire startLoader() on OnCliencClick of your button or whatever element you are using to fire the server-side event and call stopLoader() from the server-side at the end of your process. Something like this:
//rest of the server-side code above ...
Page.ClientScript.RegisterStartupScript(this.GetType(), "someKey", "stopLoader();", true);
If you don't mind that the browser is not responsive in the meantime, the simplest way of doing this is using an animated gif:
Activity indicators
ajaxload.info
webscriptlab
The trick is showing the image when starting your processing, and hiding it when finished. You can show it in an img, and use jQuery or whatever you want to show/hide it.
If you need the browser to keep responsive, use Web Workers. But be aware that some of the older browsers don't support it. See this reference
Related
I wonder if there is way to run JS code whenever a asp.net page is contacting the server.
I.e. I'm looking for a general event handler that is triggered whenever a call to the server is made for runat="server" components.
I know there's a way to get jQuery.ajax() to run JS code before and after a call to the server is made, but I'm not sure how to do it in asp.net. Especially, since this projects uses a lot of custom components.
The goal here is to show some kind of loading image when a button is clicked, to prevent the user to click a button twice and also to show the user that the system is working.
If the click causes the page or an updatepanel to refresh, I'd only like to display the loading image before the refresh, eg. User clicks, "loading" is shown, page/update panel is refreshed (causing the "loading" to disappear), the new page/content is displayed as normal.
Update 1:
I can't use UpdateProgress because some of the buttons aren't inside UpdatePanels. What I really want to do is to fire a JS as soon as any button/control that will contact the server is clicked. I.e. if the user clicks a dropdown which doesn't contact the server, nothing should happend. But if that dropdown has any connection to the server, a JS should be run.
I understand that this is ASP.NET Ajax and not jQuery Ajax, I only used jQuery as an example. Because I've used a jQuery method before (with jQuery Ajax) to trigger JS before the server call was made. Eg. to lock the element that was clicked or to display a "Loading..." screen.
I could of course add a bit of a JS hack to every page which adds a "onclick" event to every button manually, but I thought it would be better if there was a general solution for this (since I've got lots of pages, each with a few buttons that contact the server on them).
Update 2:
When I think about it, it doesn't necessarily need to be a JS that is triggered. It would be good enough if the page somehow only made sure that the same button wasn't clicked twice. Either by disabeling the button or by adding something in front of it (like a "Loading..." screen).
You can use an UpdateProgress for this to use with update panels, but if you are doing a full page postback (i.e. not ajax) the only loading animation you can have is the browsers own.
UpdateProgress:
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
Shiny loading aninmation
</ProgressTemplate>
</asp:UpdateProgress?
Here is how you can do it using jquery:
$(function() {
$('a[href]').on('click', function(e) {
var self = this;
e.preventDefault();
loadAsync($(self).attr('href'));
});
});
function loadAsync(url) {
$('div#result').fadeOut(200, function() {
$('div#loader').fadeIn(200, function() {
$.get(url, function(data) {
$('div#result').html($(data));
}).done(function() {
console.log('done');
}).fail(function() {
$('div#result').html('Error');
}).always(function() {
$('div#loader').fadeOut(200, function() {
$('div#result').fadeIn(200);
});
});
});
});
}
OK, this feels like a question that should be easy to answer, but as with so much mixing of asp.net and jQuery, it's a bit of a nightmare.
What I want to do is be fading a div in and out at various times during the client's viewing of my asp.net page; I fade it out using jQuery's fadeTo() before triggering an UpdatePanel update (to indicate that the data there is not fresh) and I want to fade it in again once the UpdatePanel has been updated. I've gotten as far as updating the UpdatePanel in the codebehind, and this results in the div's content changing... but how do I fade the div back in again?
The way I see it, there are 2 ways; register an event handler on page load to detect when the div's content has been changed and fade it back in, or call a function from the asp.net codebehind when I've updated the div to fade it back in.
In the first case, there doesn't seem to be any event triggered by the div's content changing, so I can't seem to do that. If anyone knows how I could get a client event to trigger when I update the div, that would be a nice solution.
In the second case, I thought that this was what ClientScriptManager was for, but it doesn't quite do what I want. I want to be able to register a particular Javascript function with ClientScriptManager and then tell ClientScriptManager to execute it in the client, from my codebehind. You can't seem to do that. ClientScriptManager.RegisterClientScriptBlock() simply inserts some <script> into the HTML output, rather than calling a function. Presumably this would work if I kept registering things like:
<script>fadeBackIn();</script>
because the Javascript would be immediately evaluated and run, but this feels messy, and that feeling is intensified by the fact that I'd have to keep randomly generating a new unique value for the key argument of ClientScriptManager.RegisterClientScriptBlock(), because it's specifically designed to stop this kind of repeated code. However, I'm struggling to see an alternative. Can anyone come up with a good idea as to a better mechanism to get this to work?
I'd really like something along the lines of:
ClientScriptManager.RegisterClientScriptFunction("fadeBackIn", "function fadeBackIn(){ ... }");
[...]
ClientScriptManager.ExecuteClientScriptFunction("fadeBackIn");
but I don't see that functionality available. :-(
You can attach to the page loaded event. The pageLoaded handler recieves an argument of type PageLoadedEventArgs which contains a get_panelsUpdated method that you can call to enumerate all the UpdatePanels whose content was just updated.
Example:
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(pageLoaded);
function pageLoaded(sender, args)
{
var panels = args.get_panelsUpdated();
if (panels.length > 0)
{
for (var i in panels)
{
if (panels[i].id == "DesiredUpdatePanelId")
{
fadeBackIn();
}
}
}
}
Because html is stateless, you'll need an asynchronous way of determining if the content has changed in your div. I would try using the window.setInterval to check if your content has changed every 500ms or so.
If your div looks like this:
<div id="mycontent">Blah, blah.</div>
Then this might be a solution:
$(function(){
var $mycontent = $('#mycontent');
var myContentHtml = $mycontent.html();
var intervalId;
function checkIfDivChanged()
{
window.clearInterval(intervalId);
if (myContentHtml != $mycontent.html())
{
myContentHtml = $mycontent.html();
// run update here..
fadeBackIn();
// if you want to stop the check then uncomment next line..
// return;
}
intervalId = window.setInterval(checkIfDivChanged, 500);
}
checkIfDivChanged();
});
If you are using, or are willing to use the ASP.NET Ajax Control Toolkit, it contains a control that does exactly what you are looking for: UpdatePanelAnimation.
Take a look here for more information on that control, and here for more information on using animations.
I have written small script with pure JavaScript (no jQuery required), you can just have this script in your page and change the alert part.. plus of course the ID of the div element.
<script type="text/javascript">
var _watchTimer = 0;
window.onload = function WindowLoad(evt) {
_watchTimer = window.setInterval("WatchForChange();", 100);
}
function WatchForChange()
{
var oDiv = document.getElementById("MyDivToWatch");
if (oDiv == null)
{
window.clearInterval(_watchTimer);
return;
}
var prevHtml = oDiv.getAttribute("prev_html") || "";
var curHtml = oDiv.innerHTML;
if (prevHtml != curHtml)
{
if (prevHtml.length > 0)
{
alert("content changed");
}
else
{
//first run, ignore
}
oDiv.setAttribute("prev_html", curHtml);
}
}
</script>
<div id="MyDivToWatch">I can be changed..</div>
<button type="button" onclick="document.getElementById('MyDivToWatch').innerHTML += ' like this..';">Change</button>
Added the whole code used to test, used a button to dynamically change the contents but it should work with changing it via AJAX call aka UpdatePanel.
I have a page that performs a long-running task (10 to 15 seconds) in the page_load method.
I have client-side javascript code that will display a decent "page loading" animated gif to the user.
I am able to invoke the JavaScript method from the code-behind, to display the "page loading" animated gif, however, the long-running task is hanging up the UI such that the animated gif doesn't actually display until after the long-running task is complete, which is the exact opposite of what I want.
To test this out, in my page_load method I make a call to the JavaScript method to display the animated gif. Then, I use Thread.Sleep(10000). What happens is that the animated gif doesn't display until after Thread.Sleep is complete.
Obviously I am doing something incorrect.
Any advice would be appreciated.
Thanks.
Chris
Below is an example of the code-behind:
protected void Page_Load(object sender, EventArgs e)
{
ClientScript.RegisterStartupScript
(GetType(), "Javascript", "javascript: ShowWaitIndicator(); ", true);
Response.Flush();
Thread.Sleep(10000);
}
I accomplished this by placing a timer on the page. After its first tick disable it and run your task.
<asp:Timer runat="server" id="UpdateTimer" interval="500" ontick="UpdateTimer_Tick" />
I placed this within an UpdatePanel for my needs. I'm not sure what will work best for you. Then in your code behind...
Protected Sub UpdateTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
UpdateTimer.Enabled = False
' run method here
End Sub
The reason is that the Page.Load event fires before any response has been sent to the client; so any instructions for the client (such as executing your javascript) doesn't occur until the client receives the response.
So... placing the long-running task in Page.Load won't have the effect you want. This sounds like a case for using AJAX or some other form of asynchronous data-retrieval. In this scenario the page you return to the client doesn't containt he result of your long-running task--so the page itself (with it's spinner) loads quickly, then the client requests the data; when the data is ready (10-15 seconds later) you can update the DOM in the client with the results.
Following example will work for you. just place in Page Load Event.
Response.Write("<div id=\"loading\" style=\"position:absolute; width:100%; text-align:center; top:300px;\"><img src=\"http://www.cse.iitk.ac.in/users/singhroh/images/loading.gif\" border=0></div>");
Response.Flush();
System.Threading.Thread.Sleep(5000);
Response.Write("script>document.getElementById('loading').style.display='none';</script>");
I would avoid invoking your JavaScript from the code-behind.
Instead, utilize the jQuery library. You can trigger your code to be called immediately after the DOM is loaded by using the following code:
$(document).ready(function() {
//Call your JavaScript method here.
});
You'll also need to add jQuery to your page, which is a single script include from the Microsoft CDN. Add this to your markup to do this:
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
This will call your GIF JavaScript method immediately when the page is loaded and you can eliminate your Thread.Sleep. I'm assuming your animated GIF method automatically hides the image after 10-15 seconds.
Taking it old school, you could switch buffering off. With buffering on (the default), the page is generated in it's entirity before it's sent to the client. If you switch buffering off, it's sent as it's loaded, so you can send the html to switch the loading graphic on, do your long running task, then send some javascript to switch it off.
It sounds like the Javascript onload is being displayed after the ASP onload processes. You may want to delay your time-intensive action to Page.LoadComplete - this way, your user will see the Javascript render before the intensive operation occurs.
Then, you would update the page contents as needed after the function completes.
Can you provide an example of the code?
I am wanting to use the Facebox plugin for JQuery but am having a few issues getting it running how I want. The div that houses the facebox content is created outside of the tag so even though I am loading up some web controls none of them are firing back to the server.
Has anyone dealt with this that can give me some pointers?
poking around the facebox.js I came across this line in the function init(settings)...
$('body').append($.facebox.settings.faceboxHtml)
I changed that to ...
$('#aspnetForm').append($.facebox.settings.faceboxHtml)
and it loads up in the form tag, not sure yet if there are any side effects
You can use this code to register the PostBack event:
btn.OnClientClick = string.Format("{0}; $.facebox.close();",ClientScript.GetPostBackEventReference(btn, null));
this will let the button fires a PostBack.
Even after the :
$('#aspnetForm').append($.facebox.settings.faceboxHtml)
change I found it problematic. When you look at the page source using firebug you see that all the html in the div assigned to be the facebox div is doubled up (repeated).
So all of those controls with supposed unique id's are doubled up on the page, that can't be good on the postback, i've decided putting asp.net web controls in a facebox is not a good idea.
I modified facbox.js to do this. Maybe there is a better solution but this works like a charm
Here what i did:
add two lines on top of facbox.js before '(function($)'
var willremove = '';
var willremovehtml = '';
find "reveal: function(data, klass) {" and add this lines before the first line of function.
willremove = data.attr('id')
willremovehtml = $('#'+willremove).html()
$('#'+willremove).html('')
find "close: function() {" and make it look like below.
close: function() {
$(document).trigger('close.facebox')
$('#'+willremove).html(willremovehtml)
willremovehtml = ''
willremove = ''
return false
}
I have just started working with the AnimationExtender. I am using it to show a new div with a list gathered from a database when a button is pressed. The problem is the button needs to do a postback to get this list as I don't want to make the call to the database unless it's needed. The postback however stops the animation mid flow and resets it. The button is within an update panel.
Ideally I would want the animation to start once the postback is complete and the list has been gathered. I have looked into using the ScriptManager to detect when the postback is complete and have made some progress. I have added two javascript methods to the page.
function linkPostback() {
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(playAnimation)
}
function playAnimation() {
var onclkBehavior = $find("ctl00_btnOpenList").get_OnClickBehavior().get_animation();
onclkBehavior.play();
}
And I’ve changed the btnOpenList.OnClientClick=”linkPostback();”
This almost solves the problem. I’m still get some animation stutter. The animation starts to play before the postback and then plays properly after postback. Using the onclkBehavior.pause() has no effect. I can get around this by setting the AnimationExtender.Enabled = false and setting it to true in the buttons postback event. This however works only once as now the AnimationExtender is enabled again. I have also tried disabling the AnimationExtender via javascript but this has no effect.
Is there a way of playing the animations only via javascript calls? I need to decouple the automatic link to the
buttons click event so I can control when the animation is fired.
Hope that makes sense.
Thanks
DG
The flow you are seeing is something like this:
Click on button
AnimationExtender catches action and call clickOn callback
linkPostback starts asynchronous request for page and then returns flow to AnimationExtender
Animation begins
pageRequest returns and calls playAnimation, which starts the animation again
I think there are at least two ways around this issue. It seems you have almost all the javascript you need, you just need to work around AnimationExtender starting the animation on a click.
Option 1: Hide the AnimationExtender button and add a new button of your own that plays the animation. This should be as simple as setting the AE button's style to "display: none;" and having your own button call linkPostback().
Option 2: Re-disable the Animation Extender once the animation has finished with. This should work, as long as the playAnimation call is blocking, which it probably is:
function linkPostback() {
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(playAnimation)
}
function playAnimation() {
AnimationExtender.Enabled = true;
var onclkBehavior = $find("ctl00_btnOpenList").get_OnClickBehavior().get_animation();
onclkBehavior.play();
AnimationExtender.Enabled = false;
}
As an aside, it seems your general approach may face issues if there is a delay in receiving the pageRequest. It may be a bit weird to click a button and several seconds later have the animation happen. It may be better to either pre-load the data, or to pre-fill the div with some "Loading..." thing, make it about the right size, and then populate the actual contents when it arrives.
With help from the answer given the final solution was as follows:
Add another button and hide it.
<input id="btnHdn" runat="server" type="button" value="button" style="display:none;" />
Point the AnimationExtender to the hidden button so the firing of the unwanted click event never happens.
<cc1:AnimationExtender ID="aniExt" runat="server" TargetControlID="btnHdn">
Wire the javascript to the button you want to trigger the animation after the postback is complete.
<asp:ImageButton ID="btnShowList" runat="server" OnClick="btnShowList_Click" OnClientClick="linkPostback();" />
Add the required Javascript to the page.
function linkPostback() {
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(playOpenAnimation)
}
function playOpenAnimation() {
var onclkBehavior = ind("ctl00_aniExt").get_OnClickBehavior().get_animation();
onclkBehavior.play();
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.remove_endRequest(playOpenAnimation)
}