14

I am trying to load records using breeze. While loading record i am showing spin icon. But somehow spin icon seems to stop while records are being loaded in grid. Here is my html

<div id="showSpin" data-bind="visible: isSpinning" style="padding: 10px; position: absolute; top:248px;left: 320px;  background-color: #FFF; opacity: 0.9; filter: alpha(opacity=90);">
    <img src="/images/spin.gif" />
</div>

here is my code to load image

isSpinning(true)
context.getData(name, records).then(function (data) {
     isSpinning(false);

    setTimeout(function () {
        isSpinning(false);
    }, 300);

})
.fail("Record not found");

Update1 I tried below code as per answer but nothing happens. I also included css. But cant see anything.

<div id="loading" data-bind="visible: isSpinning" style="padding: 10px; position: absolute; top:240px;left: 280px;  background-color: #FFF; opacity: 0.9; filter: alpha(opacity=90);">
    <i class="icon-spin " style="width: 40px"></i>
    <!--<img src="../../../../../Content/images/download.jpg" style="width: 40px" />-->
</div> 
James
  • 1,827
  • 5
  • 39
  • 69
  • 1
    I believe this is because the browser is loading a lot of records and basically freezes for a bit before showing the record. This effect might vary based on browsers. I am not sure if there is anyway around it – Huangism Jul 09 '14 at 14:46
  • I tested on IE 10 , chrome and result is same. So you mean no solution for this? – James Jul 09 '14 at 14:50
  • Could be your computer is slower or you are simply loading too much at once. I don't know the functions you are using, you can try jquery ajax and see if that works better – Huangism Jul 09 '14 at 14:54
  • @Huangism My computer is very fast. Even if i load 20 records it doesnt spin. I am using breeze to load data – James Jul 09 '14 at 14:55
  • I assume you already tested that the spinner actually spins by itself. I don't know about breeze, it is possible that's the cause of it. I have experienced similar behaviour with jquery but it's only sometimes – Huangism Jul 09 '14 at 14:58
  • @Huangism yes it spins. I clicked on image and checked if it was spinning. I would be thankful to you if you can give me solution. I also experience same with ajax request while trying to retrieve from WCF service. So i think its nothing to do with breeze – James Jul 09 '14 at 15:02
  • I don't know a solution for this, I added the js tag to your question and hopefully it will draw more attention and someone can provide a solution – Huangism Jul 09 '14 at 15:04
  • Do you not see that your set timeout has commented out the closing line? Also you are telling it after 300 milliseconds to stop spinning if you uncomment that out... – PW Kad Jul 09 '14 at 17:14
  • @PWKad When i click on button then i set isSpinning variable to true and then i call service to get the data and after recieving data from service i stop spinning by setting timeout. By mistake i had commented the line but its not the actual case – James Jul 09 '14 at 18:03
  • 2
    Not sure who is downvoting and why? What is wrong with my question? i have put in some efforts as well – James Jul 09 '14 at 19:32
  • Please help me. I am really struggling. – James Aug 04 '14 at 17:55
  • 3
    You may need to do the loading in a worker thread, its possible the loading is blocking the thread which animates the icon. – Vality Aug 08 '14 at 14:23
  • @Vality can you tell me how. You can get bounty if you can answer it – James Aug 08 '14 at 14:37
  • +1 ed to counteract the downvote, this question is valid and actually quite an interesting issue, also please see my answer. and feel free to reply if it is lacking. – Vality Aug 08 '14 at 15:09
  • @Vality many thanks for upvoting. dont know which useless had downvoted it – James Aug 08 '14 at 15:15

3 Answers3

11

This is happening because a Gif requires the thread to be open to display the next image. If you were using a pure css approach you would not see this issue. Take font-awesome for example -

<i class="icon-spin icon-spinner"></i>

Because this is a pure CSS approach the spinner will continue to spin even when the thread bogs down loading all of of your new records and associating their related entities.

If you need to continue spinning I would highly including this bit of CSS from the Font-Awesome source -

@-moz-keyframes spin {
  0% {
    -moz-transform: rotate(0deg); }

  100% {
    -moz-transform: rotate(359deg); } }

@-webkit-keyframes spin {
  0% {
    -webkit-transform: rotate(0deg); }

  100% {
    -webkit-transform: rotate(359deg); } }

