I am creating an login page using Angular. After I process my login in the backend, I set the values in MyService from my LoginCtrl and then move to the next page using $window.location.href= 'main.jsp'; . But when I call the values which I set in LoginCtrl from HomeCtrl, the values are empty?
I know that Services are singletons and will maintain the same state throughout the app. But in this case, It jut resets. I think it is because of using $window.location.href. Please help me solve my problem.
This is my service ( MyService ):
app.service('MyService', function() {
var user = {
name: '',
permissions: ''
};
this.getUser = function() {
return user;
}
this.setUser = function(userr) {
this.user = userr;
}
});
This my LoginCtrl: ( I've just posted the http.post part)
$http({
method: 'POST',
url: 'login',
data: JSON.stringify($scope.user),
headers: {
'Content-Type': 'application/json'
}
}).success(function(data) {
if (!("failure" == data)) {
console.log(data);
var user = MyService.getUser();
user.name = data.name;
user.permissions = data.permissions;
console.log(user);
console.log(MyService.getUser());
$window.location.href = 'main.jsp';
// MyService.changeLocation('main.jsp', true);
} else {
$scope.information = "Invalid username/password!"
}
}).error(function(data) {
console.log(data);
});
And this is my HomeCtrl:
app.controller('HomeCtrl', function($scope, $http,MyService) {
console.log(MyService.getUser());
var user = MyService.getUser();
$scope.flashMessage="Hello " + user.name;
});
Here user.name is empty.
You are changing your web page. The angular application is not persisted across the website boundary; remove the alteration to the window.location.href.
In order to simulate page changing in Angular consider using the official router (shipped with Angular 1.4+), ngRoute or Angular UI Router. These solutions use the HTML History Api and fallback to hashbang URLs to emulate the sort of thing you're trying to achieve.
This ends up creating a single-page application, which is what Angular is designed for.
In LoginCtrl, while reaching the success callback, you are not setting the response value(data in your case) to user object in MyService service.
You are getting the user object from the Service by
var user = MyService.getUser();
But setting the values to that object will not set the user object in the Service.
You need to use MyService.getUser(user); to set values in your service and the same will be available in your HomeCtrl
$http({
method: 'POST',
url: 'login',
data: JSON.stringify($scope.user),
headers: {
'Content-Type': 'application/json'
}
}).success(function(data) {
if (!("failure" == data)) {
console.log(data);
var user= {};
user.name = data.name;
user.permissions = data.permissions;
MyService.getUser(user); //set the values for user
var obj= MyService.getUser(); //get the values for user
console.log(obj);
//should display user object
//with respective name and permissions should be available
console.log(MyService.getUser());
$window.location.href = 'main.jsp';
// MyService.changeLocation('main.jsp', true);
} else {
$scope.information = "Invalid username/password!"
}
}).error(function(data) {
console.log(data);
});
UPDATE:
The reason why your code doesnt seem to work is: you are using $window incorrectly to change the route. $window.location.href = 'main.html' is somehow changing the route outside angular's context and hence not running the HomeCtrl. To fix this, you need to do the following:
First, define routes for your angular application (preferabbly using ui-router)
app.config(function($stateProvider){
$stateProvider
.state('login',{
url:'/',
templateUrl:'login.html',
controller:'LoginCtrl'
})
.state('main',{
url:'/main',
templateUrl:'main.html',
controller:'HomeCtrl'
})
.state("otherwise", { url : '/'})
})
Use $location.url('/main'). Notice it is same as the url pattern we defined for state: main. Or better, you should use $state.go('home'); to redirect the user to desirable state
Here's a working plunkr
Hope this helps!
Related
I have this $http request interceptor
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
request: function(req) {
// Set the `Authorization` header for every outgoing HTTP request
req.headers['cdt_app_header'] = 'tamales';
return req;
}
};
});
});
Is there any way we can add a header or cookie to every $http request, but keep the header value secure / not visible with JavaScript?
We can add an obfuscation layer with this header to prevent easy access to our API endpoints, but I am wondering about a more truly secure solution.
Cookies are used for secure sessions, and these are more secure because they cannot be accessed with JavaScript. Say we have a user who can do this request with front-end code:
GET /api/users
we don't really want them to be able to make a simple request with cURL or a browser without an extra piece of information. The cookie we give them will give them the ability to use the browser address bar to make a GET request to /api/users, but if we add the requirement to have another cookie or header in place, then we can prevent them from accessing endpoints that are authorized for, in a format that we don't really want them to use.
In other words, we want to do our best to give them access, but only in the context of a front-end Angular app.
I can't add a comment because of my rep but what are you doing on the back-end to authorize users? If the cookie is signed and contains user permissions it shouldn't matter that the header is visible in the client as it will also be verified on the back-end API call.
in this sample i used HttpRestService to get RESTful API, read this article
at first we create a service to get our configs in this sample is getConfigs
we use getConfigs in the app.run when application is started, after get the configs we set them all in the header as sample.
after that we can get userProfile with new header and also secure by call it from our controller as you see.
in this sample you need to define apiUrl, it's your api host url, remember after logout you can remove the header, also you can define your configs dynamically to make more secure for your application.
HttpRestService.js github link
app.js
var app = angular.module("app", ["HttpRestApp"]);
app.service
app.service("service", ["$http", "$q", "RestService", function (http, q, restService) {
this.getConfigs = function () {
var deferred = q.defer();
http({
method: "GET",
async: true,
headers: {
"Content-Type": "application/json"
},
url: "you url to get configs"
}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.resolve(error);
});
return deferred.promise;
}
var api = {
user: "User" //this mean UserController
}
//get user with new header
//this hint to your api with this model "public Get(int id){ return data; }"
//http://localhost:3000/api/users/123456
this.getUserProfile= function(params, then) {
restService.get(params, api.user, true).then(then);
}
}]);
app.run
app.run(["RestService", "service", function (restService, service) {
var header = {
"Content-Type": "application/json"
}
//get your configs and set all in the header
service.getConfigs().then(function (configs) {
header["systemId"] = configs.systemId;
});
var apiUrl = "http://localhost:3000/";
restService.setBaseUrl(apiUrl, header);
}]);
app.controller
app.controller("ctrl", ["$scope", "service", function ($scope, service) {
$scope.getUserProfile = function () {
//this is just sample
service.getUserProfile({ id: 123456 }, function (data) {
$scope.user = data;
});
}
$scope.getUserProfile();
}]);
In my project, I need to maintain a common data object for all the modules in the application.
This is where I store all the REST API's and the app wide data. (Something like store in react redux)
dataService.js
define(['jquery', 'app'], function($, app) {
var url = app.serviceURL;
function loginUser(data) {
data.type = "login";
return $.ajax({
url: url + '/authentication.php',
data: data,
method: "POST"
});
};
function logoutUser(data) {
data.type = "logout";
return $.ajax({
url: url + '/authentication.php',
data: data,
method: "POST"
});
};
return {
actions: {
loginUser: loginUser,
logoutUser: logoutUser
},
user: {
isLoggedIn: ''
}
}
});
I am requiring dataService.js file in ViewModel files to call login and logout services.
dataService.actions.loginUser(data)
.then(function(data) {
dataService.user.isLoggedIn = true; // I changed the app wide data here
}
});
My problem is, when I requiring the dataService in some other ViewModel, the value of dataService.user.isLoggedIn is set as default value. How can I preserve this as an app wide data?
It may be instantiating a new object for each module. You can create a global variable by doing:
document.dataService = new dataService();
Try using the above method. This might solve your problem.
I have a basic angular APP that makes a GET request call to a API URL. The data returned is in JSON format. The API documentation states the following:
You must provide your App ID and key with every request that you make to the API. To do this, set an HTTP Authorization header on your requests that consists of your ID, followed by a colon, followed by your key, eg abc123:xyz789.
How do I incorporate this to my basic HTTP request.my code is below.
angular.module('myApp', [])
.controller('MyControler', function($scope, $http) {
$scope.$watch('search', function() {
fetch();
});
$scope.search = "My Search Query";
function fetch() {
$http.get("https://APIURlGoesHere" + $scope.search )
.then(function(response) {
$scope.details = response.data;
});
$http.get("ttps://APIURlGoesHere" + $scope.search)
.then(function(response) {
$scope.related = response.data;
});
}
});
Best way I know so far to implement this is: Interceptors
You can find some useful info about it here and here
And on SO, here: AngularJS $http interceptors
In your case, basically, you need to create a file with the following implementation (or equivalent) and include it into your project:
function myInterceptor() {
function request(req) {
var token = "mytoken" ; //<<--here you need to set the custom header's info (eg: abc123:xyz789)
var myHeader = "myHeader"; //<<--here you need to set the custom header's name (eg: Authorization)
if (token) {
//put custom header for sending the token along with every request
req.headers[myHeader] = token;
}
return req;
}
return {
request: request
};
};
function conf($httpProvider) {
$httpProvider['interceptors'].push('myInterceptor');
};
angular
.module('your_module_name')
.factory('myInterceptor', myInterceptor)
.config(['$httpProvider', conf]);
This will intercept every request made from your frontend app and will include that header on it.
Citing this topic:
How to use Basic Auth with jQuery and AJAX?
So, in Angular, it would be:
$http({
method: "GET",
url: "https://APIURlGoesHere" + $scope.search,
headers: { 'Authorization' : 'Basic '+btoa(username + ":" + password)
})
.then(function(response) {
$scope.details = response.data;
});
I have a problem with my angular app- after a user signs in, if he hits the refresh button, the signin info is lost and the app redirects to the log in page. I found a SO answer for something similar here using $cookieStore but I don't think it can work for me as I'm not using cookies. Can anyone suggest a solution? Here's my authorization service-
var app = angular.module('myApp.services');
app.factory('SignIn', ['$resource', '$q', function($resource, $q) {
var signInUrl = 'https://example.com'
var API = $resource(signInUrl, {}, {
signIn: {
withCredentials: true,
url: signInUrl + '/session',
method: 'POST'
},
signOut: {
url: authApiUrl + '/session',
method: 'DELETE'
},
currentUser: {
url: signInUrl + '/users/#me',
method: 'GET'
}
});
var _currentUser = undefined;
return {
isAuthenticated: function() {
return !!_currentUser;
},
getUser: function(){
var d = $q.defer();
// If _currentUser is undefined then we should get current user
if (_currentUser === undefined) {
API.currentUser(function(userData) {
_currentUser = userData;
d.resolve(userData);
}, function(response) {
if (response.statusCode === 401) {
_currentUser = null;
d.resolve(_currentUser);
} else {
d.reject(response);
}
});
} else {
d.resolve(_currentUser);
}
return d.promise;
},
signIn: function(username, password){
var d = $q.defer();
API.signIn({email: username, password: password}, function(data, headers){
_currentUser = data;
d.resolve(_currentUser);
}, d.reject);
return d.promise;
},
signOut: function(){
var d = $q.defer();
API.signOut(function(){
_currentUser = null;
d.resolve();
}, d.reject);
return d.promise;
}
};
}]);
If you just need to keep track of the _currentUser data past a refresh then you could use sessionStorage within the browser. That extends all the way back to IE 8 and we really shouldn't be supporting any browsers before that anyway.
Usually these things are done with cookies though. When the client first makes a connection to the server (even before the first API call in some cases) a cookie is sent to the client so the server can maintain a session associated with that particular client. That's because the cookie is automatically sent back to the server with each request and the server can check its local session and say, "Oh, I'm talking to this user. Now I can use that additional piece of context to know if I can satisfy their API call or not."
You don't show any of your other API calls here but I'm guessing that you're sending something out of the _currentUser with each API call to identify the user instead? If so, that certainly works, and it avoids the need to synchronize cookies across multiple servers if you're clustering servers, but you're going to have to use something local like sessionStorage or localStorage that won't get dumped like your current in-memory copy of the data does when you refresh the page.
Maybe I'm going about this the wrong way, i'm not sure, but I have 2 services, one is a user service which gets a bunch of details about the user from the server, the other being one that relies on some user details from the user service and then makes some more calls to the server to get other information.
Anyway, because of the async stuff that goes on when the 2nd service makes the calls the information required from the user server has not yet been populated.
I know Angular services can depend on one another, but not in this context it would appear?
factory('User', ['$resource', function ($resource) {
return $resource(usersUrl, {}, {
//The data model is loaded via a GET request to the app
query: {method: 'GET', params: {}, isArray: false},
putupdate: {method: 'PUT', params:{}}
});
}])
.factory('UserData', function() {
var data = {}
data.userinfo = {};
if(data = {}){
}
return {
updateinfo: function(newdata) {
data.userinfo = newdata;
// alert(data.userinfo.user)
},
userinfo: data
}
})
.factory('PlansData', ['UserData', 'User', '$rootScope', function(userData, user, $rootScope) {
var data = {}
data.plansinfo = {};
//alert(userData.data.userinfo.user.email)
if(data = {}){
}
return {
updateinfo: function(newdata) {
alert(user.query())
data.plansinfo = newdata;
},
plansinfo: data
}
}])
So I have a user service and a caching userdata service, but if I ever try and call anything from UserData in the PlansData service I get undefined.
How do I get plansData to wait for UserData to have some data?
Thanks
Tom
I'm not sure what you're trying to accomplish, but this line of code:
if(data = {}){
}
In both your services is wiping out your data object. You're setting the whole data object to be {}