AngularJS $broadcast $on doesn't work at first time - intermittent issue - angularjs

There is a strange behaviour in my AngularJS application. The use case it to create a user at "usercreate.html" and after successfully creating (over Backend REST- Service -> HTTP POST) a redirect to "usercreateresponse.html" is made.
I have two controllers:
usercreate.html -> CreateUserController
usercreateresponse.html -> CreateUserResponseController
and I share the data between CreateUserController and CreateUserResponseController over $rootScope.$broadcast and $rootScope.$on.
My problem now is, that when I have changed something in the code (AngularJS code) and I refresh the browser page and afterwards I will create a user, the user creation works fine and I am also redirected to usercreateresponse.html but no values are shown -> but this is only the first time -> if i navigate back to usercreate.html and create a user once again everything works fine.
I found out that in case if it is not working, a HTTP GET is made to get the HTML- Code from the back end. In case it works than only HTTP POST is made an everything works fine.
Does anybody know how to prevent the first user creation -> redirect problem?
Here the controller/services:
function CreateUserController($scope, CreateUserService) {
$scope.createUser = function(data) {
CreateUserService.createUser($scope.usernameCreate, $scope.passwordCreate, $scope.role);
}
}
services.factory('CreateUserService', function($rootScope, $location, $http) {
...
var res = $http.post('/users/createUser', dataObj);
res.success(function(data, status, headers, config) {
$rootScope.$broadcast('handleCreatedUser', {usernameCreate: data.username, passwordCreate: data.password, role: data.roles});
$location.path("usercreateresponse");
});
res.error(function(data, status, headers, config) {
...
});
}
return sharedCreateUserService;
});
function CreateUserResponseController($rootScope) {
$rootScope.$on('handleCreatedUser', function(event, args) {
$rootScope.usernameCreated = args.usernameCreate;
$rootScope.passwordCreated = args.passwordCreate;
$rootScope.role = args.role[0];
});
}

Instead of a broadcast, you can use a service which will store the response of the Creation of user. Then you can share it using the service with the CreateUserResponseController. With broadcast event, what might be happening is, you are broadcasting an event before the controller which handles it is instantiated. So, the $on may not have registered till then. And hence the issue you face.

Related

How to display response data ($http) from server in AngularJS

How to display reponse data (get) from server in AngularJS
in my code I need to alert $scope.orders inside that controller but it willll not show..
function OrderController($scope,$http)
{
var orderPromise = $http.get("../api/order");
orderPromise.success(function(data, status, headers, config)
{
$scope.orders=data
alert(JSON.stringify($scope.orders)) // First alert
});
orderPromise.error(function(data, status, headers, config)
{
alert("Error");
});
alert(JSON.stringify($scope.orders)) // Second alert
}
How can i access $scope.orders to outside the success fun()
Here I am alerting $scope.data in two times
In here First Alert is shown
but Second Alert is nothing to show why?
How to show second one?
Second alert will not show because there's nothing in $scope.orders when you alert it. That's the nature of asynchronous calls, only when you enter the success/error sections will you have something there (or not...).
Until the server returns your response and the success/error functions trigger, that variable is still unpopulated since $scope.orders=data hasn't run.
You should read the docs for some more info, and get some deeper understanding into how promises work.

AngularJs watch for change in variable inside a service

