i am trying to write a Factory for my WebApiCall
so ive written this :
mod.factory('AccountService', function($http) {
var service = {};
var onError = function(response) {
if (response == '') {
return ['Timeout Occured !'];
}
var errors = [];
for (var key in response.ModelState) {
for (var i = 0; i < response.ModelState[key].length; i++) {
errors.push(response.ModelState[key][i]);
}
}
return errors;
};
var onSuccess = function(response) {
return true;
}
service.Login = function(credentials) {
$http.put('http://localhost:9239/Api/Account/', credentials).success(function(data) {
return onSuccess(response);
}).error(function (response) {
return onError(response);
});
};
return service;
});
The Controller :
mod.controller('accountCtrl', function ($scope, $http, $window, $location, ConfigService, AccountService) {
$scope.credentials = { username: '', password: '' };
$scope.Errors = [];
$scope.registerModel = { username: '', password: '', passwordrepeat: '', email: '', emailrepeat: '' };
$scope.isLoading = false;
$scope.Login = function () {
$scope.Errors = [];
$scope.isLoading = true;
AccountService.Login($scope.credentials).onSuccess(function(response) {
$window.sessionStorage.setItem('loginToken', data.SuccessMessages[0]);
if (data.SuccessMessages[1] != '') {
$window.sessionStorage.setItem('groupId', data.SuccessMessages[1]);
}
$scope.isLoading = false;
$location.path('/Home');
}).onError(function(errors) {
$scope.Errors.push(errors);
$scope.isLoading = false;
});
Ok when i Login the Login MEthod is called. But wenn the Success or Error Method from $http is called it doesnt return my onSuccess or onError function.
I think i made some mistakes did i ?
A few things, $http promises don't have onSuccess or onError callbacks, they're sucess and error instead. In your service you are not returning the $http promise, and then you call sucess and error on it, of course it won't work! $http calls, much like Ajax calls anywhere take place asynchronously, meaning that you cant simply return a value in the success/error callback, you can however return a promise:
service.Login = function(credentials) {
return $http.put('http://localhost:9239/Api/Account/', credentials)
};
and in your controller you could use that like:
AccountService.Login($scope.credentials).then(function(data) {//success callback
$window.sessionStorage.setItem('loginToken', data.SuccessMessages[0]);
if (data.SuccessMessages[1] != '') {
$window.sessionStorage.setItem('groupId', data.SuccessMessages[1]);
}
$scope.isLoading = false;
$location.path('/Home');
},function(errors) {//error callback
$scope.Errors.push(errors);
$scope.isLoading = false;
})
when you call then on the promise, it gets executed when the promise is resolved, the first function is success callback and the second is the error callback.
Related
I have capturing the application response error ,while capturing the error, i am getting the error.
In Interceptor ,according to the response code , have assign the rootscope broadcast and show the alert message in controller.
Here $rootScope.$broadcast('loginRequired'); is assigning in interceptor and while capturing in service response inside controller.
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
interceptor.
var interceptor = function($q, alerts, $rootScope, $timeout, $location) {
return {
request: function(config) {
console.log(config);
return config;
},
response: function(response) {
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status == 500) {
$location.url('/ho');
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return $q.reject(rejection);
}
console.log(rejection.status);
return $q.reject(rejection);
}
}
};
$httpProvider.interceptors.push(interceptor);
alertManagerfactory
var alertsManager = function() {
return {
alerts: {},
addAlert: function(message, type) {
this.alerts[type] = this.alerts[type] || [];
this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in this.alerts) {
delete this.alerts[x];
}
}
};
};
alertsManager.$inject = [];
In controller :
var LoginController = function($scope, $rootScope, alerts, alertsManager) {
$scope.alerts = alertsManager.alerts;
// getting error in this line
//getting typeError: Cannot read property 'alerts' of undefined
LoginService.AfterLogin(username, password)
.then(function(response) {}, function(status) {
console.log("Error" + status);
if (status === 500) {
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
}
});
};
LoginController.$inject = ['$scope', '$rootScope', 'alerts', 'alertsManager'];
In controller view.
<div ng-repeat="alert in alerts" ng-class="'alert-' + (alert.type || 'warning')" close="closeAlert($index)">{{alert.msg}}</div>
"this" keyword in your addAlert "method" is actually referencing anonymous function you have assigned to addAlert prop.
There are couple of ways to deal with this. For example creating variable that holds your object.
var alertsManager = function() {
var $this = {
alerts: {},
addAlert: function(message, type) {
$this.alerts[type] = $this.alerts[type] || [];
$this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in $this.alerts) {
delete $this.alerts[x];
}
}
};
return $this;
};
alertsManager.$inject = [];
i am trying to combine a cordova application with angularjs and ionic framework with a rest service for user login and register.
this is the code of data.js which connects to the rest api
app.factory("Data", ['$http', 'toaster',
function ($http, toaster) { // This service connects to our REST API
var serviceBase = 'http://blace.co/task_manager/v1/';
var obj = {};
obj.toast = function (data) {
toaster.pop(data.status, "", data.message, 10000, 'trustedHtml');
}
obj.get = function (q) {
return $http.get(serviceBase + q).then(function (results) {
return results.data;
});
};
obj.post = function (q, object) {
console.log(object);
return $http.post(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.put = function (q, object) {
return $http.put(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.delete = function (q) {
return $http.delete(serviceBase + q).then(function (results) {
return results.data;
});
};
return obj;
}]);
and this is the code of my controller authCtrl.js
app.controller('authCtrl', function ($scope, $rootScope, $routeParams, $location, $http, Data) {
//initially set those objects to null to avoid undefined error
$scope.login = {};
$scope.register = {};
$scope.doLogin = function (login) {
Data.post('login').then(function (results) {
Data.toast(results);
if (results.status == "success") {
$location.path('dashboard');
}
});
};
$scope.register = {};
$scope.Register = function (register) {
Data.post('register').then(function (results) {
Data.toast(results);
if (results.status == "success") {
$location.path('dashboard');
}
});
};
$scope.logout = function () {
Data.get('logout').then(function (results) {
Data.toast(results);
$location.path('login');
});
}
});
this one is the part of my rest api for the registar part
$app->post('/register', function() use ($app) {
// check for required params
verifyRequiredParams(array('name', 'email', 'password'));
$response = array();
// reading post params
$name = $app->request->post('name');
$email = $app->request->post('email');
$password = $app->request->post('password');
// validating email address
validateEmail($email);
$db = new DbHandler();
$res = $db->createUser($name, $email, $password);
if ($res == USER_CREATED_SUCCESSFULLY) {
$response["error"] = false;
$response["message"] = "You are successfully registered";
} else if ($res == USER_CREATE_FAILED) {
$response["error"] = true;
$response["message"] = "Oops! An error occurred while registereing";
} else if ($res == USER_ALREADY_EXISTED) {
$response["error"] = true;
$response["message"] = "Sorry, this email already existed";
}
// echo json response
echoRespnse(201, $response);
});
the rest api is working perfectly and i have checked it with Advanced Rest Client
you can see it here
image
in internet explorer i have this error
BAD REQUEST - The request could not be processed by the server due to invalid syntax. (XHR): POST
i think the problem is that
By default, the $http service will transform the outgoing request by serializing the data as JSON and then posting it with the content- type, "application/json". When we want to post the value as a FORM post, we need to change the serialization algorithm and post the data with the content-type, "application/x-www-form-urlencoded".
another post
but i do not know how to implement this
any idea is appreciated!
thank you
I am trying login user using factory function in angularjs.
This is my code for checking login info:
$scope.login = function(user) {
if(!$rootScope.isLoggedIn) {
LoginService.login($scope.user, $scope);
console.log($rootScope.isLoggedIn);
} else {
$location.path('/home');
}
}
While LoginService factory service look like this:
.factory('LoginService', ['$http', '$location', '$rootScope', function($http,$location, $rootScope) {
return {
login: function(user, scope) {
$rootScope.processGoingOn = true;
var $promise = $http.post('user.php', user);
$promise.then(function(msg) {
var responseData = msg.data;
console.log(responseData);
if(responseData['login_success'] == 'true') {
$rootScope.isLoggedIn = true;
$rootScope.processGoingOn = false;
// success redirect
} else {
$rootScope.isLoggedIn = false;
$rootScope.processGoingOn = false;
// try login again
}
});
}
....
}
});
The change in $rootScope.isLoggedIn is not reflecting back to $scope.login() function in either success or failure, any suggestions?
this is because login function is returned before promise is resolved.
on way to do this can be return $promise like this
login: function(user, scope){
$rootScope.processGoingOn = true;
return $http.post('user.php', user);
}
call then where you have console.log($rootScope.isLoggedIn);
I'm using the code below in order to simplify the backend requests but I didn't catch how to call either a success method or an error method.
How can I reach the expected behavior commented in the code?
app.factory('REST', function ($http, $q, sweetAlert) {
return {
load: function (module, action, data) {
var deferred = $q.defer();
var promise = deferred.promise;
$http
.post('/api/'+module+'.php?action='+action, data)
.success(function (data) {
if(data.error)
{
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
}
else
deferred.resolve(data.result);
}).error(function () {
//HERE I WANT TO CALL .error(details)
});
promise.success = function(fn) {
promise.then(fn);
return promise;
}
return promise;
}
};
});
This is the code which uses the code above:
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload).success(function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
}).error(function(data) { //THIS SHOULD BE CALLED
$scope.loading = false;
});
}
First of all, I strongly discourage you from attaching .success to the promise you are returning. This is not Promises/A-compliant, and its subtle difference from .then (as is implemented by $http) causes a lot of confusion. Just return a pure promise.
Other than that, a few things to note:
1) you don't need another $q.defer and deferred.resolve() - just chain to the original promise of $http and return the resulting promise. (see deferred anti-pattern)
2) to reject a promise - that is, to cause the .catch (not .error - see above about the subtle difference) to fire - you should return $q.reject().
All of the above produces the following:
app.factory('REST', function($http, $q, sweetAlert){
return {
load: function(module, action, data) {
// this "return" returns the promise of $http.then
return $http.post('/api/' + module + '.php?action=' + action, data)
.then(function(response) {
var data = response.data; // .then gets a response, unlike $http.success
if (data.error) {
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
return $q.reject(data.error);
}
return data.result; // what you would have "resolved"
});
}
};
})
Then, as I said above, use the .then/.catch as you would with promises:
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload)
.then(function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
})
.catch(function(error) {
$scope.loading = false;
});
}
Update yr code as below
app.factory('REST', function ($http, $q, sweetAlert) {
return {
load: function (module, action, data) {
var deferred = $q.defer();
$http.post('/api/'+module+'.php?action='+action, data)
.success(function (data) {
if(data.error)
{
sweetAlert.swal({
title: "Error",
text: data.error,
type: "warning"
});
//HERE I WANT TO CALL .error(details)
deferred.reject(data.error);
}
else{
deferred.resolve(data.result);
}
})
.error(function (error) {
//HERE I WANT TO CALL .error(details)
deferred.reject(error);
});
return defferred.promise;
}
};
});
for yr controller
$scope.login = function () {
$scope.loading = true;
var payload = {'credentials': $scope.logindata};
REST.load('access', 'login', payload).then(
function(data) {
if(data.redirect)
$state.go(data.redirect);
$scope.loading = false;
},
function(error) {
$scope.loading = false;
});
}
First time calling, the authenticated property is false, even the credential is OK. If I login once again with the same credential, it will be OK.
Anyway, I am not sure that my factory below is the right way in angularjs or not. Would you please give me any suggestions?
Factory:
app.factory('authenticatorService',['$resource', function($resource){
var authenticator = {};
authenticator.attempt = function(email, password){
var current = this;
$resource("/service/authentication/:id",null,{'update' : { method: 'PUT'}})
.save({'email' : email,'password': password},
//success
function(response){
current.authenticated = sessionStorage.authenticated = true;
current.userinfo = response.user;
current.authenticated = true;
},
function(response){
current.authenticated = false;
}
);
return this.authenticated;
};
authenticator.logout = function(){
delete sessionStorage.authenticated;
this.authenticated = false;
this.userinfo = null;
return true;
};
authenticator.check = function(){
if(this.userinfo && this.authenticated){
return true;
}
return false;
};
return authenticator;
}]);
Controller:
app.controller('authenCtrl',
[
'authenticatorService',
'$scope',
'$sanitize',
'$log',
'$location',
function(alert, authenticator, $scope, $sanitize, $log, $location){
$scope.login = function(){
if(authenticator.attempt($sanitize($scope.email) ,$sanitize($scope.password))){
$location.path('/dashboard');
}else{
alert.add("danger","Login fail.");
}
}
}]);
The this.authenticated in authenticator.attempt will return before the asynchronous call from $resource has completed.
You will need to wait for the promise to be resolved before returning from the factory, and before receiving in the controller.
Something like this should hopefully work:
Factory:
authenticator.attempt = function(email, password){
var current = this;
$resource("/service/authentication/:id", null, {'update' : { method: 'PUT'}})
.save({'email' : email,'password': password},
function(response){
current.authenticated = sessionStorage.authenticated = true;
current.userinfo = response.user;
current.authenticated = true;
},
function(response){
current.authenticated = false;
}
).$promise.then(function () {
return current.authenticated;
});
};
Controller:
$scope.login = function() {
var email = $sanitize($scope.email);
var password = $sanitize($scope.password);
authenticator.attempt(email, password).then(function(isAuthenticated) {
if (isAuthenticated) $location.path('/dashboard');
else alert.add("danger", "Login fail.");
});
};