@-o-keyframes spin {
  0% {
    -o-transform: rotate(0deg); }

  100% {
    -o-transform: rotate(359deg); } }

@-ms-keyframes spin {
  0% {
    -ms-transform: rotate(0deg); }

  100% {
    -ms-transform: rotate(359deg); } }

@keyframes spin {
  0% {
    transform: rotate(0deg); }

  100% {
    transform: rotate(359deg); } }

.icon-spin {
  display: inline-block;
  -moz-animation: spin 2s infinite linear;
  -o-animation: spin 2s infinite linear;
  -webkit-animation: spin 2s infinite linear;
  animation: spin 2s infinite linear; }

And using either a static icon, image or sprite and just applying class of 'icon-spin' to it, regardless of whether it is an icon or not.

Edit

Add this wherever you are declaring your CSS -

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">

Change this -

<div id="showSpin" data-bind="visible: isSpinning" style="padding: 10px; position: absolute; top:248px;left: 320px;  background-color: #FFF; opacity: 0.9; filter: alpha(opacity=90);">
    <img src="/images/spin.gif" />
</div>

to this -

<div id="showSpin" data-bind="visible: isSpinning" style="padding: 10px; position: absolute; top:248px;left: 320px;  background-color: #FFF; opacity: 0.9; filter: alpha(opacity=90);">
    <i class="fa fa-spinner fa-spin"></i>
</div>

The reason it is fa instead of icon is the current version of FontAwesome changed to use fa instead of icon due to collisions

Last Edit

Your problem now is that your logic is faulty. I have tried to explain this in comments but I will give one last update with EXACTLY how your logic should look if you want to show your spinner and have it spinning.

isSpinning(true)
context.getData(name, records).then(function (data) {
    isLoading(false);

    setTimeout(function () {
        isSpinning(false);
    }, 1000);

})
.fail("Record not found");


<div id="showSpin" data-bind="visible: isSpinning" style="padding: 10px; position: absolute; top:248px;left: 320px;  background-color: #FFF; opacity: 0.9; filter: alpha(opacity=90);">
    <!-- THERE IS NO REASON TO USE A VISIBLE BINDING HERE AND ON THE PARENT -->
    <i class="fa fa-2x fa-spinner fa-spin"></i>
</div>

The reason this wasn't working is in your logic. Copy this EXACTLY to your solution and it will work.

Community
  • 1
  • 1
