I have read other postings that discuss ways that other technologies clean up the browser cookies when the browser is closed down, but none of them show how to get AngularJS to do this. How do I trigger a cookie removal method in AngularJS to run when the browser is closed?
In AngularJS 1.4.8, cookies can be removed with the syntax $cookies.remove('keyname'). I can write a method to remove all the cookies by name. But how do I make sure that the cookie removal method is called whenever the browser is closed? Is the syntax any different if I want the method to be called when a browser tab is closed?
ONGOING EFFORTS:
As per #User2341963's suggestion, I added cookie removal code to the run method in the main module of the app. The same exact code runs correctly when I put it in a logout() method elsewhere in the app, but when I put breakpoints in the firefox debugger and close the browser, I see that the cookie removal code is never run when the browser is closed. What specific changes do I make to the code to get the cookies to all be removed by AngularJS when the browser is closed?
Here is the code for the main module of the app, including the run() method:
angular
.module('hello', [ 'ngRoute', 'auth', 'home', 'secure', 'public1', 'navigation' ])
.config(
function($routeProvider, $httpProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {
templateUrl : 'js/home/home.html',
controller : 'home'
})//plus other routes
.otherwise('/');
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}
).run(['$cookies', '$window', '$log', function($cookies, auth, $window, $log) {
//other stuff
$window.onbeforeunload = function() {
// Clearing all cookies now!
$cookies.remove("AUTH1");
$cookies.remove("AUTH2");
$cookies.remove('procStep');
$cookies.remove('usrname');
$cookies.remove('exists');
$cookies.remove('wid');
};
}]);
If you need only cookies, because you need it to be sent to backend server, you can use the main controller's $destroy event.
Using $on.$destroy
This event called when the controller about to be destroyed.
Other option is to use angularjs sessionStorage
Session Storage removed when the window is closed automatically
Related
Would like to check what is the issue caused to my <a href> links when after I introduced an interceptor to my angular app and it has cause the links to not reload when it is on the same page? below is how i introduce my interceptor to add jwt's Authentication token to my web service requests header.
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(['$q', '$localStorage', '$location', function ($q, $localStorage, $location) {
return {
'request': function (config) {
config.headers = config.headers || {};
if ($localStorage.jwtToken) {
config.headers.Authorization = 'Bearer ' + $localStorage.jwtToken;
}
return config;
},
'responseError': function (response) {
if (response.status === 401 || response.status === 403) {
}
return $q.reject(response);
}
};
}]);
}]);
Noted that the presentation site and the business logic processing part are both independently separated and do not rely on each other. Which means that the presentation site is responsible to load the javascripts and HTML codes while the scripts are the one that is responsible to request data from the server. Authentication are done via JWT therefore I use the interceptor to inject the JWT related headers to every RESTful requests.
PHP => (renders HTML template) + (data from Angular) <= Angular => (send request to server get data)
Using the code above i was able to complete the JWT authentication but it causes all the <a href> links on the presentation page to not reload if it is in the same page. For example I have 3 items in my menu (Home, Page1, Page2). When I'm in Home and if I click on the Home link, it suppose to reload the page (like F5) but nothing happens. I would need to navigate away from the page then only i am able to click on the link.
What have i done wrong here?
Update 1: Question from #Sanjeev: How are you handling routing in you app, are you using ng-router module or custom ui-router module ? Can you add the routing code as well.
Noted that at this moment all routes are within the HTML itself using links. The javascripts do not handle any routes. Its responsibility is to GET and POST data.
Update 2: Added plunker link. Note that i would suggest you to try both commenting and uncommenting the entire interceptor section to see the difference when clicking the link. Follow these instruction below and you will recreate the scenario i mentioned.
Load and run the plunker file
On the top right corner, click on "Launch the preview in a separate window"
Copy the URL in the window and replace it in the section in line 25. The url should look something like run.plnkr.co/somerandomkeys+
Close the separate window and try clicking the link in the menubar.
When commenting the said section, notices that the page will load (acts as a refresh) but when you uncomment the section the link will not work anymore. Some sort of same page detection thing is blocking the action.
Solution :
Analysis: I ran your demo and understood the issue you were highlighting, actually the issue is not related to interceptors at all. Interceptors get called only when you make http requests using $http service.
In Angular apps the anchor tag behavior changes the moment you inject '$location' service in your app, you have injected '$location' service in your interceptor module (although i don't see it being used). So this solves the mystery why you start getting anchor issue when you add interceptor :)
In your example the anchor has same link as the current location so Angular is preventing the default behavior of anchor tag and clicking anchor does not reload your page.
You can solve it be multiple ways:
Don't inject '$location' service if you are not using it, if you can't remove it then go for solution 2 or 3.
Add attribute target="_self" or target="_blank" as per your case, this will solve your issue without requiring any Js code change. I tested this fix with your code and it worked for me.
Add a ng-click handler on anchors and change window.location in
it, or better create a directive for anchors and check if href is
same as current location then force page reload using location.reload()
If you decide to use angular routing which is great feature of Angular JS then use $route.reload() method
I am trying to integrate adal.js in my application. Below is my Code. Could someon please let me know why the authentication is not triggered.
var app = angular.module('TestWebApp', [
'ngRoute',
'testControllers',
'testServices',
'datatables',
'AdalAngular'
]);
/**
* Configure the Routes
*/
app.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', function ($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
// Home
.when("/dashboard", {templateUrl: "partials/package.html", controller: "searchCtrl",requireADLogin: true})
// else 404
.otherwise("/404", {templateUrl: "partials/404.html", controller: "searchCtrl"});
adalAuthenticationServiceProvider.init(
{
tenant: 'test.onmicrosoft.com',
clientId: '23234sf-sdf-------'
},
$httpProvider
);
}]);
And my page url is something link this.
http://localhost:8081/test-ui/#/dashboard
This should go to the Azure login page but its not going.
I think that your tenantID is probably right - it needs to be whatever your AD portal had for the app id URI(minus the name of the application) inside your azure portal. I would also look at the manifest - This is a pretty big deal to have changed. The steps are on the page Omar linked. The SinglePageApp example is a great resource in starting out, and the example is clean, if maybe a bit trivial for the adal portion. You should also check outvittorio's excellent deep dive for a good explanation and great review of adal.js
For me the problem was that I had a login button with a form that was redirecting because of action="#" and so the Azure redirecting was not working.
I just removed the whole action-attribute and it worked
Your issue: I think you have an issue with the link. Your link should be like this:http://localhost:8081/test-ui#/dashboard
Home
ToDo List
Contact List
Adal.Js uses requireADLogin keyword to interrupt the route change event. If you don't see any redirection, it could be related to the routechange event not firing. I suggest to add different routes to your app to verify angular routes first.
General guideline: You can try the sample app first to see if your configurations is working.
Sample app:https://github.com/AzureADSamples/SinglePageApp-DotNet
If you have an issue with config, you can follow the steps in the ReadMe file to setup your app config.
If sample app works for your config and your app is having issues, you can see the calls with Fiddler and further debug into adal.js as well. Login redirect event also broadcasts adal:loginRedirect.
Im working on porting a project over to an angular based SPA. Its currently a more "traditional" node/locomotivejs app that serves up templates from the server side (never known the proper term for this).
The projects too large to to migrate all at once, so we are converting it to angular a page at a time.
My problem: if you load the angular part of the app, everything works fine. You can go to the angular routes correctly. However if you then go to a non-angular route (that should be handled serverside), then nothing loads (the ng-view goes blank, rather than a whole new template being loaded up). If you go to a serverside route first or hit refresh, the page loads correctly.
My guess is that angular is trying to handle these routes, and i am unsure how to get it to let the server take back over.
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/something/page1', {
templateUrl: '/page1.html',
controller: 'page1Ctrl'
});
$routeProvider.when('/something/page1/subpage', {
templateUrl: '/subpage.html',
controller: 'subpageCtrl'
});
}]);
this is my angular routeProvider. No "otherwise" specified. Serverside I have something like:
this.match( '/someOtherPage', 'someOtherPage#showstuff');
If i go to /someOtherPage directly, it loads correctly from the serverside. If i go to /something/page1, then go to /someOtherPage, it does not seem to contact the server.
Because you are using angular html 5 mode angular cannot tell the difference between a route that you want angular to handle, and one you don't. I think you need to tell angular to ignore certain routes. Looks like this is what you are looking for:
https://docs.angularjs.org/guide/$location#html-link-rewriting
So change your links to non-angular pages to use a target.
ex. link
Situation
I scaffolded the application using a yeoman generator angularjs, thus providing me a clean directory structure and framework structure. I'm just learning angularjs and using yeoman is a cleaner way to do things, and soc is pretty much achieved.
Now I am having a problem on where to put things, like services and factories, I am currently dealing with user authentication right now.
Basically
I have two routes as of the moment, one is / and one is /secured. I set this up so that I can really get started playing with authentication.
I found this article about authenticating a user. And I found it really interesting, and I thought I could somehow understand it, and really I understood it, but I think I fail to understand some of the basics of how should factories and services should be used.
What I currently have
First, I do not have a backend service, because the app fails right on and writing a service won't be necessary right now, I can just write a simple php script that returns 401 status or 200, but that wont be necessary right now, I want to work on the client side first.
In my app.coffee file
angular.module('myApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute'
])
.config ($routeProvider) ->
$routeProvider
.when '/',
templateUrl: 'views/home.html'
controller: 'HomeCtrl'
.when '/secured',
templateUrl: 'views/secured.html'
controller: 'SecuredCtrl'
resolve:
loggedin: checkLoggedin // this is the wrong one
.otherwise
redirectTo: '/'
.config ($httpProvider) ->
$httpProvider.interceptors.push('httpInterceptor')
In the article, it says create a function that checks if the user is loggedin or not, and in his codebase he made that function inside the config method. And btw, he ain't using yeoman so that pretty much complicate things to me.
I thought that checking loggedin status is a factory or a service's job, so I thought of writing a factor for that like so
angular.module('myApp')
.factory 'checkLoggedin', ($q, $timeout, $http, $location, $rootScope) ->
deferred = $q.defer()
$http.get('api/loggedin').success (user) ->
if user isnt '0'
$timeout(deferred.resolve, 0)
else
$rootScope.message = 'You need to be logged in.'
$timeout(
() -> deferred.reject(),
0
)
$location.url '/login'
What is the proper way of doing this?
You can use the awesome angular-http-auth module - https://github.com/witoldsz/angular-http-auth
All you need to do is listen for the event 'auth-loginRequired' at the app controller level and then show the login screen.
What i have done (not the best way) is listen for this event in my app controller and redirect to login route -
$scope.$on('event:auth-loginRequired', function() {
$location.path('/login');
});
I am developing an application in Angular that makes many calls to a couple of webservices. I want to develop an offline component to the app such that it will cache certain webservice results to LocalStorage and use them when the connection is offline.
That part is fairly simple, and the part I am having the most trouble with is how to branch logically when the app is offline.
Here is my current flow:
User loads the page
Webservice calls happen as usual
$http interceptor looks for a 404 error and marks a $rootScope.isOnline boolean flag to indicate we are offline, otherwise if no 404 then we mark as online
I want my code to branch depending on this flag, in a way that is maintainable. As such, I was looking at using dependency injection to inject either an 'online' service which makes calls to the webservice, or an 'offline' service, which interfaces with the LocalStorage results if they exist.
Can I base my dependency injection based off the online/offline flag to inject the correct service like so?
.factory('AuthService', ['$rootScope', '$injector', function($rootScope, $injector) {
if($rootScope.isOnline) {
return $injector.get('OnlineAuthService');
}
else {
return $injector.get('OfflineAuthService');
}
}])
.service('OnlineAuthService', ['$rootScope', '$http', '$location', 'serviceEndpoint', 'securityEndpoint', 'organisationId', function ($rootScope, $http, $location, serviceEndpoint, securityEndpoint, organisationId) {
this.ensureSession = function (data) {
// Do some connection to the webservice
};
}])
.service('OfflineAuthService', ['$rootScope', function ($rootScope) {
this.ensureSession = function (data) {
// Do some LocalStorage stuff
};
}])
AuthService.ensureSession(data);
The issue I am having is that $rootScope.isOnline is not marked as offline before the first call to my webservice, so even when the connection is offline, the dependency injection looks at $rootScope.isOnline and injects the OnlineAuthService.
Is this the correct way of developing an Online/Offline app in Angular or is there a better way?
There are many ways to do this. Take a look at this simplified plunker:
http://plnkr.co/edit/CpiJaF480Mai050b63RC?p=preview
The idea there is that you have the singleton service object returned by the service, but then you copy over different function methods as online/offline changes.
There are many other ways to do this, such as adding levels of indirection (creating a service from which you can request the current active service for example).
You can not simply do it with angular injection, because both factory and service calls will get called once and only once and will simply register a single instance for the service name (they will not get called every time you inject).