I'm new to angular, so I'm pretty sure it is a stupid question, but I failed to figure this out myself.
I have an app in which I have provider to get token from cookies using ngCookies, and I need to access it from inside app.config. I've tried several methods, but all I can get is service code, not a returned variable. What am I doing wrong?
var app = angular.module('appClient', [
'ngCookies'
]);
app.provider('getToken', function getTokenProvider() {
this.$get = ['getTokenService', function(getTokenService, $cookies) {
var token = $cookies.get('token');
return token;
}]
});
app.config(function ($resourceProvider, $httpProvider, getTokenProvider) {
var token = ? //need to get token from getTokenProvider here
$httpProvider.defaults.withCredentials = false;
$resourceProvider.defaults.actions = {
save: { method: 'POST', params: { token: token } },
get: { params: { token: token } },
query: { params: { token: token }, isArray: true }
};
});
Get a tricky solution based on your code. Anyone tell me if this has any side effects or drawbacks.
app.provider('getToken', function getTokenProvider() {
this.$get = ['$cookies', function($cookies) {
//$cookies.put("token","test");
var token = $cookies.get('token');
return token;
}]
});
app.config(function (getTokenProvider,$injectorProvider) {
//instantiate or get the service here
//this should be how angular gets services
//$injectorProvider.$get returns the instance injector (the $injector service)
var token = $injectorProvider.$get().get("getToken");
console.log(token);
});
Here is the plunker
UPDATE:
Using this may cause problem when the there is any code changing the provider of depended services used to instantiate 'getToken'. For example, if you have module.decorate for ngCookies after this config block, it will fail to work.
return {
token: token
};
add this line to return statement in provider and use
getTokenProvider.token
in controller
Related
What I am looking to do I when a user comes to the index.html page I need the login module to do 2 things:
I need this to check if a user is authenticated ( which I think I already started with the "function authService" ) if the user has a valid token then change the ui-view to dashboard/dashboard.html and if the key is not valid or there is no key at all then load login/login.html into ui-view.
Once they have successfully logged in I want them to be routed to "dashboard/dashboard.html"
Here is my login script:
function authInterceptor(API) {
return {
request: function(config) {
if(config.url.indexOf(API) === 0) {
request.headers = request.headers || {};
request.headers['X-PCC-API-TOKEN'] = localStorage.getItem('token');
}
return config;
}
}
}
function authService(auth) {
var self = this;
self.isAuthed = function() {
localStorage.getItem('token');
}
}
function userService($http, API) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;';
$http.defaults.transformRequest = [function(data) {
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];
var self = this;
self.login = function(username, pwd, ctrl) {
ctrl.requestdata = API + '/winauth' + '; with ' + username;
return $http.post(API + '/winauth', {
username: username,
pwd: pwd
})
};
var param = function(obj) {
var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
for(name in obj) {
value = obj[name];
if(value instanceof Array) {
for(i=0; i<value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value instanceof Object) {
for(subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value !== undefined && value !== null)
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}
return query.length ? query.substr(0, query.length - 1) : query;
};
}
function LoginCtrl(user) {
var self = this;
function handleRequest(res) {
self.responsedata = res;
self.message = res.data.message;
var authToken = res.data.auth_token;
localStorage.setItem('token', authToken);
}
self.login = function() {
this.requestdata = 'Starting request...';
user.login(self.username, self.pwd, self)
.then(handleRequest, handleRequest)
}
}
// Login Module
var login = angular.module('login', ["ui.router"])
login.factory('authInterceptor', authInterceptor)
login.service('user', userService)
login.service('auth', authService)
login.constant('API', 'http://myserver.com/api')
EDIT - I added this into my login controller to provide the login routes
login.config(function($httpProvider, $stateProvider, $urlRouterProvider) {
$httpProvider.interceptors.push('authInterceptor');
$urlRouterProvider.otherwise('/login');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('login', {
url: '/login',
templateUrl: 'login/login.html',
controller: "mainLogin",
controllerAs: "log"
})
// nested list with just some random string data
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard/dashboard.html',
})
})
login.controller('mainLogin', LoginCtrl)
Here is my index.html:
EDIT - I removed "ng-include" and added "ng-view" to control the routes.
<body ng-app="login" ng-controller="mainLogin as log" class="loginPage">
<div class="main" ui-view></div>
</body>
As you can see I have a function that is checking for the token in the users local storage:
function authService(auth) {
var self = this;
self.isAuthed = function() {
localStorage.getItem('token');
}
}
And I am loading it in the module as a service:
login.service('auth', authService)
This is where I am stuck. I don't know where to go from here. I don't even know if I am using my authService function properly. I am still learning a lot about AngularJS so its easy for me to get stuck. :)
Another thing you will notice is in my index.html file I am just loading the "login/login.html" partial as default. I need it to load either login.html or dashboard.html depending if they are logged in or not. And then also route them to dashboard.html once they have successfully logged in.
The script works great as far as hitting the auth API, authenticating the user and then storing a valid auth key on their local storage.
Anyone know how I can accomplish this?
There are two separate concerns that you are dealing with. The first, is to be able to determine if you are logged in. Assuming the user needs to be logged in for any state except the login state, you would implement it like so by listening for $stateChangeState events and verifying that the user is logged in:
login.run(function($state, authService) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
var authToken = authService.isAuthed();
if (!authToken && toState !== 'login') {
//not logged in, so redirect to the login view instead of the view
//the user was attempting to load
event.preventDefault();
$state.go('login');
}
})
});
This will put them on the login state if they haven't already logged in.
The second part is to redirect to the correct view after they login, which you would do in your login controller:
function LoginCtrl(user, $state) {
var self = this;
function handleRequest(res) {
self.responsedata = res;
self.message = res.data.message;
var authToken = res.data.auth_token;
localStorage.setItem('token', authToken);
//after successful login, redirect to dashboard
$state.go('dashboard');
}
self.login = function() {
this.requestdata = 'Starting request...';
user.login(self.username, self.pwd, self)
.then(handleRequest, handleRequest)
}
}
ok I see you are using ui.router so let's work within this framework.
You want to
check if a user is logged in
redirect user to a view
What you're looking for is resolve:{loggedIn: checkLoggedInFn}
so your route for dashboard could be something like
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard/dashboard.html',
resolve: {
loggedIn: function(){
//do your checking here
}
}
})
what this does basically is that the controller will not instantiate until every resolve is resolved (so you can use a promise here for example), and then the value is passed into the controller as a parameter, so you could then do something like:
if(!loggedIn){
$state.go('login');
}
You would handle the logic inside your login controller specifically here:
self.login = function() {
this.requestdata = 'Starting request...';
user.login(self.username, self.pwd, self)
.then(handleRequest, handleRequest)
}
Inside the callback for the success on login, you would simple do a state change to send the user to the correct state.
In authInterceptor add code for response. So:
return {
request: function(config) {
if(config.url.indexOf(API) === 0) {
request.headers = request.headers || {};
request.headers['X-PCC-API-TOKEN'] = localStorage.getItem('token');
}
return config;
},
response: function(response) {
//ok response - code 200
return response;
},
responseError: function(response){
//wrong response - different response code
}
};
On server side check http header X-PCC-API-TOKEN and if is wrong ( no authentication) response should have different code like 403. So responseError method will run in interceptor.
responseError: function(response){
//wrong response - different response code
if (response.status === 403) {
alert("No rights to page");//your code for no auth
//redirect to different route
$injector.get('$state').transitionTo('login');//use injector service
return $q.reject(response);//return rejection - use $q
}
}
Your service is fine and it's on the loginModule but you are not using it anywhere where i can see. You need to inject your service into controller to do stuff you want. In your authService you are getting item from localstorage but you are not returning anything for example you have your login service
function authService(auth) {
var self = this;
self.isAuthed = function() {
return localStorage.getItem('token');
}
}
//here you can inject your auth service to get it work as you want
function LoginCtrl(user, auth) {
var self = this;
function handleRequest(res) {
self.responsedata = res;
self.message = res.data.message;
var authToken = res.data.auth_token;
localStorage.setItem('token', authToken);
}
self.login = function() {
this.requestdata = 'Starting request...';
user.login(self.username, self.pwd, self)
.then(handleRequest, handleRequest)
}
}
login.service('auth', authService)
function authService(auth) {
var self = this;
self.isAuthed = function() {
**localStorage.getItem('token');**
}
}
Where are you getting the localstorage item into? The LValue is missing.
At the most basic level, you could handle a check for this item - token - in the Dashboard page, at the time of loading the page and if it is null ie. empty, then redirect/route the user to the login page. Btw, use the sessionStorage rather than the localStorage as the former will flush as soon as the browser session is closed.
There are more elegant and simpler ways of accomplishing it like Passport. Have you checked it? It is as simple as this:
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect:'/login'}));
Your code isn't checking on url changes or affecting routes in a cross-cutting way.
Remember that authentication and authorization are cross-cutting concerns. That being said, Angular has a way for you to intercept routing calls by listening on $routeChangeStart. Your "interceptor" should be added there. You can then redirect the router to the required view by manually routing there. Have a look as the solution from a previous stack overflow thread.
There is a simple way you can achieve what you want for your application, using PassportJs.
The documentation is pretty simple and easy to implement.
You can also refer this tutorial to implement authentication using Passport. This tutorial teaches in very simple way, how to do authentication for your application.
Simple way to do that is just use https://github.com/Emallates/ng-enoa-auth package. You just need to include it in your app, nothing else.
I am trying to create a service to use throughout my Angular app that pulls in data from a .json file using $http. This is what the factory looks like:
var trooNewsServices = angular.module('trooNewsServices', []);
trooNewsServices.factory('Articles', ['$http',
function($http){
$http.get('resources/articles.json').success(function(data) {
return data;
});
}]);
I passed in the trooNewsServices dependency into my module declaration. Any controller that I try to pass in my new Articles service, I get a
"Could not instantiate controller HomeController"
error in the console. Not sure what I am missing/what is wrong with this code. Should I be using $resource instead of $http?
Here is how I am passing the 'trooNewsServices' into my main module:
var TrooNews = angular
.module('TrooNews', ['ngMaterial', 'ngNewRouter', 'trooNewsServices'])
.config(function($mdThemingProvider) {
$mdThemingProvider
.theme('default')
.primaryPalette('indigo')
.accentPalette('pink');
})
.config(function($locationProvider) {
$locationProvider.html5Mode({
enabled: false,
requireBase: false
});
});
Here is how I try to inject 'Articles' into one of my controllers:
TrooNews.controller('HomeController', ['Articles',
function(Articles) {
this.name = 'Troo News';
this.articles = Articles.query();
}]);
And here is how I set up routing in my 'AppController':
TrooNews.controller('AppController', function($router, $mdSidenav, $mdToast, $parse, $http) {
$router.config([{
path: '/',
component: 'home'
}, {
path: '/home',
component: 'home'
}, {
path: '/about',
component: 'about'
}, {
path: '/settings',
component: 'settings'
}, {
path: '/article/:id',
component: 'article'
}]);
this.toggleSidenav = function(menuId) {
$mdSidenav(menuId).toggle();
};
this.navigateTo = function(link) {
var parts = link.match(/^(.+?)(?:\((.*)\))?$/);
var url;
if (parts[2]) {
url = '.' + $router.generate(parts[1], $parse(parts[2])());
} else {
url = '.' + $router.generate(parts[1]);
}
$mdToast.show($mdToast.simple().content('Navigate To: ' + url).position('bottom right'));
$router.navigate(url);
this.toggleSidenav('left');
};
});
Inside your HomeController, you are executing this.articles = Articles.query();, but your Articles service doesn't define any query function.
Instead, your service is just immediately executing an HTTP GET request upon creation. Not sure why this would lead to your error, but it is a red flag.
Try changing your Articles service to the following:
trooNewsServices.factory('Articles', ['$http',
function Articles($http){
this.query = function() {
return $http.get('resources/articles.json')
.then(function(response) { return response.data; });
};
}]);
I was experiencing the same error message under different conditions. In my case, it was because I was referencing $scope in my dependencies (old habit that I'm trying to break). In my case, I wasn't using $scope and could easily remove the reference. That cleared up my error. Check your code for $scope references and see if that fixes it.
https://github.com/angular/router/issues/313
and
How can we watch expressions inside a controller in angular 1.4 using angular-new-router
First i want to say that i'm fairly new to AngularJS, so it might be that i'm asking my question with some oddnes.
I'm trying to pass a string to a factory which in return gives me a result from my database. When i hardcode the values, everything works. But when i try to pass inn values from my view/controller things stop working.
Here is my factory:
healthServices.factory('Entry',
function ($resource) {
return $resource('http://localhost:60673/api/hierarchy/:id', { id: '#id'}, {
query: { method: 'GET', isArray: true }
});
});
Here is the controller im using:
$scope.changeData = function (type) {
$scope.entry = new Entry();
$scope.id = type;
$scope.healthData = $scope.entry.$query({ id: $scope.id });
}
And this is how it looks in my html:
<button ng-click="changeData('someValue')">
At the moment i keep getting
TypeError: value.push is not a function
As i mentioned im quite new to this, so I might be far off. Any help would be very much appreciated.
What is intended by this line of code?
$scope.entry = new Entry();
Entry is your service you want to call.
You should pass this into your controller via dependency injection.
Angular does the 'new' for you.
myApp.controller('myCntrl', HomeCtrl);
HomeCtrl.$inject = ['$scope', 'Entry'];
function HomeCtrl($scope, Entry) {
...
}
I am not seeing any wrong with your $resource configuration.
var myApp = angular.module('myApp',['ngResource']);
myApp.factory('Entry', function ($resource) {
return $resource('http://localhost:60673/api/hierarchy/:id', { id: '#id'}, {
query: { method: 'GET', isArray: true }
});
});
myApp.controller('myCtrl', ['$scope', 'Entry', function($scope, Entry){
$scope.changeData = function (type) {
$scope.entry = new Entry();
$scope.id = type;
$scope.healthData = $scope.entry.$query({ id: $scope.id });
}
}]);
i am getting below in console
GET http://localhost:60673/api/hierarchy/someValue
error lies on other part of the code, please post your controller completely.
Trying to pull in some data using angular and an API. Obviously I'm quite new to this.
My custom service:
readIp.service('ip', ['$resource', function($resource){
this.getIP = function(ip) {
var ipData = $resource("http://www.telize.com/jsonip?callback=getip", {
callback : "JSON_CALLBACK"
}, {
get : {
method: "JSONP"
}
});
return ipData.get({ getip: ip });
}
}]);
From my controller:
$scope.getIP = ip.getIP($scope.getip);
HTML:
<strong>Your IP is:</strong> {{ getIP.ip }}
I'm getting an error currently:
Uncaught ReferenceError: getip is not defined
as the API shows up as: getip({"ip":"###.###.##.##"}); from the source.
Your service isn't properly defined. It should return an object that contains your getIp method. try something along the lines of :
readIp.factory('ip', ['$resource', function($resource){
return {
getIP: function(ip) {
// your code goes here
}
}
}]);
try this in place of above code. Hope you have already added ngResource module.
readIp.factory('ip',['$resource',function($resource){
return $resource('http://www.telize.com/jsonip?callback=getip', {}, {
query: {method:'GET', params:{}}
});
}])
I built a simple app with user authentication base on this: link
Basically, I have a userAccountService, responsible for communicating with server and login controller handling the login process.
From other controller I want to check if user is already logged in (to hide LogIn button, and show user profile instead).
So I have a navController
function navCtrl ($scope, $modal, userAccountService) {
$scope.IsUserLoggedIn = function () {
return userAccountService.isUserLoggedIn;
}
}
So in HTML I use this ng-hide="isUserLoggedIn()
my userAccountService:
app.factory('userAccountService', ['$http', '$q', userAccountService]);
function userAccountService($http, $q) {
var service = {
registerUser: registerUser,
loginUser: loginUser,
logOut: logOut,
getValues: getValues,
isUserLoggedIn: false,
accessToken: ""
};
// code ommited
function loginUser(userData) {
var tokenUrl = serverBaseUrl + "/Token";
if (!userData.grant_type) {
userData.grant_type = "password";
}
var deferred = $q.defer();
$http({
method: 'POST',
url: tokenUrl,
data: userData,
})
.success(function (data,status,headers,cfg) {
// save the access_token as this is required for each API call.
accessToken = data.access_token;
isUserLoggedIn = true;
// check the log screen to know currently back from the server when a user log in successfully.
console.log(data);
deferred.resolve(data);
})
.error(function (err, status) {
console.log(err);
deferred.reject(status);
});
return deferred.promise;
}
}
What am I doing wrong? Here's another interesting read I took inspiration from: link
You can't return a variable, but you can return a function, so create a function that returns that variable.
Try something like this, it returns your service object (you might want to put a $watch on it):
Service
function userAccountService($http, $q) {
function getData() {
return service;
}
...
}
Controller
$scope.IsUserLoggedIn = userAccountService.getData().isUserLoggedIn;
Also, you're not correctly updating the state variable from your success callback - you're creating global variables instead of using the service object properties. So, for example:
isUserLoggedIn = true;
should be:
service.isUserLoggedIn = true;