PW Kad
  • 14,953
  • 7
  • 49
  • 82
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58977/discussion-between-happy-and-pw-kad). – James Aug 08 '14 at 16:35
  • So if the solution is working for you but you need to make adjustments I would recommend reading the font awesome docs. As it stands this should fix the problem you are having but you just need to make some adjustments to accommodate for your liking. In particular use fa-2x or fa-3x to make it larger but their docs are invaluable – PW Kad Aug 08 '14 at 16:35
  • Its spinning but if i use it on button and not inside div.. why?? – James Aug 08 '14 at 16:44
  • sorry not working on button either. I mean it works if i just use icon on button with no hiding and unhidding – James Aug 08 '14 at 16:51
  • if i remove visible binding on icon then it works icon – James Aug 08 '14 at 16:54
  • Happy, I am trying to help you but I don't think you are trying anything right now. You obviously are setting isLoading = false as soon as your data call begins. So you are making it visible from the parent but not from the child. http://jsfiddle.net/pwkad/0bzg5ne0/1/ – PW Kad Aug 08 '14 at 16:56
  • Hello i am really trying and thats why i tested if i use icon on button then it works or not. I am realy frustrated with this issue – James Aug 08 '14 at 17:00
  • so when should i make it visible then? I want to make visible after i click on search button. When i click search button it goes to server to fetch data via ajax request. after i recieve data i want to stop showing. – James Aug 08 '14 at 17:03
  • If you see my question , thats what i am exactly doing, so dont know what your edit will solve my issue. I am exactly doing same steps – James Aug 08 '14 at 17:08
  • It spins for 1 second or so and then when it goes to fetch data from server it just freezes – James Aug 08 '14 at 17:14
  • That's probably because you aren't using promises properly. Console.log when the promise gets returned to the getData call vs when the actual data is returned, I bet the getData call's promise is returned first. – PW Kad Aug 08 '14 at 17:31
  • So is there any other way i can use promise? Where exactly to put console.log? before calling getData and after it has finished? – James Aug 08 '14 at 17:36
  • @Happy The problem here is I have more than adequately answered your original question and demonstrated multiple times that the reason you are experiencing variations right now is because you have logic or syntactical issues. Your original question was around how can you prevent the gif from freezing, which I answered, then how can I use icons, which I answered, where do I put CSS, I answered, why doesn't it show when xxx, I answered, now you are asking about how to properly use q.js and JavaScript promises. Consider marking this as correct if the solution works and searching on promises. – PW Kad Aug 08 '14 at 17:57
  • By the way a 300 ms timeout means your spinner is only spinning for .3 seconds, change it to 10000 for 10 seconds and you will see what I mean. If you aren't sure how to use logging statements in JavaScript consider looking up how to console.log and check timing. If you need to learn promises consider looking up a tutorial or walk-thru on promises.\ – PW Kad Aug 08 '14 at 17:58
  • I set 100000000 but still it freezes. Your solution doesnt solve my issue. My logic is correct but dont know exactly whats happening behind. I would really like to thank to you for being patient with me.Why dont you try yourself in your application to load lots of record using ajax and check if it works. I dont have any logic issues but i am missing something or need to add something. I cant tell you how frustrated i am . Solving this past 1 week now :( – James Aug 08 '14 at 18:14
  • i read this somewhere "I have seen this behavior in the past when making AJAX calls. I believe this is related to the fact that browsers are only single threaded, so when the AJAX call is returned the thread is working on the call, so consequentially the animated GIF needs to stop momentarily." – James Aug 08 '14 at 18:23
  • I have it in many of my applications and pure css animations are not affected by the browsers' single thread being held up. – PW Kad Aug 08 '14 at 18:25
  • Are you aware about breeze framework? I think its something to do with that. I am using breeze framework to get data which internally uses oData and Ajax. If i use this spin.js using normal ajax request then i can see that its spinning but when i make request through breeze then it freezes – James Aug 11 '14 at 12:23
  • Breeze.js only does what you tell it to, the freezing is because you are returning way too much data. To get around it you asked how to prevent the icon from freezing, which I have already answered numerous times, if you have a new question related to it not freezing I would suggest starting a new question. – PW Kad Aug 11 '14 at 12:44
  • I am returning 200 records, still then it freezes? Is there any other way we could handle this? Is it anything special does breeze framework require? – James Aug 11 '14 at 12:50
  • Is it something you can point to me https://groups.google.com/forum/#!topic/durandaljs/VnSX-uV-fA8 – James Aug 11 '14 at 12:51
  • Note: You have to use inline styles to get this to work nicely. And here are some style changes to center it on the page: `top: 50%; left: 50%; transform: translate(-50%, -50%);` – phyatt Feb 28 '18 at 21:02
1

This is only something of a guess, but I think your record loading script is blocking the web browser by running in the main thread. This is not a desirable behaviour but fortunately this is exactly what webworkers were made for. First I recommend you read this page to get a little background then read on.

Basically you need to first move your loading code into a separate javascript file. This separate file should contain all the code to load the record, then place it in an object and post the object back to the main thread as shown bellow. You can then invoke this code from your main thread using:

var myWorker = new Worker("my_loading_code.js");

Now the content which a webworker can directly access is limited due to thread safety concerns, therefore you may need to retrieve the records and then send them to the main thread using a postMessage(x); call which allows you to send any object back to the main page. The main page can then react to this message by installing a message handler with:

myWorker.onmessage = function(record){
    // This is only pseudo code, use your actual code here
    reactToRecievingRecord(record);
}; 

This means that the main thread is not blocked while all the records load and can animate your icon, only blocking briefly when it receives a record.

If this is not clear enough or you need more help please just ask :)

EDIT:

To go into detail, in your case, inside the separate file you will want some code which does something along the lines of:

context.getData(name, records).then(function (data) {
    postMessage(data);
})

then in your main file you want:

isSpinning(true);
var myWorker = new Worker("my_loading_code.js");
myWorker.onmessage = function(record){
    // This is only pseudo code, use your actual code here
    isLoading(false);
    isSpinning(false);
}; 