I have a service named loginManager which stores objects called is_logged_in & api_token along with few others. My various controllers make ajax calls using $http using the api_token.
If the api_token is reset/expired on server, response is sent as auth_error, at this point I set is_logged_in = false
What i want to achieve is, whenever is_logged_in is changed, the service redirects to /login using $location.path('/login'), i.e. to say, I want to watch the object inside the service, and invoke callback on change from service itself.
I just want the service to take care of login and corresponding routing, without any controller worrying about weather user is logged in or not.
I believe Pankaj Pakar's answer could work but you should use angular's interceptors for that. They intercept all messages. You could add hook for response or responseError and when you recieve auth_error you do any action you like. For example $location.path('/login'), display error to user, etc.
If you want to separate logic you could inject your service with all code inside and just call some method on it.
I'd suggest you to put that watcher in run phase on the angular application which will be there at a single place, by which you could check the value is_logged_in flag of service & if user is not login then redirect him/her to login page directily.
Code
app.run(function($rootScope, loginManager, $location){
$rootScope.$watch(function(){
return loginManager.is_logged_in;
}, function(newValue){
if(angular.isDefine(newValue) && !newValue)
$location.path('/login');
//$state.go('login'); //if you are using ui.router
})
})
Edit
Really curious part of your question is, from where you are changing is_logged_in flag of your service as #JBNizet asked? If any code is there is JavaScript then you should directly redirect to login page from there.
I feel the need to answer something more, Mior is quite right, but his answer needs more meat.
Here I show you how I managed to handle ALL server XHR requests with response 401 unauthorized.
First of all you need a service:
'use strict';
angular.module('theModule')
.factory('interceptorService', ['$q', '$location', function ($q, $location) {
return {
response: function (response) {
return response || $q.when(response);
},
responseError: function (rejection) {
var returnTo = $location.path().replace(/^\/|\/$/g, '');
if (returnTo === 'login') {
return;
}
if (rejection.status === 401) {
console.log('Unauthorized');
$location.path('/login').search('returnTo', returnTo);
}
return $q.reject(rejection);
}
};
}]);
This will be used to intercept all XHR calls and to change the location every time a 401 error is found.
I've also added an improvement that is the "returnTo" parameter, you will be able to use it after login to return to the previous page.
To bind it to each request you have to call the config method, this is my main javascript.
'use strict';
/**
* #author Gianmarco Laggia
*
* Main module of the application an configurations.
*/
angular
.module('theModule', [])
.config(['$httpProvider', function ($httpProvider) {
//Http Interceptor to check auth failures for xhr requests
$httpProvider.interceptors.push('interceptorService');
}]);
This is pretty much what you need to intercept every request, working on the rejection.status you can also intercept events such as server down (status is -1), internal server error (500+), success status (in the response part, status 200+) etc.

Angularjs $http can't read my RESTful app

I am working on a small project that has a RESTful api and now I need to connect it to my Angular.js frontend in order to bring it all together and start working on the GUI. I have tested out my api thoroughly and it works fine from multiple sources and multiple test programs/locations so I am fairly certain that it is not a server side problem.
I am using the following code to attempt connection with the api but it never gets out:
(function() {
'use strict';
var app = angular.module('expenseAPI', []);
app.controller('ExpenseController', ['$scope', '$http',
function($scope, $http) {
var user = {
username: "Richard",
password: 11111
};
$scope.registerUser = function() {
console.log("sending data");
$http.post('https://apiserver/user', user).success(function(data) {
$scope.result = data;});
});
};
}
]);
}());
I have tried manually editing the api call as well as calling different endpoints but it still doesn't go through. Can you tell me what mistake I am making or if maybe there is an easier way of doing this?
Update: after some more testing the problem still isn't resolved. It sends the authorization header properly and responds with 401 on appropriate routes if authorization fails. Also, this particular request (supposed to make a new user in the database) returns a status 200 but the base doesn't get updated and the promise returns a failure.

How to get logged user with Angular?

Maybe I am missing something very trivial, but I can't find an answer.
I am implementing a webapp based on nodejs, express and angular for the client side.
I handle my signup and sessions through passport.js. Therefore, server-side, I can access the logged user through request.user.
Now, I have a logged user which goes on its profile page, displayed through an angular view.
Here is the issue: In order to show them user information now I am thinking to send an $http request to the server, which takes the user from request and sends it back to the client where it is displayed. However, this sounds weird to me.
So here is my question: Is there any way to access the user in the session thruogh angular? If so, what user information is actually stored in the client?
Thanks in advance, and apologies if this is something too trivial to be asked:/
You will need to create a service in Angular that requests the current user, and stores the result so you can retrieve it for use in your controllers. There is nothing built in to Angular for this; you will have to create it your self. However, it's fairly straight forward.
myapp // assume was previously defined using var myapp = angular.module('myapp', []);
.factory('principal', ['$http', '$q', function($http, $q) {
var _identity = undefined;
return {
// this function returns the current _identity if defined; otherwise, it retrieves it from the HTTP endpoint
identity: function(setIdentity) {
if (setIdentity) {
_identity = setIdentity;
return;
}
var deferred = $q.defer();
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
$http.get('/path/to/endpoint')
.success(function(result) {
_identity = result;
deferred.resolve(_identity);
})
.error(function() {
_identity = undefined;
deferred.reject();
});
return deferred.promise;
}
};
}]);
This principal service has one method at the moment, identity(). This method returns a promise. If the identity has already been retrieved, it will resolve with that value immediately. If not, it will attempt to get it from an HTTP endpoint. If the HTTP call succeeds, it will memoize the result to _identity and resolve the promise. If the call fails, the promise will be rejected. identity has a secondary use. If you supply it with a single parameter, it will set that value as the identity and return nothing. This is useful if you already have the identity and want to set it right away, for example, after they successfully sign-in.
You could manage the sign-in page like this:
myapp.controller('SignInCtrl', ['$scope', 'principal', '$http', function($scope, principal, $http) {
// these two values will be bound to textboxes in the view
$scope.username = '';
$scope.password = '';
// this function will be called when the sign in form is submitted
$scope.submit = function() {
$http.post('/path/to/signin', {username: $scope.username, password: $scope.password })
.success(function(identity) {
// assumes /path/to/signin returns a JSON representation of the signed-in user upon successful authentication
// presumably the endpoint also sets a cookie representing an auth token or something of that nature. the browser will store this automatically for you
principal.identity(identity); // set the identity immediately
// do whatever else on successful login, like redirect to another route
});
}
}]);
And a controller somewhere else that needs the current identity could do something like this:
myapp.controller('MyCtrl', ['$scope', 'principal', function($scope, principal) {
// retrieve the identity. when it resolves, set user on the scope
principal.identity().then(function(identity) {
// after this, you can use user in your view or later in your controller
$scope.user = identity;
});
}]);
Now you have a way of storing the identity immediately after sign-in. I do make an assumption that your code that signs the user in sets a cookie to represent an auth token or whatever in your signin endpoint. The good thing about this is that if the user refreshes their browser, or the cookie is stored for a period of time, the user can just visit your app and the identity will resolve it using that token cookie automatically.
This plunk is a working demo of a more elaborate set up. Some of it may not apply to you (for example, it uses ui-router instead of regular routing), but it should be a reasonable reference point.

