Show user info after login AngularJS - angularjs

I am building an AngularJS application and I would like to show user info in the header of the page when user is logged in. Something like "Welcome,{{username}}".
I have created a SessionHandler service where I keep the information about the user.
module Shared{
export class SessionHandler implements ISessionHandler {
loggedUser: string;
SetLoggedUser(user: string) {
this.loggedUser = user;
}
GetLoggedUser(): string {
return this.loggedUser;
}
}
}
angular.module("MainApp").service("SessionHandler", Shared.SessionHandler);
I have tried creating a directive
angular.module("MainApp").directive("myheader", function () {
return {
restrict: "E",
templateUrl: "/app/shared/partials/header.html",
controller: function ($scope, SessionHandler: Shared.ISessionHandler) {
$scope.LoggedUser = function () {
SessionHandler.GetLoggedUser();
}
}
}
});
my header template is simple..
<span>{{LoggedUser()}}</span>
my main app module looks like this
angular.module("MainApp", ['ui.bootstrap','Login', 'Module2'])
...
.run(($rootScope: ng.IRootScopeService, $location: ng.ILocationService, SessionHandler: Shared.ISessionHandler) => {
$rootScope.$on("$routeChangeStart", (event, next, current) => {
if (!SessionHandler.IsUserLoggedIn()) {
$location.path("/login");
}
if (next.templateUrl == "/app/module/login/partials/login.html") {
SessionHandler.ClearSession();
return;
}
});
});
and my login Controller which is in Login module calls a method on the server and after successful login sets logged user by calling SetLoggedUser() method on SessionHandler.
my index
<html data-ng-app="MainApp">
<head>
</head>
<body>
<div class="container-fluid">
<myheader></myheader>
<div data-ng-view=""></div>
</div>
</body>
</html>
When I start my application logged user is empty so I only see Welcome (I will fix this later) but after I login I am redirected to another partial view but nothing changes in the header.
I am obviously missing something but I just can't figure out what.
Thank you.

In your header directive you are missing the dependency or you can say reference to your SessionHandler service.
Can you try to add that and check what happens next..

Related

How to call an Angular method when browser closes

I'm relatively new in AngularJS and I have been asked to modify our application so that when the user closes the browser, we also log them out of Auth0 service.
I have the code below but I can't get it to fire. Kindly help.
$rootScope.$on("$destroy", function() {
lockService.logout();
});
lockService.logout() holds the functionality that successfully logs out the user when they click logout button, including the Auth0 signout function.
Somewhere I read that I can use the $on.$destroy of the main Controller but I am not sure how to do this. The above code is in fact inside the mainController function but it still doesn't work.
This is where I found my answer: https://stackoverflow.com/a/36444134/1168597
You can add a directive like this to your body element.
<body body-unload></body>
app.directive('bodyUnload', [
function () {
return {
restrict: 'A',
replace: false,
link: function (scope, element) {
function cleanupApp(){
// do cleanup here
}
element[0].onbeforeunload = function() {
cleanupApp();
};
}
};
}
]);
I have found a much cleaner and more effective approach.
$rootScope.$on('$locationChangeStart', function () {
if (!($window.performance.navigation.type === 1) && lockService.isAuthenticated()) {
$rootScope.logout();
}
});
So here if window.performance.navigation.type is 1, it means the page was refresh-ed. What I do then is if it is not refreshed AND my lockService.isAuthenticated() returns true, I logout the user.

Angular controller not updating view without page refresh

