Load data without hard refresh - javascript

Case: An user purchases an airplane ticket from American Airline on my website. It takes an hour for the order to get processed, after which I need to show a navigation tab.
Question: I don't want the user to refresh the page to load the data. Is there something that can trigger an API call an hour later after the user's ticket purchase?
I'm using AngularJS, Express, NodeJS.
Update:
I thought about using setTimeout(fn, delay) to initiate a timer and then use clearInterval(id) to close it after request success. However, the browser executes on a single thread asynchronously so the timer delay is not guaranteed. In addition:
If the user put the computer to sleep, the setTimeout would freeze.
If the user switched tab, the setTimeout function becomes inaccurate.
The setTimeout behavior could be different in different browsers.
I also consider about node-cron, but I still need to make a node call automatically because the navigation controller is loaded on app start. This means the user still has to do a hard refresh to see the navigation tab.
Is there a way to use browser cache to do it?

With javascript, you can dynamically load and change data.
for example below code will get triggered every minute and inside, you can perform asynchronous tasks such as calling http requests, and dynamically update value.
setInterval(function(){
//perform async task and get new value
document.getElementById('DOM').value = "NEW VALUE";
}, 60000);

Related

Clean design for centralized navigation?

Context
Single page / ajax web app
Basic code structure
LocationManager (responsible for updating the browser hash and switching the application location to a different tile)
Page/Tile Flow
Basic Info > Household Info > Vehicle Info > Purchase Options > Review Order > Enter Payment and Submit
Problem
When the user navigates from Purchase Options to Review Order, a long (5-8 second) service call is made to calculate order details. Upon the call's resolution, the callback is designed to navigate the user to Review Order page. The issue is, if the user clicks back during that time and goes back to Household Info, as soon as the call resolves, they will be "automatically" brought to Review Order. Very awkward user experience.
Limitations
Canceling the call is not an option. Need a solution to handle the navigation.
Current Proposed Implementation
Save "currentLocation" prior to making the calculateOrder call.
Pass the "currentLocation" in the callback to the setLocation method as intendedStartingPoint.
Inside setLocation method if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate}
To sum it up, if the user changes the location while the call is in progress, upon the call's resolution, we won't navigate since the user doesn't expect to be navigated to Review Order at that point.
This works, right?
The Catch
We have many places in the app where setLocation is called within a callback for a long-running call. This means that I will have to update all the setLocation calls with a new parameter - intendedStartingPoint. While it makes sense to me, it does seem like it has potential to get a bit cluttered.
Any ideas on how to clean it up and centralize it?
So, right now a user can click the Calculate button on a Purchase Options page. You then display some kind of a loading indicator (hopefully)
and send an asynchronous request to a server with setLocation('ReviewOrder') attached in a continuation. There is quite a number of places in the application where you use this pattern.
The problem of unexpected (from a user point of view) redirects is there because with this approach server data retrieval and UI navigation are coupled. A solution that comes to mind is to decouple them and remove setLocation calls
from all long-running request continuations. It can work the following way.
When the user clicks the Calculate button, you start an asynchronous request and at the same time immediately navigate to the Review Order page (this is important from a UX perspective since users now clearly understand that the Calculate button navigates to Review Order). On the Review Order page, display a loading indicator saying something like 'please wait, about 10 seconds remaining...' When a request completes, hide the loading indicator and show the data.
This way your users will have a consistent UX knowing that whenever they click a button in your application the same thing happens (they navigate to a view), and there are no surprising automagical redirects.
Given that you can't prevent the user from navigating among the tiles, notifying her about the calculation delay won't solve the whole problem. You can tell the user the estimated time to completion, you can display a progress bar, and you can take her immediately to the Review Order tile to wait for the results, but if she navigates away from the tile, you're left with your original problem.
If the user chose to navigate away after all of that information, she must have made a conscious decision to interrupt the proceedings. It would be bad UX to transport her back to Review Order. What now?
You propose, quite reasonably, that the callback function sent with calculateOrder should pass an intendedStartingPoint parameter to setLocation. You worry that this would require you to modify every call to setLocation to accommodate the new parameter. Never fear, JavaScript offers a neat way to solve this dilemma.
You can add a new parameter to setLocation without modifying the existing calls. This merely requires that intendedStartingPoint be the last argument in setLocation's argument list. Then your new version of setLocation can check the value of intendedStartingPoint to see if it's undefined.
If intendedStartingPoint is undefined, you know that setLocation is receiving one of the old calls, the ones that don't pass intendedStartingPoint. In these cases you ignore intendedStartingPoint and proceed as before. Otherwise, you compare intendedStartingPoint to the current location and proceed according to the result of the comparison.
An even better approach would be to make the new parameter not intendedStartingPoint, but an object called options that contains intendedStartingPoint as one of its attributes. This allows you to pass further optional values to setLocation if the need arises in the future.
The new behavior of setLocation is quite simple. Before setting a new location, you check whether intendedStartingPoint is equal to the current location. If it is, you don't have to do anything because the user is already where she's intended to be. But if the intendedStartingPoint is different from the current location, the user has navigated away, so you do something like this:
if (LocationManager.currentLocation !== options.intendedStartingPoint) {
// Tell the user that the calculation has finished.
// Ask her if she wants to return to Review Order now.
}
First thing, calculate order details via asynchronous call and show/simulate a progress bar to the end-user via javascript.
The second thing: do not enforce ReviewOrder tile opening in your service callback function. As the service completes it's calculation, your callback function checks the current tile, and if it is not ReviewOrder tile, then it stores the calculated information in the Session or Local Storage.
As user navigates ReviewOrder tile, compare order details which came from the user with the stored order details (via hashing function, for example).
If hashcodes of user order details and stored order details are the same, then show saved order information, otherwise call the service again.
Important note: to prevent order forging, consider the following way:
Upon calculating order details on the server, generate unique order id, that will be returned to the user. Then store the calculated order details along with this id in the server database. If user did not change order details, your script will post only this order id to the server as a sign, that order has been accepted. Then read your database and process the order by this id.
If order was not completed then employ a scheduled task, that cleans up your database from non-completed orders (for example - orders, calculated 24 hours ago, but still not completed).
First of all, if the user is able to go back and change any entered information on previous pages, it is a must to invalidate any pending service calls. If a service call based upon outdated information returns, it must be discarded.
This means, if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate} is not sufficient. You have to do something like if(intendedStartingPoint === Locationmanager.currentLocation && /* no information altered in the meantime*/) {//Navigate}.
Now for your design question: It's a little hard to construct this without any concrete code, but you could do the following:
Provide means to register and manage long-running calls in LocationManager
Long-running calls should then always be registered with the LocationManager
LocationManager should assure that at most one long-running call is active at a moment
If any location change occurs, all (or the one active) long-running call must be invalidated
Call-back of a long-running call should check if it not has been invalidated, and only navigate if this is the case. LocationManager could do this in a unified manner for all call-backs.
New long-running calls could replace/invalidate an already running call or be rejected, as you like.
I hope this makes sense in your concrete situation.

PHP Mysql: Avoid overflow on pageviews?

So I have certain profile pages that needs to have page views. I stored the view data in mysql and php (actually laravel). Basic idea is to add view by 1 every time the profile is visited.
Basic idea is to add a php code in the profile page, like
$page->view += 1; $page->save();
But what if the user hits f5 several times? It will cause the query to run a lot and I fear it will eventually slow the app.
I was thinking of making a prompt with js when user hits f5 and an ajax call to add the view. But I was wondering if there is a better solution (like, how did youtube do their views)?
You can count the views after a certain amount of time on the page.
To achieve this, you will need a delayed javascript function like:
setTimeout(function(){countView()},3000); (3 second delay)
Where countView() should be an ajax call to a function that runs $page->view += 1; $page->save();
And since Ajax calls are asynchronous, the execution will not delay your application flow, although a simple increment on a page-view table should not do much harm in terms of performance
This will work as long as the user doesnt delete the cookies.
if ($_COOKIE['returning']!="yes")
{
// first visit!
}
setcookie("returning", "yes", time()+360000);
Another way would be to log the IP in a database and check if user already visited, but the code is more complex than i can post right now. I will edit the post later if accepted.

Triggering a DB call on browser leaving current page?

I've got an application that I intend to set a lock flag in my database that would exclude others from viewing that same page if set.
However, once set - I have no idea how to "unset" it. I could make it up to the user to unset the flag, but that seems unnecessary.
I'd want to simply look for the browser to leave the page, make a call to the database, and unlock the page.
How does one do this "type" (not looking for the exact way) of thing with JSF/Javascript/jQuery (all options)
There's really not a reliable way to do this, that I've seen anyway.
You can use the browser's onbeforeunload event to tell the server, "Hey I'm leaving the page now.". The issue is you can't actually block the page from unloading. If the user is actually closing the browser, any open sockets are going to be closed immediately. Your web server may or may not get the request in time. I've had very flaky results with this approach.
One approach that might work is to employ some sort of timeout mechanism. The page would ping the server every 30 seconds or what not, saying "I'm still here." If the server did not get this update after a few minutes, it would invalidate the session and free up that document. Perhaps this could be optimized by checking for the last ping when someone new came along. One issue with this is if someone left the page, the next user might have to wait a minute or two before they could go to the page. You'd then have to find a ping frequency that doesn't flood your server with traffic, but also doesn't make the next user have to wait too long.
It's also possible to combine these two methods. When the user leaves the page, trap the onbeforeunload event and immediately invalidate the session. However, if it didn't work, the session would time out after a minute of not being pinged.
Are there better solutions?
If you really need to lock a document in a web app so multiple users can't edit it, you might want to investigate your overall design. Are you afraid of users clobbering data? If so, maybe employ a mechanism that can resolve merge conflicts, or detect if both sets of changes can be combined.
If you wanted to go truly Web 2.0, you could design something similar to Google Docs, where changes appear live as they're made. No need for a Save button anywhere!
Sending a "keep-alive signal" might be an option. Something along these lines on the frontend side, combined with session cookies.
setInterval(function() {
var img = new Image();
var src = "http://examle.com/keepalive.gif?cachebuster=" +
Math.ceil(Math.random() * 10000 );
}, 1000);

OpenGraph watch action - less than 50% watched

I'm using the Javascript SDK to contact the API.
Using Built-in Watch action:
When a user watches less than 50% of a video, or if a user removes a watch activity from your app/site, you should also remove the corresponding action instance that was published to Open Graph by invoking the following call
DELETE https://graph.facebook.com/[watch action instance id]
My problem is with when the user navigates away from the page.
I've tried to use the jquery unload method to make the delete call to the API but it fails to. I've also tried using ajax to make the call synchronously but this holds/freezes the browser for at least 5 seconds on average.
Any ideas?
I've tried to use the jquery unload method to make the delete call to the API but it fails to.
Well, that’s a problem with the call being asynchronous. Your unload handler fires, starts the request, and then the browser navigates away from the page. Wait, what, there’s a request still running? Let’s terminate that, since I’m about to load and display another page anyway …
I've also tried using ajax to make the call synchronously but this holds/freezes the browser for at least 5 seconds on average.
If that’s how long your call takes, then I see no realistic way of shortening that.
You could try setting up a script that terminates straight away, without giving a return value (or a yeah OK, go on with your stuff, browser response) – and finishes the rest (making the actual API call) afterwards, server-side.

Run a function on load and then every 10 minutes

Is there a way to execute a javascript function when the page loads and then not again for another 10 minutes. Even if user browses to another page? If they dont want the function to run again until the 10 minutes is up?
Simply create the function you want to execute onload, call it once on DOM ready, and then use setTimeout()
<script type="text/javascript">
function doSomething() {
alert("Doing something here");
}
window.onload = function () {
doSomething(); //Make sure the function fires as soon as the page is loaded
setTimeout(doSomething, 600000); //Then set it to run again after ten minutes
}
</script>
Edit:
Didn't notice the part about even if they're on another page. If it's another page on your website, you could set a cookie once the page loads, and include a timestamp for when the cookie was set. In the window.onload of the rest of your pages, you can then read that timestamp, and calculate whether 10 minutes have passed or not. If it has, call the function again.
Edit: 10 minutes is 600000 milliseconds
Even if user browses to another page?
Nope. Once your page closes, you no longer have control over what happens.
function callfunction () {
// do something
}
window.onload = function () {
// Initial function call
callfunction();
setInterval(function () {
// Invoke function every 10 minutes
callfunction();
}, 600000);
}
Use setInterval or setTimeout to make something happen in the future.
But as soon as the user leaves your page those timers will be cancelled - the entire content will be unloaded.
You can use:
setTimeout(alert('After 10 secs'),10000);
If the user visits another website in the same tab, you're out of luck; once your webpage is closed the javascript dies.
However you're in luck if the user is just visiting other pages on your website. You can do this as follows... but it requires a fundamental redesign of your website. You'd have to use a framework which wraps all link-clicks in XHRs (XML HTTP Requests) and replaces the page with the new data, giving you the illusion that you are clicking links (which you are, but the browser never refreshes the page). This can be done with an iframe or not. This is how GMail works.
You could code such a framework yourself, but you're better off using a framework which has url-rewriting support for faking a history (or else your website won't work well with user histories).
To answer your question about the "every 10 minutes", use setInterval(myFunction, 10*60*1000) (which may have issues in some browsers due to optimizations).
Assuming that you use a server language, you can set a session variable at startup (startup date/time) and then, using setInterval, compare current time with startup date/time.
you're required to save the state in a remote server.
this is your steps:
the javascript function loads, no matter where or when, it loads.
when loaded (the javascript fn) calls a remote server using a UNIQUEID, that can be obtained via cookies, or session variable, from the loggedin user id...whatever.
you send a request to a server using such UNIQUEID (via ajax, normally), your server will tell you if such process must start again or you should avoid it (a page jump as an example or a page refresh in a different browser using the same login, it depends on your business logic).
the response from step3 is used to execute/initialize the javascript loader or not.

Categories

Resources