Angular doc states:
Angular services are singletons
I want to use the angular service as singleton, so I can access the logged-in user data every where in my application. but the serivce does not seem to return the same data, here is my codes.
Service:
angular.module("myapp", [])
.service("identity", function (){
this.token = null;
this.user = null;
});
Facotry:
.factory("authentication", function (identity, config, $http, $cookieStore) {
var authentication = {};
authentication.login = function (email, password, remember) {
var p=$http.post(config.baseUrl+"api/","email="+email+"&password="+password);
return p.then(function (response) {
identity= response.data;
if (remember) {
$cookieStore.put("identity", identity);
}
});
};
authentication.isAuthenticated = function () {
if (!identity.token) {
//try the cookie
identity = $cookieStore.get("identity") || {};
}
console.log(identity) // {token: 23832943, user: {name: something}}
return !!identity.token;
};
return authentication;
});
controller:
.controller('LoginCtrl', function ($state, $scope, authentication, identity) {
var user = $scope.user = {};
$scope.login = function () {
authentication.login(user.email, user.password, user.remember)
.then(function () {
if (authentication.isAuthenticated()) {
console.log(identity); // {token:null, user: null}
$state.transitionTo("dashboard");
}
});
};
});
The identity is injected to both authentication and controller. But the first console logs the correct user data, while the second console just logs the same data as initially defined. If the service is singleton as stated, I would expect two identity returns the same data. What am I doing wrong here?. any pointers are appreciated.
In your authentication service change
identity= response.data;
to
identity.token=response.data.token;
identity.user=response.data.user;
and things should work.
Basically what you are doing is replacing the identity object reference.
Related
My logout function, linked to a logout button is:
$scope.logoutUser = function() {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
ref.unauth();
console.log(ref.getAuth);
$state.transitionTo('login');
}
When I click logout, it prints this to the console:
function (){x("Firebase.getAuth",0,0,arguments.length);return this.k.P.we()}
I am checking for authData in my other controller with:
CONTROLLER:
.controller('SearchCtrl',
function ($scope, $http, Movie, $state, UsersRef, AuthData, $timeout) {
$scope.$on('$ionicView.enter', function () {
if (!AuthData) {
console.log("Auth data null!");
swal("Unauthorized", "You are not logged in", "error");
$state.transitionTo('login');
} else {
console.log("Auth data found: " + AuthData);
//do stuff
}
});
})
FACTORY:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
});
return data;
}
])
If I logout, then go back to the page linked to SearchCtrl by changing the URL, it still says it found the authData.
However, if I try and go to the search page the FIRST time I open the app, before anybody has logged in, it gives me the right error message and exits out to the login page.
How can I ensure the user can't go back into the app after logging out?
Welcome to async programming 101.
Firebase's onAuth methods listens for changes on auth state. When the auth state changes, the callback method you provide is invoked. But while it's waiting for auth state changes, your other code continues to run.
It most easy to see this if you add some log statements to your code:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
console.log('before onAuth');
ref.onAuth(function (authData) {
console.log('in callback');
if (authData) {
data = authData;
}
});
console.log('after onAuth');
return data;
}
])
The output is going to be:
before onAuth
after onAuth
in callback
Which is likely not what you expected when you wrote this code.
The simplest way to fix this in your code is to use the synchronous ref.getAuth() method:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
return ref.getAuth();
}
])
But you're going to run into this asynchronicity problem quite often. I highly recommend using and studying AngularFire instead of reinventing the wheel.
You are never cleaning data inside AuthData so it will always have data after the first guy logs in. I'm not familiar with Firebase but you need something like this in your AuthData factory:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
else
data = null;
});
return data;
}
])
This is my code for initializing the app and creating a controller.
var app = angular.module('newstalk',[]);
app.controller("articleCtrl",['$scope','$http','dataService',function($scope,$http,dataService){
$scope.articles = dataService.getArticles();
$scope.commentForm = function(id,userid){
console.log(userid);
var uid = userid;
var c = this.contents;
var data = {
content: c,
user: uid
};
console.log(data);
$http.post('/api/article/'+id,data);
};
}]);
Now, I have also created a service to fetch the data from the server. Here is the code for that:
(function(){
angular.module('newstalk')
.factory('dataService',dataService);
function dataService(){
return {
getArticles : getArticles
};
function getAricles(){
console.log("yolo");
return $http({
method:get,
url:'/api/articles/0'
})
.then(sendResponse);
}
function sendResponse(response){
console.log(data);
return response.data;
}
}
})
This is in a seperate file. Now when I run this I get a Error: $injector:unpr Unknown Provider error.
I've read multiple other such questions, none of which came to help. Any ideas?
I think you have not used IIFE correctly.
you should put () at the end of file.
(function(){
angular.module('newstalk')
.factory('dataService',dataService);
function dataService(){
return {
getArticles : getArticles
};
function getAricles(){
console.log("yolo");
return $http({
method:get,
url:'/api/articles/0'
})
.then(sendResponse);
}
function sendResponse(response){
console.log(data);
return response.data;
}
}
})()
putting () execute/run the function. rightnow you are not executing IIFE.
I am following a book tutorial, I am currently building the authentication for the app. Whenever I login correctly, I can't seem to set the token back into the request. The error I am getting is:
Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'function () {
return $window.localStorage.getItem('token');
}' is not a valid HTTP header field value.
Any help would be greatly appreciated
authService.js
angular.module('authService', [])
// ===================================================
// auth factory to login and get information
// inject $http for communicating with the API
// inject $q to return promise objects
// inject AuthToken to manage tokens
// ===================================================
.factory('Auth', function($http, $q, AuthToken) {
// create auth factory obj
var authFactory = {};
// login user
authFactory.login = function(username, password) {
// return promise obj and its data
return $http.post('/api/authenticate', {
username: username,
password: password
})
.success(function(data) {
console.log(data);
AuthToken.setToken(data.token);
return data;
});
};
// logout user by clearing token
authFactory.logout = function() {
AuthToken.setToken();
};
// check if user is logged in
// checks for local token
authFactory.isLoggedIn = function() {
if (AuthToken.getToken())
return true;
else
return false;
};
// get logged in user
authFactory.getUser = function() {
if (AuthToken.getToken())
return $http.get('/api/me', { cache : true});
else
return $q.reject({ message : 'User has no token.'});
};
return authFactory;
})
// ===================================================
// factory for handling tokens
// inject $window to store token client-side
//
//
// ===================================================
.factory('AuthToken', function($window) {
var authTokenFactory = {};
// get token out of local storage
authTokenFactory.getToken = function() {
return $window.localStorage.getItem('token');
};
// function to set token or clear token
// if a token is passed, set the token
// if there is no token, clear it from local storage
authTokenFactory.setToken = function(token) {
if (token)
$window.localStorage.setItem('token', token);
else
$window.localStorage.removeItem('token');
};
return authTokenFactory;
})
// ===================================================
// application configuration to integrate token into requests
// ===================================================
.factory('AuthInterceptor', function($q, $location, AuthToken) {
var interceptorFactory = {};
// this will happen on all http requests
interceptorFactory.request = function(config) {
// grab token
var token = AuthToken.getToken;
// if token exists add it to the header as x-access-token
if (token)
config.headers['x-access-token'] = token;
return config;
};
// happens on response errors
interceptorFactory.responseError = function(response) {
// if 403 from server
if (response.status == 403) {
AuthToken.setToken();
$location.path('/login')
}
//return the errors from server as promise
return $q.reject(response);
};
return interceptorFactory;
});
app.js
var app = angular.module('userApp', [
'ngAnimate', 'app.routes', 'authService', 'mainCtrl', 'userCtrl', 'userService']);
// app config to integrate token into req
app.config(function($httpProvider) {
// attach our auth interceptor to the http reqs
$httpProvider.interceptors.push('AuthInterceptor');
});
app.controller('mainController', function($http) {
// Bind this to view / vm-view model
var vm = this;
// define variables and objects on this
// this lets them be available to our views
// define a basic variable
vm.message = 'Hey! Message';
$http.get('/api/users')
.then(function(data) {
// bind users to vm.users
vm.users = data.users;
});
});
In custom interceptor factory
interceptorFactory.request = function(config) {
// grab token
var token = AuthToken.getToken;
// if token exists add it to the header as x-access-token
if (token)
config.headers['x-access-token'] = token;
return config;
};
change AuthToken.getToken; to AuthToken.getToken();
and your error was quite clear that you were passing function into header instead of value
Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'function () {
return $window.localStorage.getItem('token');
}' is not a valid HTTP header field value.
I want to access an authenticated user's data from my front-end using Angular.
This is the response from Express on Node.js.
routes/dashboard.js
exports.build = function (req, res) {
res.render('dashboard', {
uid: req.user._id
});
};
I grab the uid through the ng-init directive, currently.
views/dashboard.jade
doctype html
html
head
title= title
link(href='//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css', rel='stylesheet')
link(rel='stylesheet', href='/stylesheets/style.css')
block styles
body(ng-app='App', ng-controller='MainCntrl', ng-init='setUId(#{JSON.stringify(uid)})')
// some content
script(src='//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js')
script(src='//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js')
script(src='//cdnjs.cloudflare.com/ajax/libs/q.js/1.0.1/q.js')
script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js')
script(src='/javascripts/ng-app.js')
However, I would like to avoid doing this.
public/javascripts/ng-app.js
var app = angular.module('App', []);
app.factory('User', function () {
return {
// Promise for retrieving JSON User data
getProperties : function (id) {
var deferred = Q.defer();
$.getJSON('/api/user/' + id
, function (data) {
deferred.resolve(data);
}).fail(function () {
deferred.reject(true);
});
return deferred.promise;
}
};
});
app.controller('MainCntrl', ['$scope', '$http', 'User', function ($scope, $http, User) {
$scope.uid, $scope.user;
$scope.setUId = function (id) {
$scope.uid = id;
};
$scope.initUser = function () {
User.getProperties($scope.uid).then(function (user) {
$scope.$apply(function () {
$scope.user = user;
});
}, function (err) {
console.log(err);
});
};
}]);
I would like to pass this uid data to Angular without having to use the ng-init directive. Is there a way to access the response data, something along the lines of:
console.log(res.body.uid);
Retrieving the uid param from the response would remove the need for the ng-init directive. How would I go about retrieving it?
A more, and perhaps elegant solution would be to have the User ID in the URL and map a route to accept an id.
Then your controller can look more like this
app.controller('MainCntrl', ['$scope', '$http', 'User','$stateParams', function ($scope, $http, User, $stateParams) {
// NOTE: I do not know which route engine you're using, though you would inject
// its parameters provider in here, to get access to the url parameter (userId) of the route
// so for this example, i have assumed ng-router hence the injection of $stateParams
// the getProperties() method here is assuming ng-routet
User.getProperties($stateParams.id).then(function (user) {
$scope.user = user;
}, function (err) {
console.log(err);
});
}]);
I am trying to send extra header in XHR request (init with $resource).Following is my config
var app = angular.module('app',['angularMoment']).
run(function ($rootScope,$location,$route, $timeout, $http) {
var token = localStorage.getItem("userToken");
$http.defaults.headers.common.token = token;
}
I am changing hash params (eg. after login process) to navigate in app. So when I am sending any XHR request after login process (wihout mannual reload), it's sending token (request header) as NULL. But when I reload my page manually it's working fine (i.e sending token as header). Also I tried with $route.reload() but it's not working.
Please suggest how can I get rid of this issue.
Thanks
EDIT :
After trying with follwing code :
app.factory('tokenInterceptorService', ['$q', '$location', function ($q, $location) {
var tokenInterceptor = {};
var request = function (config) {
config.headers = config.headers || {};
var token = localStorage.getItem("userToken");
config.headers.token = token;
return config;
}
// if response errors with 401 redirect to lgoin
var response = function (rejection) {
if (rejection.status === 401) {
$location.path('/');
}
return $q.reject(rejection);
}
tokenInterceptor.request = request;
tokenInterceptor.response = response;
return tokenInterceptor;
}]);
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('tokenInterceptorService');
});
app.run(function ($rootScope, $location,$route, $timeout, $http) {
$rootScope.config = {};
$rootScope.config.app_url = $location.url();
$rootScope.config.app_path = $location.path();
$rootScope.layout = {};
$rootScope.layout.loading = false;
$rootScope.$on('$routeChangeStart', function () {
//need to validate
console.log($rootScope.isValidated + "app");
//show loading
$timeout(function(){
$rootScope.layout.loading = true;
});
});
$rootScope.$on('$routeChangeSuccess', function () {
//hide loading
$timeout(function(){
$rootScope.layout.loading = false;
}, 200);
});
$rootScope.$on('$routeChangeError', function () {
alert('Something went wrong. Please refresh.');
$rootScope.layout.loading = false;
});
})
It stop rendring the views in application with ".run" and trapping in $rootScope.$on('$routeChangeError', and giving the error Error: [$rootScope:inprog] $digest already in progress.
Since if I understand correctly your user token is always taken from localstorage, you can setup a watch on that localStorage key in your run function (Demo plunker for working with Localstorage in angular: http://plnkr.co/edit/7hP13JAjPybxkRuMZLZ0?p=preview )
angular.module('app',[]).run(['$rootScope', '$http', function($root, $http) {
$root.$watch(function() {
return localStorage.getItem('userToken');
}, function(userToken) {
$http.defaults.headers.common.token = userToken;
});
});
This should solve your problems without any interceptors etc.
However I'd actually recommend using http interceptor as calls to localStorage are slow, or setting the defaults where you actually set the user token after login or logout (save it also on a scope variable, and initialize it in the run part like you do now).
You need to set up an interceptor that alters every request sent to the server. You can find out more form the docs here, but essentially you need to set up a factory service on your app to add the token header like so:
app.factory('tokenInterceptorService', ['$q', '$location', 'localStorage', function ($q, $location, localStorage) {
var tokenInterceptor = {};
var request = function (config) {
config.headers = config.headers || {};
var token = localStorage.getItem("userToken");
if (token) {
config.headers.token = token;
}
return config;
}
// if response errors with 401 redirect to lgoin
var response = function (rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
tokenInterceptor.request = request;
tokenInterceptor.response = response;
return tokenInterceptor;
}]);
and then register it during the config stage with:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('tokenInterceptorService');
});
module.run executes well before anything else in the app (but after module.config). Would the localStorage have been set by then? I think that is happening later, which is why you see this value after reloading the page.
An interceptor would be the way to go.
How are you setting the value in localStorage?
Fiddle