Last active
March 3, 2016 04:26
-
-
Save katowulf/7328023 to your computer and use it in GitHub Desktop.
A service and several directives based on ng-cloak, which wait for angularFire to finish authenticating--rather than just until Angular bootstraps--before displaying it's content. Handy to get rid of those blips that display when user isn't logged in--oh, wait, now they are.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
/** | |
* This module monitors angularFire's authentication and performs actions based on authentication state. | |
* directives/directive.ngcloakauth.js depends on this file | |
* | |
* Modify ng-cloak to hide content until FirebaseSimpleLogin resolves. Also | |
* provides ng-show-auth methods for displaying content only when certain login | |
* states are active. | |
* | |
* Just like other ng-cloak ops, this works best if you put the following into your CSS: | |
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { | |
display: none !important; | |
} | |
* | |
* See usage examples here: https://gist.github.com/katowulf/7328023 | |
*/ | |
angular.module('simpleLoginTools', []) | |
/** | |
* A service that returns a promise object, which is resolved once $firebaseSimpleLogin | |
* is initialized. | |
* | |
* <code> | |
* function(waitForAuth) { | |
* waitForAuth.then(function() { | |
* console.log('auth initialized'); | |
* }); | |
* } | |
* </code> | |
*/ | |
.service('waitForAuth', function($rootScope, $q, $timeout) { | |
function fn(err) { | |
if($rootScope.auth) { | |
$rootScope.auth.error = err instanceof Error? err.toString() : null; | |
} | |
for(var i=0; i < subs.length; i++) { subs[i](); } | |
$timeout(function() { | |
// force $scope.$apply to be re-run after login resolves | |
def.resolve(); | |
}); | |
} | |
var def = $q.defer(), subs = []; | |
subs.push($rootScope.$on('$firebaseSimpleLogin:login', fn)); | |
subs.push($rootScope.$on('$firebaseSimpleLogin:logout', fn)); | |
subs.push($rootScope.$on('$firebaseSimpleLogin:error', fn)); | |
return def.promise; | |
}) | |
/** | |
* A directive that wraps ng-cloak so that, instead of simply waiting for Angular to compile, it waits until | |
* waitForAuth resolves (in other words, until the user's login status resolves via Firebase) | |
* | |
* <code> | |
* <div ng-cloak>Authentication has resolved.</div> | |
* </code> | |
*/ | |
.config(function($provide) { | |
// adapt ng-cloak to wait for auth before it does its magic | |
$provide.decorator('ngCloakDirective', function($delegate, waitForAuth) { | |
var directive = $delegate[0]; | |
// make a copy of the old directive | |
var _compile = directive.compile; | |
directive.compile = function(element, attr) { | |
waitForAuth.then(function() { | |
// after auth, run the original ng-cloak directive | |
_compile.call(directive, element, attr); | |
}); | |
}; | |
// return the modified directive | |
return $delegate; | |
}); | |
}) | |
/** | |
* A directive that shows elements only when the given authentication state is in effect | |
* | |
* <code> | |
* <div ng-show-auth="login">{{auth.user.id}} is logged in</div> | |
* <div ng-show-auth="logout">Logged out</div> | |
* <div ng-show-auth="error">An error occurred: {{auth.error}}</div> | |
* <div ng-show-auth="logout,error">This appears for logout or for error condition!</div> | |
* </code> | |
*/ | |
.directive('ngShowAuth', function ($rootScope) { | |
var loginState = 'logout'; | |
$rootScope.$on('$firebaseSimpleLogin:login', function() { loginState = 'login'; }); | |
$rootScope.$on('$firebaseSimpleLogin:logout', function() { loginState = 'logout'; }); | |
$rootScope.$on('$firebaseSimpleLogin:error', function() { loginState = 'error'; }); | |
function getExpectedState(scope, attr) { | |
var expState = scope.$eval(attr); | |
if( typeof(expState) !== 'string' && !angular.isArray(expState) ) { | |
expState = attr; | |
} | |
if( typeof(expState) === 'string' ) { | |
expState = expState.split(','); | |
} | |
return expState; | |
} | |
function inList(needle, list) { | |
var res = false; | |
angular.forEach(list, function(x) { | |
if( x === needle ) { | |
res = true; | |
return true; | |
} | |
return false; | |
}); | |
return res; | |
} | |
function assertValidStates(states) { | |
if( !states.length ) { | |
throw new Error('ng-show-auth directive must be login, logout, or error (you may use a comma-separated list)'); | |
} | |
angular.forEach(states, function(s) { | |
if( !inList(s, ['login', 'logout', 'error']) ) { | |
throw new Error('Invalid state "'+s+'" for ng-show-auth directive, must be one of login, logout, or error'); | |
} | |
}); | |
return true; | |
} | |
return { | |
restrict: 'A', | |
link: function(scope, el, attr) { | |
var expState = getExpectedState(scope, attr.ngShowAuth); | |
assertValidStates(expState); | |
function fn() { | |
var show = inList(loginState, expState); | |
// sometimes if ngCloak exists on same element, they argue, so make sure that | |
// this one always runs last for reliability | |
setTimeout(function() { | |
el.toggleClass('ng-cloak', !show); | |
}, 0); | |
} | |
fn(); | |
$rootScope.$on('$firebaseSimpleLogin:login', fn); | |
$rootScope.$on('$firebaseSimpleLogin:logout', fn); | |
$rootScope.$on('$firebaseSimpleLogin:error', fn); | |
} | |
}; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style> | |
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { | |
display: none !important; | |
} | |
</style> | |
<script> | |
// include the waitForAuth module as a dependency | |
angular.module('myApp', ['simpleLoginTools']) | |
// you can use waitForAuth directly from your scripts | |
.controller('myController', function(waitForAuth) { | |
waitForAuth.then(function() { | |
/* do something after auth completes */ | |
}) | |
}) | |
</script> | |
<!-- ng-cloak now waits for simple login to resolve --> | |
<div ng-cloak>Authentication has resolved.</div> | |
<!-- using a string --> | |
<div ng-show-auth="'login'">{{auth.user.id}} is logged in</div> | |
<!-- using a $scope variable which equals 'login', 'logout', 'error', or an array of those --> | |
<div ng-show-auth="variable_name">Logged out</div> | |
<div ng-show-auth="'error'">An error occurred: {{auth.error}}</div> | |
<!-- using an array --> | |
<div ng-show-auth="['logout','error']">This appears for logout or for error condition!</div> |
waitForAuth is 'undefined' when I try calling it from my controller, even though I have included 'simpleLoginTools' as a dependency for my app module.
Any ideas?
Hi, I'm having issues with this module and ui-router. I have view setup like this:
app.config(['$stateProvider',
function($stateProvider) {
$stateProvider.state('home', {
url: '/home',
views: {
"main": {
controller: 'HomeController',
templateUrl: 'home/home.tpl.html'
},
"top-nav": {
controller: 'HomeController',
templateUrl: 'home/home-topNav.tpl.html'
}
},
data: {
pageTitle: 'Home'
},
resolve: {
currentUser: ["simpleLogin",
function(simpleLogin) {
// $getCurrentUser returns a promise so the resolve waits for it to complete
return simpleLogin.getCurrentUser();
}
]
}
});
}
]);
My top-nav template looks like this:
<nav class="navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" ui-sref="home">Home</a>
</div>
<div class="control-group nav navbar-nav navbar-right">
<button class="btn btn-default navbar-btn" ng-cloak ng-show-auth="['logout','error']" ui-sref="login">Log In</button>
<button class="btn btn-default navbar-btn" ng-cloak ng-show-auth="login" ng-controller="LoginController" ng-click="logout()">Log Out</button>
<button class="btn btn-default navbar-btn" ng-cloak ng-show-auth="login" ng-click="createStory()">Create Story</button>
</div>
</div>
</nav>
The problem is that ng-show-auth directives are not working. They resolve to 'logout' state even though the user is logged in. When I remove 'resolve' from my route state config, directives work fine. Can you help please?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I solve this calling waitForAuth service into app run()