Angular scope variable not saved when using browser back button? - javascript

I have a AugularJS controller that have something like the following when initialized
$scope.urlChanged = false;
and the url is like /firstpage/test
There is a button in the page and when user clicks the button, the following is executed
$scope.urlChanged = true;
window.location = '/secondpage/test';
The page goes to /secondpage/test as expected. When clicking the browser back button, the page goes back to /firstpage/test. But the $scope.urlChanged is false, not the final value true. Is this expected in Angular? How do I make $scope.urlChanged = true when going back?

Scope variables are not saved when you navigate. In fact not even services will retain their values/state. When you navigate with browser back you actually request a whole new angular app.
This is not angular's fault. That is how the browser is expected to handle a new request. The way to persist data in this case is saving any data in something that will persists between requests.
As i see it you have three(ish) options:
Save state in cookies: Well supported by almost all browsers but take caution to handle them as clientside cookies or you won't be able to save data on a page you did not submit (excatly your problem with navigate back in browser).
Save server-side. This has the same problems as the server side cookies. You need to post data to the server for it to persist - which you could do with ajax calls and 'auto-save' with a timeout function. You also need a way of tracking who you are so you can get the correct data from the server - which is often done with cookies, but can be done with querystring parameters but also with (basic) authentication.
LocalStorage. This is my favorite option and is pretty well supported, if you don't need to support legacy IE browsers. There are good frameworks designed for angular that makes it easy to use - and some even have fallback to cookies if not supported.
Check out this link for LocalStorage support:
https://github.com/grevory/angular-local-storage

On change of view,new controller comes into picture and the previous view's controller's instance gets finished. Also , as every controller has its private scope which gets destroyed once view is changed to avoid confusion.

Related

Clear the storage persistent flag for a website

I'm using the navigator.storage.persist() API on Chrome, and have managed to get it set to true. But I would like to (at least for testing) be able to clear the setting putting back to false.
The API definition does not include a method or flag to clear as far as I can tell. See https://developer.mozilla.org/en-US/docs/Web/API/StorageManager and the living standard: https://storage.spec.whatwg.org/#storagemanager
However, for my purposes it would also be acceptable if there was a way from 'Site settings', the clear cache options, or even a custom page like the chrome://appcache-internals/ page for appcache.
If not, where does the flag get stored? i.e. what would I need to delete in the file system to reset things?
I has not been able to find a way to clear the flag for a specific website.
But for testing purposes (as you requested), the flag returned by the StorageManager.persisted() can be cleared by:
navigate to chrome://settings/?search=cooki
click on Clear browsing data
select Cookies and other site data in the popup and click Clear
data button
After performing the above steps, StorageManager.persisted() starts to return false.

Refreshing a page with cookies in Play

I have a drop-down selection of available languages. Currently, clicking on a language is mapped to a method in controller which updates the Play Session (which is a cookie under the hood) with the selected language and returns index page.
View:
English
Controller:
def setLanguage(language: String): Action[AnyContent] = Action { implicit request =>
val updatedSession = request.session + (("lang", language))
Redirect(routes.Application.index()).withSession(updatedSession)
}
As you can see, I redirect to index page and it's working fine. However, as the language selection is available at all times on my page, it may be clicked from /resource1, /resource2, /resource3 etc. and I would like to refresh that particular view instead of being returned to home page. I cannot simply get request.uri in the controller and refresh whatever it's pointing to because setLanguage() is mapped to its own route, so my request URI is always /language?lang=whatever.
So, how do I know that prior to invoking GET on /language, client was on, say, /items so I can reload items page instead of returning him to home page? Should I send a GET request with resource as a parameter (e.g. ?lang=en&location=items) to know which page to render? Should I make an ajax request and call window.location.reload() on success? Do I even need to go to server or can I simply update the PLAY_SESSION cookie manually from the client?
I'm using Play 2.3.7.
No you cannot update the PLAY_SESSION cookie from the client side, since it is signed by play with the application secret.
So I think the easiest solution would be, as suggested, to send the current resource as parameter and trigger a redirect.
There is an HTTP header called Referer that contains the url from which the request was made. As far as I know it's supported and used by all modern browsers when you navigate from a page to another.
You can simply redirect to that Referer url.
Another solution is to track in a session or a cookie all pages that are accessed by an user, by using some kind of interceptor in Global.scala or a custom Action builder that you use everywhere. Then in case of language change you can simply redirect to the last page that was accessed by the user.