I have a problem with my login routine using passport js, express and angular. While the login as such works smoothly I want to use the window object to store the current logged in user for authentication purposes.
After username and password are validated using passport I am using the successRedirect, that triggers index.render
// Set up the 'signin' routes
app.route('/signin')
.post(passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/signin',
failureFlash: true
}));
exports.render = function(req, res) {
// Use the 'response' object to render the 'index' view with a 'title' and a stringified 'user' properties
console.log('Window Object:',req.user);
res.render('index', {
title: 'Test',
user: JSON.stringify(req.user)
});
};
Once I login to the application the window.user object turns from null to the following (index.ejs).
<!-- Render AngularJS views -->
<header data-ng-include="'header.client.view.html'"></header>
<section data-ui-view></section>
<div id="contactform" data-ng-include="'contactform.client.view.html'"></div>
<footer data-ng-include="'footer.client.view.html'"></footer>
<!-- Render the user object -->
<script type="text/javascript">
window.user = {"_id":"55cb06e523d7d6680d14c215","provider":"local","firstName":"Karl","lastName":"Karl","email":"karl.karl#test.com","__v":0,"created":"2015-08-12T08:42:13.807Z","active":true,"fullName":"Karl Karl","id":"55cb06e523d7d6680d14c215"};
</script>
Now I have been very unsuccessful in retrieving the window.user object in my angular controller as it is seen as null as long as I haven't done a page refresh.
<!-- Render the user object -->
<script type="text/javascript">
window.user = null;
</script>
So the question is now, how do I retrieve the window object, as I want to update my angular view accordingly, showing in the header that the user has been logged in?
I think you are meant to use a Angular service to store variables that are to be shared amongst controllers. Sorry, I guess I'm trying to nudge you out of your preferred solution to use the Window object to store state.
Some sample code from a common services module
//var com_serv = angular.module('common.services')
com_serv.service('sharedProperties', function () {
var property = 'Not yet set';
return {
getProperty: function () {
return property;
},
setProperty: function (value) {
property = value;
}
};
});
Related SO questions
how-can-i-pass-variables-between-controllers
how-to-pass-dynamic-data-from-one-module-controller-to-another-module-controller

AngularJS warning when leaving page