Note, this code does not actually do anything with the data once it receives it, you will want to handle that but I am sure you get the rough idea. Note these are also snippets only, you will need to turn them into full functions etc

Vality
  • 6,577
  • 3
  • 27
  • 48
  • As per you example i need to move this code context.getData(name, records).then(function (data) { to some other file ?? – James Aug 08 '14 at 15:17
  • @Happy I am going to edit the answer as this comment box is too small to reply properly. – Vality Aug 08 '14 at 15:18
  • @Happy does that help at all? You place the first bit into a file called my_loading_code.js (once you have played around and it does what you want) and the second bit replaces what you had originally in your main file. – Vality Aug 08 '14 at 15:28
  • i have still not tried but this method i cant use it because i have to create new js file only to load ajax request. I have so many ajax request in my application and then it will too many js files only for those. Any other ideas? – James Aug 11 '14 at 11:25
  • @Happy Don't worry, you dont have to create a new js file for every different ajax request. Create only a single extra js file which after being created will be sent a message by the main thread telling it what kind of request it is to handle, the worker can then run the appropriate function/code path. This means you only need a single script for the worker thread which can be used for every type of request. – Vality Aug 11 '14 at 12:06
  • As per your example i have call this "context.getData(name, records).then(function (data) {" inside new js file? am i correct? – James Aug 11 '14 at 12:10
  • @Happy Yes, that is correct, but you can prefix that with a check as to which kind of request it is so you can have multiple types in the same worker file. – Vality Aug 11 '14 at 12:15
  • Are you aware about breeze framework? I think its something to do with that. I am using breeze framework to get data which internally uses oData and Ajax. If i use this spin.js using normal ajax request then i can see that its spinning but when i make request through breeze then it freezes – James Aug 11 '14 at 12:17
  • Is it something you can point to me groups.google.com/forum/#!topic/durandaljs/VnSX-uV-fA8 – James Aug 11 '14 at 13:06
1

You could also Base64 encode the graphic and apply this loading graphic via CSS thus making subsequent usage faster and saving HTTP requests. The result of doing the Base64 encoded animated loader, is that for small common reusable assets like a loading or processing assets, it is already available and will continue to animate while performing numerous AJAX / HTTP requests, which is what you are trying to solve here.

See Also:

In this way, you can have the graphic loaded when the CSS is loaded. Because having Base64 encoded images in not exactly the most maintainable solution you could use a technology like SASS / Compass and use the path to an asset and then when you pre-process or compile to css it uses the path to the asset or resource and encodes it to a Base64 encoded version for you. This technique will work with all kinds of image formats etc..

Sass / Compass Base64 References:

"Embeds the contents of an image directly inside your stylesheet, eliminating the need for another HTTP request. For small images, this can be a performance benefit at the cost of a larger generated CSS file"

But beware! Base64 encoding it isn't without some caveats

  • hard to maintain and update: without some tool to help, manually editing and putting together image sprites is quite a chore

  • increased memory consumption (possibly very dramatic): this is often overlooked. The time to deliver the images is decreased at the expense of a bigger memory and CPU footprint, especially for large sprites and sprites with a lot of whitespace.

  • bleedthrough: for sprites that don’t have much whitespace to separate images, there’s an increased chance of nearby images visibly bleeding through other elements, as in this case where bleedthrough was only seen on iOS (but looked fine on Chrome and Safari on the desktop). Note

Example Base64 Loading Spinner:

.genericLoader { background-image: url('data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==');   
}

Working Example:

When to Base64 Encode Images (and When Not To) - http://davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to/

Encoding Tools

Other StackOverflow References:

Community
  • 1
  • 1
Frankie Loscavio
  • 344
  • 4
  • 15
  • i tried it but still same issue. It just freezes during ajax request – James Aug 11 '14 at 11:02
  • Are you aware about breeze framework? I think its something to do with that. I am using breeze framework to get data which internally uses oData and Ajax. If i use this spin.js using normal ajax request then i can see that its spinning but when i make request through breeze then it freezes – James Aug 11 '14 at 12:22
  • Is it something you can point to me groups.google.com/forum/#!topic/durandaljs/VnSX-uV-fA8 – James Aug 11 '14 at 13:08