I am trying to implement a login functionality that redirects a valid user to a personalized HTML view welcoming them with their name.
My first view is a login view. When the user clicks the button labeled "Log In" ng-click is supposed to call my controller's $scope.login which is supposed to validate the user, set the $scope.needsLoggingIn variable, and redirect to the second view (welcome page that shows the value of $scope.needsLoggingIn).
My problem is that the second view (which uses the same Angular Controller and Module) does not show the $scope.needsLoggingIn variable when I try to set it in the $scope.login function. It only shows the $scope.needsLoggingIn when I hard-coded the value like this in the controller, outside a scope function:
$scope.needsLoggingIn = "Betty";
This is the $scope.login function in my controller which tries to set the $scope.needsLogging in and is accepted in the first view but not accepted by the second view:
$scope.login = function (username, password) {
$http({
method: 'GET',
url: ('/login/' + username + '/' + password)
}).then(function successCallback(response) {
if (JSON.stringify(response.data) === '[]')
{
$scope.needsPasswordMessage = true;
if ($scope.needsPasswordMessage)
$scope.needsPasswordMessage = false;
console.log("No match");
} else {
$scope.needsLoggingIn = username;
$scope.issuccessMessage = true;
if ($scope.isfailureMessage)
$scope.isfailureMessage = false;
$window.location.href = '../indextest.html';
return response;
}
}, function errorCallback(response) {
});
};
Does anyone have any suggestions how I can get my $scope.needsLoggingIn recognized by both HTML views, without hard-coding it into the controller? I would really like to be able to have my $scope.login function set the variable value and have it recognized by all views that use this particular controller.
Data that you want to have live across different views and controller instances should be handled via a service.
Basically you would create a service something like:
function userService($http) {
let service = {}
service.needsLoggingIn = ""
service.login = login
return service
function login() {
return $http.... (your login code)
}
}
Then inject the service into your control, set properties as appropriate and since the service is a singleton, those property values will be available everywhere you inject the service.
Related
I have a factory in which I set a IP which is common for all controllers. I get the ip in the other controllers and add the web service method like this: url : $scope.ipForHttp+"getDeviceDetailsReport" where $scope.ipForHttp is http://websitename.com/ which I get from the factory.
This works in my local computer. When I host this and open the page I get it the first time for all pages. But when I refresh the page I see an error in console.
GET http://websitename.com/undefinedgetDeviceDetailsReport 404 (Not Found). I get this error only when I refresh the page and only in the hosted site, not in my local computer.
I'm getting undefined between the / and method name getDeviceDetailsReport.
This is some of the code where I use the data from factory:
$scope.ipForHttp = userDetailsFactory.getUserDetailsFromFactory().ipAddress;
$scope.loading = false;
$scope.ClientID = userDetailsFactory.getUserDetailsFromFactory().clientidFromSession;
// $scope.dev={};
$scope.getDevice =function(){
$scope.loading = true;
$http({
method : "GET",
url : $scope.ipForHttp+"getDeviceDetailsReport" //i think undefined error here.
And I'm getting this Error: ngRepeat:dupes
Duplicate Key in Repeater too only when I refresh the page.
Edit. I run the factory and set the value every time the page is refreshed. Maybe the value is undefined the second time..but I don't know how I still have the old value.
Maybe this is why I get the Error: ngRepeat:dupes when I refresh too. What am I doing wrong?
The index page's(which contains the other html pages) controller with the factory:
angular.module('tollApp')
.controller('indexController', function($scope,$http,$window,userDetailsFactory){
$scope.usernameFromServer={};
$scope.getUserDetails = function(){
$http({
method:'GET',
url:'http://192.168.1.80:4000/getUserDetails'
// url:'http://websitename.com/getUserDetails'
})
.then(function(response){
// console.log(JSON.stringify(response));
userDetailsFactory.setUserDetailsInFactory(response.data);
$scope.usernameFromFactory = userDetailsFactory.getUserDetailsFromFactory().usernameFromSession;
$scope.theIP = userDetailsFactory.getUserDetailsFromFactory().ipAddress;
// $scope.usernameFromServer = userDetailsFactory.getUserDetailsFromFactory().username;
// console.log(JSON.stringify($scope.usernameFromFactory)+"usernameFromFactory");
})
}
$scope.logout = function(request,response){
$http({
method:'GET',
url:'/logout'
})
.then(function(response){
console.log(JSON.stringify(response));
if(response.data=="logout"){
// $window.location.href="http://websitename.comlogin";
$window.location.href="http://192.168.1.80:4000/login";
}
})
}
console.log("indexController");
}).factory('userDetailsFactory',function(){
var user = {};
return {
setUserDetailsInFactory : function(val){
user.useridFromSession = val[0].UserID;
user.usernameFromSession = val[0].UserName;
user.userroleFromSession = val[0].UserRole;
user.clientidFromSession = val[0].ClientID;
user.ipAddress = "http://192.168.1.80:4000/";
// user.ipAddress = "http://websitename.com/";
// console.log("in set "+user.clientidFromSession);
},
getUserDetailsFromFactory : function(){
return user;
}
};
});
That error is because you are doing an ng-repeat on a collection with duplicates. You need to track by index when you are doing an ng-repeat on a collection with duplicates.
I'm new in angular and I need help to call an http request.
I had this controller
var app = angular.module('myApp',[]);
app.controller('freeUserController', function($scope, $http) {
$http({
method: 'GET',
url: 'users'
}).then(function successCallback(response) {
$scope.users = response.data.result;
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
});
It allows to fill Select in a modal form
This Select change every time user click on confirm button(select shows free users) so I though to create a service with the above http call and use this or on confirm button ( or on inside javascript of confirm button) or when user clicks on select tho show user.
So I change angular code like this:
var app = angular.module('"modalUploadLicense"',[]);
app.controller('freeUserController', function() {
freeUserService();
});
app.service("freeUserService",function($scope, $http){
$http({
method: 'GET',
url: 'users'
}).then(function successCallback(response) {
$scope.users = response.data.result;
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
});
But it give me an error and moreover I don't know the best way to call the service when the user list change.
This is my confirm button javascript:
$("#createLicenseButton").click(
function() {
var form = $("#addLicenseForm");
$.ajax({
type : "POST",
url : form.attr("action"),
data : form.serialize(),
// all right with rest call
success : function(data) {
//No exception occurred
if (data.status==true){
//Also the field are right(for e.g. form value)
if(data.success==true){
//il risultato sta in data.result
//window.location.reload(true);
$('#licensesTable').load(document.URL + ' #licensesTable');
angular.element(document.getElementById('freeUserController')).scope().get();
//reload only the tag with id carsTable, so only the table
//$('#carsTable').load(document.URL + ' #carsTable');
$('#addLicenseModal').modal("hide");
notifyMessage("Your license has been created!", 'success');
}
else{
//code if there are some error into form for example
}
} else {
//code exception
$('#addLicenseModal').modal("hide");
notifyMessage("Error! Your license hasn't been created!", 'error');
}
},
//error during rest call
error : function(data) {
window.location.href = "/ATS/500";
}
});
});
Whereas html Select is:
<label>Username</label> <select class="form-control select2"
style="width: 100%;" name="user" ng-model="selectedItem" ng-options="user.username as user.username for user in users track by user.username">
</select>
How can I update my Select value?thanks
First, change your service so it has a function to call:
app.service("freeUserService",['$http', function($http){
var service = this;
service.getusers = getusers;
function getusers() {
var url = "your url gets here";
return $http.get(url);
}
}]);
Then like Arnab says, you need to change your controller:
app.controller('freeUserController', ['freeUserService', '$scope', function(freeUserService, '$scope') {
$scope.fetchusers = function() {
freeUserService.getusers()
.then(handleGetUsers)
.catch(handleErrors);
}
function handleGetUsers(result){
//This is a promise, because $http only does asynchronous calls. No
//need to wrap this in a jquery thingy.
//As soon as the async function is resolved, the result data is put
// on your scope. I used $scope.users on purpose, because of your
//html
$scope.users = result.data;
}
function handleErrors(error){
console.log(error);
}
}]);
As you can see, I have put the "variable" users on the scope. I did this on purpose because of your html
<label>Username</label> <select class="form-control select2"
style="width: 100%;" name="user" ng-model="selectedItem" ng-options="user.username as user.username for user in users track by user.username">
</select>
The select looks into the scope for a variable called users. Because the variable users is on your html, angular automatically watches the variable for changes.
What I understand from your question is that every time the http call is done, you get another result, right? So Angular will automatically watch for a change, and reflect the result on your select element. if interested in more information, you should read this. I also suggest following a 'start with Angular tutorial', because going from your question, I think you are missing the basics of Angular.
The last step you need to do (I think, if i understand your question), is bind the $http function to your HTML. There is a "ng-click" directive you can use. In your case, the button then could look like this:
<button ng-click="fetchusers()" type="submit">Get me new users</button>
As you can see, I use the $scope.fetchusers() function in the ng-click directive, wich will make a new http call, getting new uses (ofcourse, if the http call gets new users every time you call it).
Plunker with modified code here.
You can use the ng-init directive to initialize the value. I ll update my plunker so that you can see how the ng-init works. You should set it right next to your ng-controller. ng-init will make the first call and give you data from the start. Then every time you press the button, new data will come, and your select will be updated. I have updated the plunk. I have added one of my own webservices. Do mind, my webservices are on a free heroku account. If you wait too long, the heroku application will go to sleep mode and the first call for data will timeout.
About multiple asynchronous calls:
Angular promises can be chained! So you can do one asynchronous call (for example doing a post to a database), wait for it to finish, then get the updated data. In your service, you could do this:
function getUsers(parameters) {
var posturl = "url to post to";
return $http.post(url, data) //the update, it returns a promise
.then(handlePost)
.catch(handleError);
}
function handlePost(result){
//the $http.get will only be executed after the previous promise has been
//resolved!
var geturl = "url to get data from";
return $http.get(url); // this is the second asynchronous call
}
It is good practice to chain promises. It would be bad practice to use a jQuery ajax call for this.
Inject freeUserService to your controller freeUserController to atleast call the service from your controller, like:
app.controller('freeUserController', ['freeUserService', function(freeUserService) {
freeUserService();
}]);
Otherwise you write like this:
var app = angular.module('myApp',[]);
app.controller('freeUserController', ['freeUserService', function($scope, freeUserService) {
var promise = freeUserService();
promise.then(function successCallback(response) {
$scope.users = response.data.result;
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
// And bind the users variable in your template
}]);
app.service("freeUserService", ['$http', function($http){
return $http({
method: 'GET',
url: 'users' // Proper url to your api
});
}]);
If the user is on a page for a long time and the session ends, if they proceed to make an AJAX call after the session is already expired.. instead of receiving the JSON object, it instead receives the HTML of the login page.
Ideally I'm trying to make it so that it will redirect to a log in page.
Is there any way i can detect this?
I already have an ActionFilterAttribute that works for non-AJAX calls like so:
public class VerifySessionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userId = filterContext.HttpContext.Session["UserId"];
var userName = filterContext.HttpContext.Session["UserName"];
if (userId == null || userName == null)
{
filterContext.Result = new RedirectResult(string.Format("/Account/Login"));
return;
}
base.OnActionExecuting(filterContext);
}
}
But that doesn't get hit for the scenario above during AJAX calls.
I've also tried an Interceptor.. something like this:
app.factory('httpAuthInterceptor', function ($q) {
return {
'responseError': function (response) {
// NOTE: detect error because of unauthenticated user
if ([401, 403].indexOf(response.status) >= 0) {
// redirecting to login page
//$state.go('home');
$window.location.href = '/Account/Login';
return response;
} else {
return $q.reject(rejection);
}
}
};
})
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpAuthInterceptor');
});
But in the same scenario it doesn't seem to hit there as well during the expired session / AJAX call
Is there anything I can do to detect this? When the session is expired I just want to redirect to the login page.. Thanks for any help
EDIT: here's how I make my calls
app.factory('HeadlinesFactory', ['$http', function ($http) {
var HeadlinesFactory = {};
HeadlinesFactory.getShowsForClient = function (clientId) {
return $http({
method: 'POST',
url: '/Show/GetShowsForClient',
data: { clientId: JSON.stringify(clientId) }
});
};
//etc
EDIT2: how all my controllers look like. Except my Account Controller where I put the VerifySession in front of everything except the Login page to prevent loop redirects:
[Authorize]
[CustomFilters.VerifySession]
public class ShowController : Controller
{ ... }
Ajax requests will not process redirect requests for security reasons. In addition, since you are returning a redirect result, a 401/403 status code is not thrown but rather a 302 is returned.
What you could do is expand your filter to conditionalize logic based on whether or not the request is an ajax request. In addition, based on your comments, it seems like creating a new Authorize attribute instead would be the right way to go since that way you can simply replace the default Authorize attribute with your own logic.
public class VerifySessionAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect =
true;
}
else
{
filterContext.Result = new RedirectResult(string.Format("/Account/Login"));
return;
}
}
}
}
This would allow your Angular interceptor to pick up the request and handle it appropriately.
Since IsAjaxRequest looks explicitly for the "X-Requested-With": "XMLHttpRequest" and AngularJS no longer provides that header with Ajax requests, you can add a configuration to the $httpProvider to always include the header.
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}]);
I have built a simple application in Angular consuming a simple API I created myself using Laravel. The application is hosted here. The API is hosted here. Now I can log in to the application at which point the API returns a simple auth_token which is sent as the URL parameter in every subsequent request that is sent to the server.
There is only one user in the system:
Email: admin#admin.com
Password: admin12345
You can log into the application using these credentials at which point the application will set a cookie using the $cookieStore service and will use the token in this cookie for every subsequent request. After using the application, a user can log out from the application, where a DELETE request is sent to the server and on the success method, the cookie is deleted from the browser.
Unfortunately there is some issue with the code I suppose. The DELETE request is working as expected and it deletes the auth_token on the server and returns 200 OK. But the success method is not called. I am not sure what I am doing wrong. It might be just a syntax problem.
app.js
function AppCtrl ($scope, $cookieStore, $location, Auth) {
$scope.setActive = function (type) {
$scope.destinationsActive = '';
$scope.flightsActive = '';
$scope.reservationsActive = '';
$scope[type + 'Active'] = 'active';
};
$scope.authenticate = function (credentials) {
Auth.save(credentials, function(data){
$cookieStore.put('auth_token', data.auth_token);
$scope.isLoggedIn = true;
$location.path('destinations');
$scope.message = null;
}, function(data){
$scope.message = "Email/Password combination incorrect!";
});
};
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete({
'auth_token': $cookieStore.get('auth_token')
}, function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
});
};
if($cookieStore.get('auth_token')){
$scope.isLoggedIn = true;
}else{
$scope.isLoggedIn = false;
}
}
The logout function is called when the log out button is pressed. What am I doing wrong here?
Note: The application is not working on Chrome for some reason (Use Firefox). If you can shed some light on that, it would be very helpful.
Both the repositories are public if you wish to have a look:
AngulAir Application: http://gitlab.learningtechasia.com:8901/rohan0793/angulair.git
AngulAirAPI: http://gitlab.learningtechasia.com:8901/rohan0793/angulairapi.git
Here is your solution
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete(
{'auth_token': $cookieStore.get('auth_token')}, // parameters
{},//postData, which you don't need for this
function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
},
// error callback
function (httpResponse) {
// do what you want for error handling here
}
);
};
Note:-> (Below points solved the problem)
Only the 2nd option(postdata) in $resource.delete API was missing. We should give it as a blank {} if it is not required for API.
And delete method should return 204 Status Code in order to execute success callback.
I'm trying to update user data, but when I make the 'PUT' request, Angular is sending the entire user $scope rather than just the fields exposed by the form.
I'm using a Factory to get and put the data. Here's my edit function
$scope.editStudent = function () {
Student.edit ({id: $stateParams.studentId}, $scope.Student, function (data) {
$location.path('/');
});
};
This doesn't work for me because the server I'm sending to does it's own server-side validation, and if I'm sending the entire student scope there will be some fields that are blank, thus will not pass validation.
The server allows me to send just the field I need to update, so I'm looking for an Angular way of doing this.
Here's a screenshot to help explain my question:
You can write function to check the field defined in the form and then only submit the value. I think the code is self explanatory.
Code Snippet
app.controller('MainCtrl', function($scope, $http) {
$scope.user = {
"name": "Ali",
"company": "MSB",
getData: function(form) {
var data = {};
angular.forEach(this, function(fieldValue, fieldName) {
if (!angular.isFunction(fieldValue) && form.$isSubmit(fieldName)) {
data[fieldName] = fieldValue;
}
});
return data;
}
}
$scope.save = function() {
$scope.userForm.$isSubmit = function(fieldName) {
return !angular.isUndefined((this[fieldName]));
}
console.log($scope.user.getData($scope.userForm));
$scope.data = $scope.user.getData($scope.userForm);
//$http.put('url', $scope.data)
}
});
I created the plunkr - http://plnkr.co/edit/6g5myBvBmWZ2IvKMLuvL?p=preview
*Tips (or rules)
input name must same as the model name
change getData function as you like to process which data to return.