I know there are a few posts similar to this but I simply can't get any of them to work for me as intended. I'm trying to setup an event handler to listen to a location change on a specific scope. The code looks like this:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="verifyViewChange">
Test
</div>
<script>
var app = angular.module('myApp', []);
app.controller('verifyViewChange', function ($location, $scope) {
$scope.$on('$locationChangeStart', function (event) {
event.preventDefault();
alert("I'm preventing you from leaving the page");
});
});
</script>
</body>
</html>
When I load the page I get the warning, but not when clicking on the link. What do I need to do to make it work?
You should use the native 'beforeunload' event by adding it to the window.
Below is an example:
$scope.addUnloadEvent = function(){
if (window.addEventListener) {
window.addEventListener("beforeunload", handleUnloadEvent);
} else {
//For IE browsers
window.attachEvent("onbeforeunload", handleUnloadEvent);
}
}
function handleUnloadEvent(event) {
event.returnValue = "Your warning text";
};
//Call this when you want to remove the event, example, if users fills necessary info
$scope.removeUnloadEvent = function(){
if (window.removeEventListener) {
window.removeEventListener("beforeunload", handleUnloadEvent);
} else {
window.detachEvent("onbeforeunload", handleUnloadEvent);
}
}
//You could add the event when validating a form, for example
$scope.validateForm = function(){
if($scope.yourform.$invalid){
$scope.addUnloadEvent();
return;
}
else{
$scope.removeUnloadEvent();
}
}
The [fix above] is great just added this bit to the handleUnloadEvent...
function handleUnloadEvent(event) {
/*
This bit here theres another bind that says if this fields initial value is
not the same as its current value add the class of input-changed if it is
remove the class...so you can flag any page ya want to prompt a dialog based
on presence of a class on an input.
*/
var changeCheckCount = $('.input-changed').length;
console.log('changeCheckCount',changeCheckCount);
if(changeCheckCount === 0)
{
$scope.removeUnloadEvent();
}
else if(changeCheckCount > 0)
{
event.returnValue = "You have Unsaved changes do you really want to leave?";
}
Allows you to say if you want dialog to reload or leave page with just a class...Suppose you could bind the dialog too by finding the first one with .input-changed and have a data attribute on it with the message to show as the dialog.
Below is working example , it may helpful to you:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<div ng-app="myApp" ng-controller="verifyViewChange">
Test
</div>
<script>
var app = angular.module('myApp', []);
app.controller('verifyViewChange', function ($location, $scope) {
$scope.funConfirm = function () {
var retVal = confirm("I'm preventing you from leaving the page");
if (retVal == true) {
return false;
} else {
event.preventDefault();
return false;
}
}
});
</script>

Why will my twitter widget not render if i change the view in angularjs?

Hi and thanks for reading.
I have a angular app im making and ive stumbled on a problem. set up as so
index.html-
<html ng-app="myApp">
...
<div ng-view></div>
<div ng-include="'footer.html'"></div>
...
</html>
I wont bother putting my routes its pretty simple /home is shows the /home/index.html and so on...
/home/index.html (default view when you come to the site)
<div class="responsive-block1">
<div class="tweet-me">
<h1> tweet me </h1>
</div>
<div class="twitter-box">
<twitter-timeline></twitter-timeline>
</div>
twitter timeline directive
directives.directive("twitterTimeline", function() {
return {
restrict: 'E',
template: '<a class="twitter-timeline" href="https://twitter.com/NAME" data-widget-id="XXXXXXXXXXXXXX">Tweets by #NAME</a>',
link: function(scope, element, attrs) {
function run(){
(!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"));
console.log('run script');
};
run();
}
};
});
So I have just created a basic twitter directive using the tag from twitter. But when I change the view example to /blog then go back to /home the twitter widget no longer renders at all.
Im also using an $anchorScroll and if i jump to anyway on the page with this the widget also disappears. Any info would be great thanks.
See this post: https://dev.twitter.com/discussions/890
I think that you may be able to get the widget to re-render by calling
twttr.widgets.load().
If you find that this does not work, you will need to wrap this code into $timeout in your controller:
controller('MyCtrl1', ['$scope', '$timeout', function ($scope, $timeout) {
$timeout = twttr.widgets.load();
}])
To build on Sir l33tname's answer:
In services declaration:
angular.module('app.services', []).
service('tweetWidgets', function() {
this.loadAllWidgets = function() {
/* widgets loader code you get when
* declaring you widget with Twitter
* this code is the same for all widgets
* so calling it once will reference whatever
* widgets are active in the current ng-view */
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
};
this.destroyAllWidgets = function() {
var $ = function (id) { return document.getElementById(id); };
var twitter = $('twitter-wjs');
if (twitter != null)
twitter.remove();
};
});
Then in controller declarations:
angular.module('app.controllers', []).
controller('view_1_Controller', tweetWidgets) {
// load them all
tweetWidgets.loadAllWidgets();
}).
controller('view_2_Controller', tweetWidgets) {
// now destroy them :>
tweetWidgets.destroyAllWidgets();
});
Now whenever you leave view #1 to go to view #2, your controller for view #2 will remove the widgets associated with view #1 and when you return to view #1 the widgets will be re-instatiated.
The problem is because when Angular switches views the script tag that was originally inserted is not removed from the document. I fixed this on my own website by removing the Twitter script element whenever my Twitter timeline directive is not in the view. See the code below with comments.
function (scope, el, attrs) {
el.bind('$destroy', function() {
var twitterScriptEl = angular.element('#twitter-wjs');
twitterScriptEl.remove();
});
// function provided by Twitter that's been formatted for easier reading
function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';
// If the Twitter script element is already on the document this will not get called. On a regular webpage that gets reloaded this isn't a problem. Angular views are loaded dynamically.
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
js.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");
}
Basically it's what Loc Nguyen say.
So every time you recreate it you must remove it first.
var $ = function (id) { return document.getElementById(id); };
function loadTwitter() {!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");}
var twitter = $('twitter-wjs');
twitter.remove();
loadTwitter();
Answer by #b1r3k works without problems :
put this in your controller:
$timeout(function () { twttr.widgets.load(); }, 500);
For those trying to load twttr.widgets.load() inside their controller, you will most likely get an error that twttr is not defined AT SOME POINT in your UX, because the async call to load the twitter script may not be completed by the time you controller instantiates and references twttr.
So I created this TwitterService
.factory('TwitterService', ['$timeout', function ($timeout) {
return {
load: function () {
if (typeof twttr === 'undefined') {
(function() {
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
})();
} else {
$timeout = twttr.widgets.load();
};
}
}
}])
and then call TwitterService.load() inside the controllers that require your widgets. This worked pretty well. It basically just checks if the twttw object exists and if it does, just reload the script... otherwise just reload the script.
Not sure if this is the best implementation, but it seems like all other solutions have edge cases where it will throw an error. I have yet to find one with this alternative.

how to implement google+ sign-in with angularjs

I have an AngularJS app, and I want to implement G+ sign-in. I've gone through their samples, and they work as standalone apps.
https://developers.google.com/+/web/signin/
In my Angular app, I am able to display the G+ sign-in button. But I'm stuck on the callback. Do I put the callback function in my controller js file?
If so, and given this controller:
app.controller('myController', function ($scope) {
function signinCallback(authResult) {
On my data-callback, how do I name it so that it goes to signinCallback inside myController?
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="123456789.apps.googleusercontent.com"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.profile.emails.read"
</span>
</span>
The Google+ PhotoHunt sample app demonstrates an AngularJS integration with Google+. The sample is available in Ruby, Java, Python, and C#/.NET for web.
Of note should be the following code in the AngularJS front-end:
Markup to render the button in:
<span id="signin" ng-show="immediateFailed">
<span id="myGsignin"></span>
</span>
JavaScript to glue the markup to code:
$scope.signIn = function(authResult) {
$scope.$apply(function() {
$scope.processAuth(authResult);
});
}
$scope.processAuth = function(authResult) {
$scope.immediateFailed = true;
if ($scope.isSignedIn) {
return 0;
}
if (authResult['access_token']) {
$scope.immediateFailed = false;
// Successfully authorized, create session
PhotoHuntApi.signIn(authResult).then(function(response) {
$scope.signedIn(response.data);
});
} else if (authResult['error']) {
if (authResult['error'] == 'immediate_failed') {
$scope.immediateFailed = true;
} else {
console.log('Error:' + authResult['error']);
}
}
}
$scope.renderSignIn = function() {
gapi.signin.render('myGsignin', {
'callback': $scope.signIn,
'clientid': Conf.clientId,
'requestvisibleactions': Conf.requestvisibleactions,
'scope': Conf.scopes,
'apppackagename': 'your.photohunt.android.package.name',
'theme': 'dark',
'cookiepolicy': Conf.cookiepolicy,
'accesstype': 'offline'
});
}
Within processAuth, you should see an access token and can update your UI to reflect this. You can also see the full controller's JavaScript code on GitHub.
I am not sure if this works, but I would try it like this:
module.factory("GPlusAuthService", function ($q, $window) {
var signIn;
signIn = function () {
var defered = $q.defer();
$window.signinCallback = function (response) {
$window.signinCallback = undefined;
defered.resolve(response);
};
gapi.auth.signIn({
clientid: "123456789.apps.googleusercontent.com"
cookiepolicy: "single_host_origin"
requestvisibleactions: "http://schemas.google.com/AddActivity"
scope: "https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.profile.emails.read",
callback: "signinCallback"
})
return defered.promise;
};
return {
signIn: signIn;
}
});
module.controller('myController', function ($scope, GPlusAuthService) {
$scope.signIn = function() {
GPlusAuthService.signIn().then(function(response) {
});
}
});
<span id="signinButton" ng-controller="myController">
<span class="g-signin" ng-click="signIn()"></span>
</span>
Function that is going to be called after user agrees to sign in is specified in data-callback, this function needs to be globally accessible, that is bound to window object.
Accessing global object from controller is an anti-pattern, as a middle ground you can use $window provided by Angular, which you can mock in your tests

Resources