What purpose is of "&rnd=" parameter in http requests?

Why do some web-applications use the http-get parameter rnd? What is the purpose of it? What problems are solved by using this parameter?
This could be to make sure the page/image/whatever isn't taken from the user's cache. If the link is different every time then the browser will get it from the server rather than from the cache, ensuring it's the latest version.
It could also be to track people's progress through the site. Best explained with a little story:
A user visits example.com. All the links are given the same random number (let's say 4).
The user opens a link in a new window/tab, and the link is page2.php?rnd=4. All the links in this page are given the random number 7.
The user can click the link to page3.php from the original tab or the new one, and the analytics software on the server can tell which one by whether it has rnd=4 or rnd=7.
All we can do is suggest possibilities though. There's no one standard reason to put rnd= in a URL, and we can't know the website designer's motives without seeing the server software.
Internet Explorer and other browsers will read an image URL, download the image, and store it in a cache.
If your application is going to be updating the image regular, and so you want your users to not see a cached image, the URL needs to be unique each time.
Therefore, adding a random string ensures this will be unique and downloaded into the cache each time.
It's almost always for cache-busting.
As has been suggested by others. This kind of behaviour is usually used to avoid caching issues when you are calling a page that returns dynamic content data.
For example, say you have a page that gets some current user information such as "mysite.com/CurrentUserData". Now on the first call to this page, the user data will be returned as expected, but depending on the timing and caching settings, the second call may return the same data - even though the expected data may have been updated.
The main reason for caching is of course to optimise the speed of frequent request. But in the instance where this is not wanted, adding a random value as a query string parameter is known to be a widely used solution.
There are however other ways to get around this issue. For example if you were doing an Ajax request with javascript/JQuery. You could set the cache to false in your call...
$.ajax({url: 'page.html', cache: false});
you could also change it for all page calls on document load with...
$.ajaxSetup({cache: false}});
If you were to do an MVC application, you can even disable the caching on the control action methods with an attribute like so...
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult NonCacheableData()
{
return View();
}
(thanks to a quick copy and paste from here)
I dare say there are also settings in IIS you could apply to get the same affect - though I have not been that far with this yet.

History.pushState and page refresh

I started looking at HTML5 new history API
However, I have one question. How can one handles the page refresh?
For example a user clicks a link, which is handled by a js function, which
asynchronously loads the content of the page
changes the URL with history.pushState()
The the user refreshes the page, but the URL of course does not exist on the server
How do you handle situations like this? With the hash solution there was no issue there
Thanks
This does require the server-side support. The ideal use of .pushState is to allow URLs from JavaScript applications to be handled intelligently by the server.
This could be either by re-serving the same JavaScript application, and letting it inspect window.location, or by rendering the content at that URL on the server as you would for a normal web application. The specifics obviously vary depending on what you're doing.
You need to perform server side redirection for copy and pasted fake URLs
It all depends on what server side technology you're using. There is no JavaScript way but writing a crazy function in your 404 page that redirect user based on incoming URL that is not a good solution.
The the user refreshes the page, but the URL of course does not exist
on the server
What do you mean by this? I have a feeling your assumption is wrong.
Actually yes, the point is it is the developer who should provide (serverside or clientside) implementation of url-to-pagestate correspondence.
If, once again, I've get the question right.
If you are using ASP.NET MVC on your server, you can add the following to your RegisterRoutes function:
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);
This will send all requests to Index action of your HomeController and your UI will handle the actual route.
For people looking for a JS-only solution, use the popstate event on window. Please see MDN - popstate for full details, but essentially this event is passed the state object you gave to pushState or replaceState. You can access this object like event.state and use it's data to determine what to do, ie show an AJAX portion of a page.
I'm not sure if this was unavailable in 2011 but this is available in all modern browsers (IE10+), now.

JavaScript: sharing data between tabs

