I'm unable to login with front-end might be problem with Angular-ui-Router. When I click on login button first I got an error as 'Possibly unhandled rejection' then I injected '$qProvider' in config file after adding $qProvider I don't see any error in console but the page is not changing it state whereas in the network tab i can see the Token is sent from the server. Can some one please help me.
App.config.js
angular.module('myApp', ['ui.router', 'myAppModel'])
.config(function($qProvider, $stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$qProvider.errorOnUnhandledRejections(false);
$stateProvider
.state('base', {
abstract: true,
views: {
'nav#': {
template: '<navigation1></navigation1>'
}
}
})
.state('base.login', {
parent: 'base',
url: '/',
views: {
'main#': {
template: '<login></login>'
}
}
})
.state('base.register', {
parent: 'base',
url: '/register',
views: {
'main#': {
template: '<register></register>'
}
}
})
.state('base.dashboard', {
parent: 'base',
url: '/dashboard',
views: {
'main#': {
template: '<dashboard></dashboard>'
}
}
})
});
login.js
angular.module('myAppModel')
.component('login', {
templateUrl: '/components/login/login.html',
controller: function loginCtrl(Authentication, $state) {
var ctrl = this;
ctrl.pageHeader = 'Login';
ctrl.credentials = {
email: '',
password: ''
};
ctrl.onSubmit = function () {
ctrl.formError = '';
if(!ctrl.credentials.email || !ctrl.credentials.password) {
ctrl.formError = 'All fields required, please try again';
return false;
}else {
ctrl.doLogin();
}
};
ctrl.doLogin = function () {
ctrl.formError = '';
Authentication.login(ctrl.credentials).then(function(status) {
console.log(status);
$state.go('base.dashboard');
});
};
}
});
Authentication-service.js
angular.module('myAppModel')
.service('Authentication', function ($window, $http) { //Register new service with application and inject $window service
var saveToken = function (token) { //create a saveToken method to save a value to localStorage
$window.localStorage['app-token'] = token;
};
var getToken = function () { //Create a getToken method to read a value from localStorage
return $window.localStorage['app-token'];
}
var register = function (user) {
return $http.post('/api/users/register', user).then(function(data){
saveToken(data.token);
});
};
var login = function (user) {
return $http.post('/api/users/login', user).then(function (data) {
saveToken(data.token);
});
};
var logout = function () {
$window.localStorage.removeItem('app-token');
};
var isLoggedIn = function () {
var token = getToken(); //Get token from storage
if(token) { //If token exists get payload decode it, and parse it to JSON
var payload = JSON.parse($window.atob(token.split('.')[1]));
return payload.exp > Date.now() / 1000; //validate whether expiry date has passed
}else {
return false;
}
};
//Getting User Information from the JWT
var currentUser = function () {
if (isLoggedIn()) {
var token = getToken();
var payload = JSON.parse($window.atob(token.split('.')[1]));
return {
email: payload.email,
name: payload.name
};
}
};
return {
saveToken: saveToken, //Expose methods to application
getToken: getToken,
register: register,
login: login,
logout: logout,
isLoggedIn: isLoggedIn,
currentUser: currentUser
};
});
Related
I am getting a backend response to get the driver details using Axios and once after getting that I want to get the name, address of the driver. For that, I tried to set the value to a state variable but couldn't be able to assign, and also I have tried assigned the values to an array state variable and to access them later but I was unable to get a result
This is the code snippet:
getDriverInfo = async () => {
var session = sessionStorage.getItem("session");
var apiBaseUrl = "http://localhost:4000/api/";
// var self = this;
let det = [];
var payload = {
nic: session,
};
axios
.post(apiBaseUrl + "driverDetails", payload)
.then(async (response) => {
console.log(response.data.success);
if (response.data.code == 204) {
console.log("Driver Data retrieved successfull");
response.data.success.map((element) => {
det.push(element);
this.state.Ddetails.push(element);
});
console.log(det.length);
console.log(this.state.Ddetails[0].dln);
await this.setState({
fname: this.state.Ddetails[0].fName,
lname: this.state.Ddetails[0].lName,
});
} else {
console.log("Details does not exists");
alert("Details does not exist");
}
})
.catch(function (error) {
console.log(error);
});
console.log(det.length);
this.state.Ddetails.map((item) => {
console.log("Map");
console.log(item.dln);
});
console.log(this.state.Ddetails.dln);
};
Don't update state directly this is an anty pattern.
https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
I have modified your code a little.
getDriverInfo = async () => {
var session = sessionStorage.getItem("session");
var apiBaseUrl = "http://localhost:4000/api/";
// var self = this;
let det = [];
var payload = {
nic: session,
};
axios
.post(apiBaseUrl + "driverDetails", payload)
.then(async (response) => {
console.log(response.data.success);
if (response.data.code == 204) {
console.log("Driver Data retrieved successfull");
response.data.success.map((element) => {
det.push(element);
//Don't use like this ==> this.state.Ddetails.push(element);
});
this.setState({Ddetails:det},()=>{
console.log(det.length);
console.log(this.state.Ddetails[0].dln);
this.setState(prevState=>{
console.log(det.length);
prevState.Ddetails.map((item) => {
console.log("Map");
console.log(item.dln);
});
console.log(prevState.Ddetails.dln);
return{
fname: prevState[0].fName,
lname: prevState[0].lName,
}
});
})
} else {
console.log("Details does not exists");
alert("Details does not exist");
}
})
.catch(function (error) {
console.log(error);
});
};
this.state = {
name: 'mickey',
lastName: 'mouse',
};
const display = () => {
// wont work
console.log(this.state.name, this.state.lastName);
}
function display() {
// will work
console.log(this.state.name, this.state.lastName);
}
As you can see arrow functions wont work with (this)
My run block is as follows:
angular.module('app', [...]).run(['$transitions', '$state', 'EmployeeService', ($transitions, $state, EmployeeService) => {
$transitions.onBefore(
{ to: 'app.*' },
() => EmployeeService.checkAuth().then(() => {
console.log("run success");
},
() => {
console.log("run caught");
return $state.target('login');
}),
{priority: 10}
);
}])
And for some reason it executes for states 'app' and 'login' though they don't match 'app.*' match criteria, and thus it brings me to infinite loop when performing the logout. What is the right syntax to specify the match criteria ? I've already looked here:
https://ui-router.github.io/ng1/docs/latest/interfaces/transition.hookmatchcriteria.html
Edit: Posting routes configurations as requested
Routes for the main module (includes 'app' state which shouldn't fire the auth check):
export default mainPageRoutesConfig;
function mainPageRoutesConfig($stateProvider, $urlRouterProvider, $locationProvider) {
"ngInject";
$locationProvider.html5Mode(true).hashPrefix('!');
$urlRouterProvider.otherwise('/');
$stateProvider
.state('app', {
url: '/',
component: 'main'
})
.state('app.timesheet', {
url: 'timesheet',
component: 'timesheet'
})
.state('app.saveHours', {
url: 'saveTaskHours',
component: 'saveTaskHours',
params: {
timesheet:null
}
})
}
Routes for the employee module (includes 'login' state which shouldn't fire the auth check as well):
export default employeesRoutesConfig;
function employeesRoutesConfig($stateProvider, $urlRouterProvider, $locationProvider) {
"ngInject";
$locationProvider.html5Mode(true).hashPrefix('!');
$urlRouterProvider.otherwise('/');
$stateProvider
.state('login', {
url: 'login',
component: 'login'
})
.state('app.employeesList', {
url: 'employees',
component: 'employeesList'
})
.state('app.saveEmployee', {
url: 'employee/?Id',
component: 'saveEmployee'
})
.state('app.deleteEmployee', {
url: 'employee/:Id',
component: 'deleteEmployee'
})
.state('app.employeePage', {
url: 'profile/:Id',
component: 'employeePage'
})
}
The solution (though not really an answer) was to specify match criteria as a function:
{
to: function(state) {
return state.name !== 'app.login' && state.name !== 'app.error';
}
}
And the full code (after moving to separate file):
export default ($transitions, $state, EmployeeService) => {
$transitions.onBefore(
{
to: function(state) {
return state.name !== 'app.login' && state.name !== 'app.error';
}
},
() => EmployeeService.checkAuth().then(() => {
console.log("run block. Authorization check succeeded");
},
() => {
console.log("run block. Authorization check failed");
return $state.target('app.login');
}),
{priority: 10}
);
}])
I am trying to get the value returned through an authorization getCurrentUser function. When calling the function, I get this resource holding a Promise. I can see the _id value in there! How do I access it?
Thank you in advance!
Console Output
Here's a bit of the Auth service where getCurrentUser is defined:
(function config() {
function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
const safeCb = Util.safeCb;
let currentUser = {};
let Auth = {};
const LOCAL_HOST = 'localhost';
if ($cookies.get('token') && $location.path() !== '/logout') {
currentUser = User.get();
}
Auth = {
getCurrentUser(callback) {
if (arguments.length === 0) {
return currentUser;
}
const value = currentUser.hasOwnProperty('$promise') ? currentUser.$promise : currentUser;
return $q.when(value).then(user => {
safeCb(callback)(user);
return user;
}, () => {
safeCb(callback)({});
return {};
});
}
...
Here is where I call it:
angular.module('toroApp').directive('csvreaderDirective', ['$http', function ($http, Auth) {
return {
controller(Auth) {
const currentUser = Auth.getCurrentUser();
console.log(currentUser);
},
compile(element, Auth) {
...
Auth.getCurrentUser() is returning a $q promise so you need to use the promise API to get the result.
controller(Auth) {
Auth.getCurrentUser().then(function (user) {
const currentUser = user;
console.log(currentUser);
});
}
Hard refreshes on my SPA React/Firebase application does not maintain auth state on immediate execution of a function. I have a workaround, but it's sketchy.
My react routes utilize the onEnter function to determine whether or not the user is authenticated or not. For instance
<Route path="/secure" component={Dashboard} onEnter={requireAuth}/>
Furthermore, my requireAuth function looks like this:
function (nextState, replace) {
console.log('requireAuth', firebase.auth().currentUser);
if (!firebase.auth().currentUser) {
console.log('attempting to access a secure route. please login first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
};
However, on a hard refresh there is a slight delay on firebase.auth().currentUser. It's null at first, then executes a POST to firebase servers in order to determine auth state. When it returns the currentUser object is populated. This delay causes issues though.
My hacky solution is the following: update: this doesn't actually work...
function (nextState, replace) {
setTimeout(function () {
console.log('requireAuth', firebase.auth().currentUser);
if (!firebase.auth().currentUser) {
console.log('attempting to access a secure route. please login first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}, 50);
};
Simply wrap it in a timeout. However, I really don't like this... any thoughts?
Update:
I have also tried to wrap it within a onAuthStateChanged listener, which should be more accurate than a setTimeout with a definitive time delay. Code as follows:
function (nextState, replace) {
var unsubscribe = firebase.auth().onAuthStateChanged(function (user) {
if (!user) {
console.log('attempting to access a secure route');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
console.log('should have called replace');
}
unsubscribe();
});
// setTimeout(function () {
// console.log('requireAuth', firebase.auth().currentUser);
// if (!firebase.auth().currentUser) {
// console.log('attempting to access a secure route. please login first.');
// replace({
// pathname: '/login',
// state: { nextPathname: nextState.location.pathname }
// });
// }
// }, 50);
};
The two log statements are executed, but react-router replace does not seem to be executed correctly. Perhaps that's a different question for the react-router experts.
update 2:
It was late at night when I was working on this. Apparently setTimeout doesn't actually work either.
Okay. So, I was able to solve this by utilizing the localStorage variable that firebase provides to store the user information.
function (nextState, replace) {
if (!firebase.auth().currentUser) {
let hasLocalStorageUser = false;
for (let key in localStorage) {
if (key.startsWith("firebase:authUser:")) {
hasLocalStorageUser = true;
}
}
if (!hasLocalStorageUser) {
console.log('Attempting to access a secure route. Please authenticate first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}
};
While this is a post related to ReactJS, I recently came across the same problem when writing my own authentication/authorisation service for AngularJS. On page refresh the onAuthStateChanged passes a user that is null because firebase is still initializing (asynchronously).
The only solution that worked for me was storing the users uid in localStorage after the user has logged in and deleting the value after the user has logged out.
Since i'm using a authService and userService seperately I registered a listener in the authService that is fired once the user is logged in/out.
Code sample authService (not the full authService):
var loginListeners = [];
var logoutListeners = [];
function addLoginListener(func) {
loginListeners.push(func);
}
function addLogoutListener(func) {
logoutListeners.push(func);
}
function login(email, password) {
return firebase.auth().signInWithEmailAndPassword(email, password).then(function(user) {
for(var i = 0; i < loginListeners.length; i++) {
loginListeners[i](user); // call registered listeners for login
}
});
}
function logout() {
return firebase.auth().signOut().then(function() {
for(var i = 0; i < logoutListeners.length; i++) {
logoutListeners[i](); // call registered listeners for logout
}
});
}
Code sample userService (not the full userService):
.provider('userService', ['authServiceProvider',
function UserService(authServiceProvider) {
var usersRefUrl = '/users';
var userInfo = null;
var userDetails = null;
// refreshHack auto-executed when this provider creates the service
var storageId = 'firebase:uid'; // storing uid local because onAuthStateChanged gives null (when async initializing firebase)
(function addRefreshHackListeners() {
authServiceProvider.addLoginListener(function(user) {
userInfo = user;
localStorage.setItem(storageId, user.uid); // store the users uid after login so on refresh we have uid to retreive userDetails
});
authServiceProvider.addLogoutListener(function() {
userInfo = null;
localStorage.removeItem(storageId);
});
firebase.auth().onAuthStateChanged(function(user) {
if(user) { // when not using refreshHack user is null until async initializing is done (and no uid is available).
localStorage.setItem(storageId, user.uid);
userInfo = user;
resolveUserDetails();
} else {
localStorage.removeItem(storageId);
userInfo = null;
userDetails = null;
}
});
})();
function isLoggedIn() {
return userInfo ? userInfo.uid : localStorage.getItem(storageId); // check localStorage for refreshHack
}
function resolveUserDetails() {
var p = null;
var uid = isLoggedIn();
if(uid)
p = firebase.database().ref(usersRefUrl + '/' + uid).once('value').then(function(snapshot) {
userDetails = snapshot.val();
return userDetails;
}).catch(function(error) {
userDetails = null;
});
return p; // resolve by returning a promise or null
}
}]);
And in a run-block you can globally register a user and resolve the user-info/details every route change (makes it more secure):
.run(['$rootScope', 'userService', 'authService',
function($rootScope, userService, authService) {
// make user available to $root in every view
$rootScope.user = userService.getUser();
$rootScope.$on('$routeChangeStart',
function(event, next, current) {
// make sure we can add resolvers for the next route
if(next.$$route) {
if(next.$$route.resolve == null)
next.$$route.resolve = {};
// resolve the current userDetails for every view
var user = userService.resolveUserDetails();
next.$$route.resolve.userDetails = function() {
return user;
}
}
});
}]);
Maybe this can help someone who is struggling the same issue. Besides that feel free to optimize and discuss the code samples.
Works by managing localStorage. Here is example how I do it.
constructor(props) {
super(props);
let authUser = null;
// setting auth from localstorage
for (let key in localStorage) {
if (key === storageId) {
authUser = {};
break;
}
}
this.state = {authUser};
}
componentDidMount() {
firebase
.auth
.onAuthStateChanged(authUser => {
if (authUser) {
localStorage.setItem(storageId, authUser.uid);
} else {
localStorage.removeItem(storageId);
}
// change state depending on listener
authUser
? this.setState({authUser})
: this.setState({authUser: null});
});
}
This worked for me try to wrap your main app in an if else statement depending on the state of firebase.auth .
constructor() {
super();
this.state={
user:{},
stateChanged:false
};
}
componentDidMount(){
fire.auth().onAuthStateChanged((user)=>{
this.setState({stateChanged:true})
});
}
render() {
if(this.state.stateChanged==false){
return(
<div>
Loading
</div>
)
}
else{
return(<div>your code goes here...</div> )
}
}
I want to develop a file configuration of json and it is called with http get the constructor and return the value I want the config file to another component. But when return gives me value undefined.
My Config.json
[ {"urlServer": "http://localhost:56877"}]
My Config.Service
export class configService
{
url: string;
constructor(public _http: Http)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try {
return this._http.get('/app/config.json',
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <configModel>res.json();
this.url = data.urlServer;
JSON.stringify(this.url);
});
}
catch (ex) {
logger.registarErros('configService', ex);
}
}
returnConfig()
{
return this.url;
}
Now my other Component
constructor(public _http: Http, public config: configService)
{
this.token = sessionStorage.getItem('token');
this.username = sessionStorage.getItem('username');
}
login(username: String, password: String)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try
{
alert(this.config.url);
return this._http.post('http://localhost:56877/api/Login/EfectuaLogin', JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return Observable.of('authObservable');
});
}
catch (ex) {
logger.registarErros('authentication', ex);
}
}
I no longer know how to solve the problem, I need your help, I'm not very experienced with Angular 2.
Thanks very much.
The problem here is that the config is load asynchronously. You could use something like that leveraging the flatMap operator:
#Injectable()
export class ConfigService {
urlServer:string;
constructor(public _http: Http) {
}
getConfig() {
if (this.urlServer) {
return Observable.of(this.urlServer);
}
return this._http.get('/app/config.json', {
headers: contentHeaders
})
.map((res: any) => {
let data = <configModel>res.json();
return data.urlServer;
}).do(urlServer => {
this.urlServer = urlServer;
});
}
}
and in your component:
login(username: String, password: String) {
return this.configService.getConfig().flatMap(urlServer => {
this._http.post('http://localhost:56877/api/Login/EfectuaLogin',
JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return data; // or something else
});
}
});
}
Another approach would be boostrap asynchronously after having loaded the configuration:
var app = platform(BROWSER_PROVIDERS)
.application([BROWSER_APP_PROVIDERS, appProviders]);
service.getConfig().flatMap((url) => {
var configProvider = new Provider('urlServer', { useValue: urlServer});
return app.bootstrap(appComponentType, [ configProvider ]);
}).toPromise();
See this question for the second approach:
angular2 bootstrap with data from ajax call(s)
You can go further by mixing the last approach with a CustomRequestOptions:
import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from 'angular2/http';
export class CustomRequestOptions extends BaseRequestOptions {
merge(options?:RequestOptionsArgs):RequestOptions {
options.url = 'http://10.7.18.21:8080/api' + options.url;
return super.merge(options);
}
}
See this question:
Angular 2 - global variable for all components