问题
How to create own service accessible globally that will call server by currentUser()
only once on page load, if User is logged in then keep it and provide data to controllers or states until logout?
Now I'm resolving many times in many states or controllers currentUser()
, I found in docs: authparseresponse that it is possibility to make own service, but I don't know how to handle this in right way.
e.g in ui-router I've two server calls, would be sufficient only when page is loaded:
.state('login', {
url: '/login',
templateUrl: 'auth/_login.html',
controller: 'AuthCtrl',
onEnter: [
'$state', 'Auth',
function($state, Auth) {
Auth.currentUser().then(function() {
$state.go('home');
});
}
]
})
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard.html',
onEnter: [
'$state', 'Auth',
function($state, Auth) {
Auth.currentUser().then(function(user) {}, function(err) {
$state.go('home');// user not logged in
});
}
]
})
or each time next server call in NavController:
app.controller('NavController', [
'$state', '$scope', 'Auth', function($state, $scope, Auth) {
$scope.signedIn = Auth.isAuthenticated;
$scope.logout = Auth.logout; // logout function
Auth.currentUser().then(function(user) {
$scope.user = user;
});
$scope.$on('devise:login', function(e, user) {
$scope.user = user;
});
$scope.$on('devise:logout', function(e, user) {
$scope.user = {};
$state.go("home");
});
}
]);
I found similar question without answer: stackoverflow
回答1:
As we know factory is a singleton object and basically it is used for data sharing. We can inject it to any controller and functions as an dependency .So this may be also solve your problem. inject this service to your state resolve block and check if user exists or not if not then call the fetch user function of factory
app.factory("User", function($http, $q, $rootScope) {
var user = null;
return {
fetchUser: function() {
/*your ajax call that return current user*/
user = YOUR_SERVER_RESPONSE;
return user;
},
getUser : function(){
return user;
},
setUser : function(u){
user = u;
}
}
});
and your state config block is look like
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard.html',
resolve : function(User){
if(User.getUser()){
return User.getUser();
}else{
return User.fetchUser();
}
}
})
回答2:
I've had to deal with user Authentication in a large Angular app and found this article very useful: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec
Specific to 'how to make it happen only once':
- you can call the init() of the Service in an app.run block
- adding it as a
resolve
requirement of an outer state for the app (if you use nested states)
A UserService
Service to represent user's logged in state works well for sharing state throughout the application. The login and logout functions can be exposed through this service and the user's details can be stored here too. Defining a UserApi
that extends $resource
will probably help too to separate the actual HTTP concerns from managing user state.
Other concerns that will catch you, both of which are covered in the article: persistent state across page refreshes, and having areas of the site that are only accessible when the user is logged in.
回答3:
I solved the issue of current_user information with angular by adding the following JavaScript (jQuery) code to the bottom of my main HTML page:
$("meta[name=current_user]").data('current_user', {
id: <%=j current_user.id.to_s %>,
username: "<%=j current_user.full_name.to_s %>"
});
You can parse in whatever information you need, this happens to be a Rails Project and all I needed was the ID and NAME (with the ID I could query the User factory object and receive a JSON with full information if I needed).
Then in your AngularJS Controller you can add (mine is coffeescript):
# Grab User Info
current_user = angular.element("meta[name=current_user]").data 'current_user'
# Stop Here if you Have the Info you Need
$scope.user = current_user
# **** OR Get More Info if User is Logged In
if current_user.id is not undefined
$scope.user = User.get {id: current_user.id}
Here is my Factory Code for the User (coffeescript again):
# In order to prevent minification from effecting the code
# must wrap functions as arrays and pass in the variables as
# strings before the function call
app.factory "User", ["$resource", ($resource) ->
$resource("/api/angular/users/:id",
{ id: "@id" },
{
update: { method: "PUT" }
})
]
Let me know if you have any questions.
回答4:
Edit: Changing some parts for the use with the angular_device module
Generally, I'm using the solution provided here (https://vickev.com/#!/article/authentication-in-single-page-applications-node-js-passportjs-angularjs) which describes how to check if a user is logged in for every HTTP call.
Check on load, if user is logged in
myApp.config(['$routeProvider','$locationProvider',
function($routeProvider, $locationProvider) {
//================================================
// Check if the user is connected
//================================================
var checkLoggedin = function($q, $rootScope, Auth, User){
// Initialize a new promise
var deferred = $q.defer();
Auth.currentUser().then(function(userObj){
deferred.resolve();
//option a: just save the user object into the $rootScope
$rootScope.User = userObj;
//option b: create a factory 'User' and a method 'save()' which parses the JSON returned from your Server
User.save(userObj);
}, function(error){
deferred.reject();
});
return deferred.promise;
};
//================================================
// Define all the routes
//================================================
$routeProvider
//Start page, where the user gets a list of registered SO and can add new ones
.when('/', {
templateUrl: 'templates/index.html',
controller: 'indexCtrl',
resolve: {
loggedin: checkLoggedin
}
})
.when('/login', {
templateUrl: 'templates/login.html',
controller: 'loginCtrl'
})
// do more here
.otherwise({
redirectTo: '/'
});
}]).run(function($rootScope, $http){
//Do some initial tasks or set some values
});
I would create an additional service named "User" (as services are singletons), define some methods, e.g. save() and get(), to save the User into the service singleton and get to return it where it is necessary. With the function "checkLoggedIn" you're able to define a "resolve" option for your routes and if the promise is resolved the user is able to access the restricted page.
回答5:
Save the user details in Global.user use 'Global' module in controllers or services.
when you have to access the user then access using Global.user
来源:https://stackoverflow.com/questions/29423383/how-to-keep-globally-current-user-until-logout-with-angular-devise