What is the best way to share data between open tabs in a browser?
For a more modern solution check out https://stackoverflow.com/a/12514384/270274
Quote:
I'm sticking to the shared local data solution mentioned in the question using localStorage. It seems to be the best solution in terms of reliability, efficiency, and browser compatibility.
localStorage is implemented in all modern browsers.
The storage event fires when other tabs makes changes to localStorage. This is quite handy for communication purposes.
Reference:
http://dev.w3.org/html5/webstorage/
http://dev.w3.org/html5/webstorage/#the-storage-event
If the first tab opens the second tab automagically, you can do something like this:
First tab:
//open the first tab
var child_window = window.open( ...params... );
Second tab:
// get reference to first tab
var parent_window = window.opener;
Then, you can call functions and do all sorts of stuff between tabs:
// copy var from child window
var var_from_child = child_window.some_var;
// call function in child window
child_window.do_something( 'with', 'these', 'params' )
// copy var from parent window
var var_from_parent = parent_window.some_var;
// call function in child window
parent_window.do_something( 'with', 'these', 'params' )
The BroadcastChannel standard allows doing this. see MDN BroadcastChannel
// Connection to a broadcast channel
const bc = new BroadcastChannel('test_channel');
// Example of sending of a very simple message
bc.postMessage('This is a test message.');
// A handler that only logs the event to the console:
bc.onmessage = function (ev) { console.log(ev); }
// Disconnect the channel
bc.close();
See also another StackOverflow thread: Javascript communication between browser tabs/windows.
In my opinion there are two good methods. One may suit you better depending on what you need.
If any of these are true...
you can't store information server side,
you can't make many http requests,
you want to store only a little bit of information[1],
you want to be pure javascript / client side,
you only need it to work between tabs/windows in the same browser.
-> Then use cookies (setCookie for sending, getCookie/setTimeout for receiving).
A good library that does this is http://theprivateland.com/bncconnector/index.htm
If any of these are true...
you want to store information server side
you want to store a lot of information or store it in a related matter (i.e. tables or multi-dimensional arrays[2])
you also need it to across different browsers (not just between tabs/windows in the same browser) or even different computers/users.
-> Then use Comet (long-held HTTP request allows a web server to basically push data to a browser) for receiving data. And short POST requests to send data.
Etherpad and Facebook Chat currently use the Comet technique.
[1] When using localStorage more data can be stored obviously, but since you'd fallback on cookies one can't rely on this yet. Unless you application is for modern browsers only in which case this is just fine.
[2] Complicated data can be stored in cookies as well (JSON encoded), but this is not very clean (and needs fallback methods for browsers without JSON.stringify/JSON.parse) and can fail in scenarios involving concurrency. It's not possible to update one property of a JSON cookie value. You have to parse it, change one property and overwrite the value. This means another edit could be undone theoretically. Again, when using localStorage this is less of a problem.
The only way I can think of: constant ajax communication with the server to report any user action on the other tabs.
How about to use a cookie to store data in one tab and poll it in another tab?
i dont know yet if a cookie is shared between tabs but just an idea now ...
I just took a look at how Facebook Chat does it and they keep a request to the server open for a little less then a minute. If data comes back to the server, the server then sends back the message to each open request. If no data comes back in a minute, it re-requests and continues to do this (for how long, I am not sure).
Given that these tabs are open with the same site in them, you might consider building an ajax script that reports user actions to server and couple it with another ajax script that reads that reports and reflects them in current window.
You could use AJAX (as everyone else is suggesting) or cookies if the data is small. See http://www.quirksmode.org/js/cookies.html for fun with cookies.
One way to do this is to not let the chat window be dependent on the tabs. Load the tabs as seperate AJAX components that when reloads doesn't affect the chat component.
Depending on the requirements you can also use cookies/sessions. However, this means the data will only be accessible on the first page load of each tab.
If you already have two tabs open, changing something in one will not change the other unless you use some AJAX.
This can be done using BroadcastChannel API in javascript. Let's say you have opened two different pages in a different tab and want to update the first page when the user changes some values in the second page you can do that like below.
First page
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.onmessage = function(e) {
console.log('ticket updated')
};
Second page
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.postMessage();
Now when you can postMessage it will trigger the onmessage on the first page.
Also, you can pass data like the below.
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.postMessage({message:'Updated'});
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.onmessage = function(e) {
console.log('ticket updated',e.data)
};

Categories

Resources