In my web application, I got a form on 2 different pages, purchase1 and purchase2.
If a customer refreshes the page at purchase2, I want the location to be changed back to purchase1.
I haven't found a way to do so, I've tried to make a config like that:
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('/purchase2', '/purchase1');
}
But obviously, that way I can never get to purchase2 page.
I need it to happen only on manual user Refresh.
Is there any way to do so? Some built-in Angular function that happens on Refresh?
Something like
$scope.onRefresh = function(){
$location.path('/dashboard/purchase1');
}
Haven't found anything like it.
You can listen for beforeunload event. beforeunload event will be triggered when someone hits a F5 or refreshes the page anyhow. Do something like,
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
event.preventDefault();
//below is the redirect part.
$window.location.href = '/purchase1';
});
Put this code on purchase2 page.
Yes, you can do it.
Register a global listener for state change:
$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
// If fromState is null that means it is a page refresh otherwise
// The fromState will be the purchase1
if (toState.name == 'purchase2' && !fromState) {
$state.go('purchase1');
event.preventDefault();
return;
}
});
By global, I mean register the above in such a controller whose scope is available throughout your application like a controller added to your body or html tag.
Also, I'm assuming the purchase1 & purchase2 are the name of your states for both the pages. If they are parameters to a common state you can alter the code above.
What happens on refresh is the same as what happens on first load.
What you can do is check whether the user has been here already, e.g. by setting a cookie and reading it later.
However, I think you should rethink your design. Why would you invest your time into making refresh function behave worse?
I think, javascript isnt the right way because javascript does not know anything about the previous page, because it will be rendered when page is already loaded. So try to check this in your server side application like in php or jsp and read the request-header because there you can get current url and redirect user to the new url
You can try to have a flag say purchase2-loaded and keep this variable in localStorage. You can then write an IIFE, which will check value of this variable and reroute to purchase1.
Also on purchase1, reset it to false.
Note:This is a pure JS Code and relies on localStorage.
Fiddle.
Sample Code
(function(){
var isPurchase2Loaded = localStorage.getItem("Purchase2");
if(isPurchase2Loaded || isPurchase2Loaded === undefined){
document.write("Rerouting to purchase 1...");
}
else{
document.write("Loading for the first Time...");
localStorage.setItem("Purchase2", true);
}
})()
Eventually, if someone is interested, I fixed it like that:
In purchase1.js I've added this to the submit() function:
$rootscope.demographic=1;
In purchase2.js I've added this code to the controller:
var init = function(){
if(angular.isUndefined($rootScope.demographic)){
$location.path('/purchase1');
}
};
init();
It is working because Refresh(F5) completely restarting the application, therefore also resets the $rootscope and makes "Demographic" Undefined.
Upon entering purchase2, "init" function will start, if we came from purchase1 everything will be ok and demographic will be defined, otherwise we will just load purchase1 again.
:)
$state.go('purchase1');
this should resolve your problem.
Related
I have a cordova application that uses push notification (still using the old plugin :-().
The application uses ngRouter and the navigation is relatively basic - in that I mean that my main menu changes ngView but popups/modals are not part of the navigation and are either triggered by some bound controller property or through a call to a controller function (e.g. $scope.openMyModal).
I am trying to be able to call such function on one of my controllers after I received push notification (and the controller is loaded).
I implemented some code using a timeout to broadcast an event which should be caught in the relevant controller and open the modal. Roughly the code is:
In app.js:
onNotification() {
// some code for determining the type of notification
// then
setTimeout(function() {
$rootScope.$broadcast("someEventCode");
}, 10); // or 1000 in case of cold start
}
In MyController.js:
.controller('MyController', function($scope, $rootScope, $modal,...) {
$scope.openMyModal = function() { // open modal using $model }
$scope.on("someEventCode", function() {
$scope.openMyModal();
});
}
This kind of works but is not consistent/deterministic. For example, in slower devices it may broadcast before the controller is ready to respond to it.
I also tried to set some variable on root scope (in onNotification) and in the controller create a function which is called from the markup (e.g. {{isNotificationReady()}}) but this also doesn't work well.
Another approach was to use double notifications - set a flag in root scope when the notification arrives, wait for an event from the target controller (indicating it is loaded) and then, at $rootScope again, if flag is set, broadcast the "open dialog" event (and delete the flag). Following this approach, I am not sure how to trigger the "loaded" event so I use a function from the markup:
In MyController.js:
$scope.isLoaded = function() {
$scope.$emit("myControllerLoaded");
}
In markup:
<div><!-- the content --></div>
{{isLoaded()}}
In app.js
$rootScope.$on("myControllerLoaded", function(event) {
if ($rootScope.notification === "someEventCode") {
$rootScope.$broadcast("openTheModel");
delete $rootScope.notification;
}
});
This seems like cumbersome and inefficient code. isLoaded() is called multiple times (not sure why) and it is kind of spaghetti code.
My question is - how should I implement something like that in a clear and efficient manner? Just a reminder, the app could be "cold started" or in the background and I need to know when it is "running" (or the controller is ready).
I've found a slightly more robust, timeout based implementation (still not exactly what I was hoping for).
The idea is to set a flag and send (broadcast) the signal after some time. Then resend the signal on interval until the flag is unset by the target controller:
In app.js
function broadcastSomeEvent() {
$rootScope.$broadcast("someEventCode");
if ($rootScope.eventFlag) {
setTimeout(broadcastSomeEvent, 50);
}
}
onNotification() {
// some code for determining the type of notification, then
$rootScope.eventFlag = true;
setTimeout(broadcastSomeEvent, 10); // or 1000 in case of cold start
}
In MyController.js
$scope.$on('someEventCode', function() {
delete $rootScope.eventFlag; // delete flag so event is stopped
$scope.openMyModal();
});
This is still an iff-y implementation to my taste. Even though it does work for both cold start and when the application is in the background I believe that it is not robust as it should.
Still, I wouldn't mark this solution as "the answer".
On the other hand, with no proper state routing, maybe there's not much more than can be done.
I have a Vue.JS application with a beforeDestroy method that I'm trying to use to store some data in local storage before the app closes or is reloaded.
Just for reference:
beforeDestroy: function() {
localStorage.setItem('preference', this.preference);
...
}
However, neither closing nor reloading the app actually calls this method.
How can I make sure this method is called before the app is closed?
What is it that "closes" or "reloads" the app? Is it the user in the browser closing the window and/or refreshing the page?
If that is the case, nothing is "destroyed" and that function won't run.
You would have to define a onbeforeunload function globally:
window.onbeforeunload = function(){
return "Are you sure you want to close the window?";
}
Then you can save items to local storage inside that function before the return statement.
Note: don't define it inside your beforeDestroy function, because again, that just won't run when you reload the page or navigate away.
Understand this question is quite old but if I'm out there searching for it, someone else is!
Unfortunately, as far I can tell there is nothing in Vue that can currently hook into before the user reloads the page either via closing, CMD+R, selecting a new URL etc.
The best solution, if it's sensitive data and you really want them to think about what they are doing, you can do the below which is taken from this medium post.
<script>
export default {
data: () => ({
isEditing: false
})
beforeMount() {
window.addEventListener("beforeunload", this.preventNav)
},
methods: {
preventNav(event) {
if (!this.isEditing) return
event.preventDefault()
event.returnValue = ""
}
}
}
</script>
This will at least bring up a dialogue box asking if they are really sure they want to reload the page.
Note the text of the dialogue box cannot be altered. A good description of what is going on with the dialogue box text is contained in this StackOverflow question.
I am wondering if there is a way to execute a JavaScript function once only on the first ever page load and then not execute on any subsequent reloads.
Is there a way I can go about doing this?
The code below will execute once the onload event fires. The statement checks if the onetime function has NOT been executed before by making use of a flag (hasCodeRunBefore), which is then stored in localStorage.
window.onload = function () {
if (localStorage.getItem("hasCodeRunBefore") === null) {
/** Your code here. **/
localStorage.setItem("hasCodeRunBefore", true);
}
}
Note: If the user clears their browsers' localStorage by any means, then the function will run again because the flag (hasCodeRunBefore) will have been removed.
Good news...
Using localStorage can be tedious because of operators and long winded function names. I created a basic module to simplify this, so the above code would be replaced with:
window.onload = function () {
if (!ls.exists('has_code_run_before')) {
/** Your code here... **/
ls.set.single('has_code_run_before', true);
/** or... you can use a callback. **/
ls.set.single('has_code_run_before', true, function () {
/** Your code here... **/
});
}
};
Update #1
Per #PatrickRoberts comment, you can use the in operator to see if a variable key exists in localStorage, so
if (localStorage.getItem('hasCodeRunBefore') === null)
becomes
if (!('hasCodeRunBefore' in localStorage))
and is less verbose and a lot cleaner.
Secondly, you can set values as you would an object (localStorage.hasCodeRunBefore = true) though it will be stored as a string, not as boolean (or whatever data type your value is).
function toBeExecutedOnFirstLoad(){
// ...
}
if(localStorage.getItem('first') === null){
toBeExecutedOnFirstLoad();
localStorage.setItem('first','nope!');
}
All JavaScript must execute every time a page loads. If the script is on the page, it will execute.
The logic that is executed within the JavaScript included on the page may execute in a different manner depending on the page state, input provided, and any other signals it receives, be it from the server or the client.
If you're using a server side language, you might choose to render a script conditionally, such as the first time a user logs in.
If you need to include the javascript irrespective of context, then you need to listen to other signals.
The simple modern solution is to make use of localStorage. localStorage can be used to store custom string values on custom key values for any given domain.
The code to make use of this would look like:
if (localStorage['...my key here...'] === '...my expected value here...') {
// The page has been visited before
} else {
// The page has not been visited before
// OR
// The user or script has cleared the localStorage value
}
localStorage['...my key here...'] = '...my expected value here...';
That's all well and good if you just need things to work on the client alone. Sometimes you might need the server to know whether or not the page has been visited before.
The (less)simple solution is to use document.cookie:
if (/(?:^|;\s*)...my key here...=...my expected value here...(?:;|$)/.test(document.cookie)) {
// the page has been visited before
} else {
// The page has not been visited before
// OR
// The user or script has cleared the cookie
}
document.cookie = '...my key here...=...my expected value here...';
If you need to defer the execution until the page has finished loading, then simply include the script in an onload callback, either by assigning the event to the window:
window.onload = function () {
// do stuff when the page has loaded
//this will override any other scripts that may have been assigned to .onload
};
or by binding the event handler to the window:
window.addEventListener('load', function () {
// do stuff when the page has loaded
}, false);
It depends on what first page load means to you. It's subjective.
If you want the function to fire once the DOM has been parsed, but only the HTML and no other external resources, bind it to the DOMContentLoaded event.
document.addEventListener('DOMContentLoaded', fn);
Otherwise, if you want to wait for external resources to be loaded and then fire the event, you should bind it to the window object's load event like so:
window.addEventListener('load', fn);
Here are some links from the Mozilla Developer Network that explain the what I just said in more detail:
https://developer.mozilla.org/en-US/docs/Web/Events/load
https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
Good luck!
I was facing something similar, the difference in my case was, I wanted to run a code whenever a new instance was being created I needed a certain code to execute, and then later for the rest of the reloads that code should not execute.
For that similar to localStorage solutions above use session storage instead:
fun_RunOnlyOnFirstPageLoad(){}
if(!$window.sessionStorage.getItem(hasRunBefore)){
fun_RunOnlyOnFirstPageLoad();
$window.sessionStorage.setItem(hasRunBefore, true);
}
using the window.sessionStorage instead stores the value only for that session.
this way once the tab is closed (session is over, that value is wiped out) and on every new instantiation, the code is executed.
I'd like to warn the user when he/she tries to navigate away from my webapp when his/her project is unsaved. Currently I don't care about edit boxes which are currently under edit. I know the project's dirty state from the session.
I quickly realized that if I set a function for the window.onbeforeunload, it'll disturb the user even if he/sh navigates to another page within my webapp. In such case no disturbance needed, the session will be still alive.
I tried a solution derived from Fire onbeforeunload only when window is closed (includes working example)
window.onbeforeunload = function (e) {
var targetHost = new URL(e.target.URL).hostname;
if (targetHost != window.location.host)
return "Project is unsaved!!!";
else
return null;
};
The new URL(e.target.URL).hostname results in (at least on IE11):
JavaScript runtime error: Object doesn't support this action
And mysteriously I can't find anything about no new URL(e.target.URL).hostname by any search.
But at the same time I hope this is not impossible, I cannot believe others didn't experience this.
There are two problems in your script:
window.onbeforeunload = function (e) {
var targetHost = new URL(e.target.URL).hostname;
if (targetHost != window.location.host) {
return "Project is unsaved!!!";
else
return null;
};
The first is that you did not close your if. The second is that e.target.URL is apparently not supported by IE. You need to have an A and a B plan.
A plan: Applies to the case when e.target.URL exists. In this case, you handle the case as you initially implemented, but with the syntax fix mentioned above.
B plan: Applies to the case when e.target.URL does not exist. For this case, you should assume that the user leaves the site. You can improve the approach by adding knowledge when the user uses your control to navigate, for instance, you know that a given anchor will stay on the site. In this case, you can define a click event and set a boolean value to true (and the default value of that boolean should be false...), so that later your onbeforeunload event will know that your user stayed on the site even if he or she uses IE.
So you need a logic like this:
var targetHost = !!e.target.URL ? (new URL(e.target.URL).hostname) : ((myFlag) ? (window.location.hostname) : ("elsewhere"));
So I have a simple tab system which I handle with the .load function to load the desired content. The problem is that the page itself which contains this tab system is a ajax loaded content. And for some reason the initial call of the tab function to display the initial tab content won't work. But after manually choosing a tab, the load function loads the content properly.
her some code to look at:
The tab handler:
function loadTab(tab) {
$(".tab_a:eq("+otab+")").removeClass("tab_slc");
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
at the end I call loadTab(tab); and the thing should be initialized. but for some reason the content remains empty. As soon as you manually click on a tab (I have an on click function which calls loadTab(tab) everything starts working)
Because the code by itself works, I think the problem is caused by the other script which handles the page itself. It is also a .load function which loads the page, which loads this tab system.
So do multiple .loads don't like each other? and if so, what can I change?
Thanks in advance ;)
EDIT: I could't post the entire code for some reason, but if you go here you can see the site in action with all the scripts:
n.ethz.ch/student/lukal/paint.net
The tab system is on the download page.
EDIT:-----------------------------------------------------------------------------------------------------------------------------
Big Update
So this is still the same issue but with a slight twist: I did what was recommended in the comments and put my secondary .load() call inside the success call of the first one.
$("#content").load("pages/contact #contentInside", function() {
$("#OtherContent").load("include/info #OtherContentInside");
});
So this works.
But now I had the great idea to make a giant load function. It is a slightly better function than just the plain load, cause it does some fading and stuff. But now I have the same problem, but even more complicated. I created the load function as a "plugin" so the function itself is in a different script file and therefore I can't access the inside of the success function. I solved this problem with a return $.ajax(); and a .done() call. The problem here is that there is some rare case where it just skips the secondary load function. So I am searching for a guaranteed way of controlling the order of the .load calls. Any idea?
The mock-up website is up to date with the new scripts if you wish to take a look. And people were complaining about potential virus spread from my link. For some reason I can't post long code snippets so the site is the best source I got to show everything. If you know a more trustworthy way to share my code please let me know.
We cannot see the rest of your code to tell where the initial call is being invoked from. A set up like the following should work:
$(function() {
var tab = 0;
loadTab( tab );
});
function loadTab(tab) {
//WHAT IS otab???
$(".tab_a:eq("+otab+")").removeClass("tab_slc"); //<<<==== otab
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
Update
The reason it does not work initial is because otab is not defined the first time the function is called. You have initialized otab at the end of the function but you are using it at the beginning of the function.
UPDATE 2
I have had a chance to look at your code and I just found out what the issues are:
You do not have DOM ready
You are not calling the function on page load.
The following version of your code should work -- try not to use global variable as you're doing with otab. Since you're loading this script at the end of the page (an you are using event delegation) you may get away with DOM ready. Adding .trigger('click') or click() as indicated below should resolve the issue.
//Tab-loader
//Haeri Studios
var tab = 0;
var otab = tab;
var counter = 0;
//click detect
$(document).on('click', '.tab_a', function() {
tab = counter == 0 ? tab : ($(this).attr('id'));
loadTab(tab);
counter++;
return false;
})
.trigger('click'); //<<<<<===== This will call the function when the page loads
//Tab setup
function loadTab(tab) {
//Content Setup
$(".tab_a:eq("+otab+")").removeClass("tab_slc");
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
//Initialize << WHAT ARE YOUR INTENTIONS HERE .. DO YOU REALLY NEED THIS PIECE?
$.ajax({success: function() {
loadTab(tab);
}});
A partial answer to this problem was to call the loadTab function inside the success call of the page load function, like charlietfl pointed out. But the problem is that there is no need to call the tabloader every time a new page gets called. So I would rather not have a rare call in every page setup function.
I am a bit disappointed by the system on stackoverflow. It seems like if you have not a high reputation level, no one gives a "S" about your questions. Well but at least some input was give, for which I am very thankful.
So by digging deeper into google I found out that the callback can be manually placed in the function where ever you like.
so if we have a function:
foo(lol, function() {
//This after
});
this does stuff after foo() is done. But what if we have another function inside foo() which we also need to wait for:
function foo(lol) {
bar(troll, function() {
//This first
});
}
The bar function is not relevant to the success call of foo. This causes the unpredictable outcome of calls.
The trick is to control when the success function of foo gets called.
If we add a parameter(callback) inside foo and call this "parameter" (callback();) inside the success call of bar, we can make sure the order is guaranteed.
And that's it:
function foo(lol, callback) {
bar(troll, function() {
//This first
callback(); //<-This callback placement defines when it should be triggered
});
}
foo(lol, function() {
//This after
});
We get:
//this first
//this after