-
-
Save katowulf/7328023 to your computer and use it in GitHub Desktop.
'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); | |
} | |
}; | |
}); |
<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> |
Seriously. Killer. Thanks!
Upgraded this to work a bit more reliably. Is also now a drag-and-drop plugin suitable for angularFire, angularFire-seed, and generator-angularfire installs.
Sometimes $firebaseSimpleLogin:logout fires before waitForAuth being registered then that promise is never resolved and ng-show-auth is never showed.
@katowulf do u know what could be the problem?
I solve this calling waitForAuth service into app run()
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?
Updated for AngularFire 0.6 (releasing this coming week)