How to load data synchronously in AngularJS application

Now I know that because of the way javascript executes it is recommended that you run all remote requests as async instead of sync. While I agree with that 99% of the time, sometimes you do want to run remote request as a sync instead of a async. For example, loading session data is something I would want to do synchronically as I don't want any views to render until that data is loaded. This plunker shows the issue with loading session data asynchronically (NOTE: I am using $timeout to simulate what would happen with an async call):
http://plnkr.co/edit/bzE1XP23MkE5YKWxRYrn?p=preview
The data property does not load anything because the data is not available when it tries to get it and data2 does only because the data is available when it tries to get it. Now in this case I could just put the session variable on the scope and be done with it but that is not always the case.
Is there a better way to do sync remote calls in an angular application other than using jQuery's .ajax() method (trying to depend on jQuery as little as possible)?
If you want the session data to be loaded prior to a controller being loaded, you should included it as as resolve parameter (assuming you are using the $routeProvider).
For example:
angular.module('mymodule', ['ngResource'])
/* here's our session resource. we can call Session.get() to retrieve it. */
.factory('Session', ['$resource', function($resource) {
return $resource('/api/session.json');
}])
/* here's our controller + route definition. */
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/foo', {
controller: 'MyCtrl',
templateUrl: '/templates/foo.html',
/* the controller will not be loaded until the items
* below are all resolved! */
resolve: {
session: ['$q', 'Session', function($q, Session) {
var d = $q.defer();
Session.get(function(session) {
/* session returned successfully */
d.resolve(session);
}, function(err) {
/* session failed to load */
d.reject(err);
});
return d.promise;
}]
}
});
}])
.controller('MyCtrl', ['$scope', 'session', function($scope, session) {
/* 'session' here is the key we passed to resolve above.
* It will already be loaded and resolved before this function is called */
$scope.session = session;
}]);
Angular is hardcoded to make the requests async. To do it synchronously would take other code, whether custom or from some other library. Here is line 9269 from angular 1.0.7:
xhr.open(method, url, true);
The third param makes it asynchronous.
I would take a step back and think about how you are doing things. You could provide some loading indicator while your async request is going and easily control the loading of a view in the success callback so that it doesn't appear until the data is loaded.
A better solution is to add a response interceptor:
checkAuth = ($q, $location) ->
success = (response) ->
response
error = (response) ->
errorCode = response.status
$location.path '/login' if errorCode is 403 or errorCode is 401
# $q.reject response - no need because we are redirecting before any other promises in the chain will resolve (were breaking our future promises)
(promise) ->
promise.then success, error
$httpProvider.responseInterceptors.push checkAuth
And in your $routeProvider, or $stateProvider in my case:
.state 'user',
templateUrl: 'assets/views/user/partials/user.html'
resolve:
credentials: (checkLogIn) ->
checkLogIn.get().$promise
When checkLogIn.get()'s promise is rejected (the error handler is fired), assuming it's a 401 or 403 response (unauthenticated or unauthorized), the promise chain will be broken and the user will be "redirected" to /login.
With this method, any error calls will be channelled into the interceptor, instead of handling errors on a route-by-route basis.

Resources