How to pass arguments through factories requests - javascript

I use the RIOT GAMES API. And I use factories for my API calls. First of all I request the summonerName and then I use this name to get the id of this summonerName.
I tried with:
$scope.summonerId = $scope.summoner.id;
And then access to this $scope but it doesn't work:
I recived this error with and undefined where I should recive the summonerId. (21694436) this is my summonerID :
https://euw.api.pvp.net/api/lol/euw/v1.3/stats/by-summoner/undefined/summary?season=SEASON2016&api_key=foo-bar-foo-bar-foo-bar
I have got the following javascript code:
'use strict';
angular.module('mean.system').controller('SummonerController', ['$scope', '$http','APIAcces', '$stateParams',
function($scope, $http, APIAcces, $stateParams) {
$scope.summonerName = $stateParams.summonerName;
APIAcces.getSummonerByName($scope.summonerName).then(
function successCallback(response) {
$scope.summoner = response.data[$scope.summonerName.toLowerCase()];
$scope.summonerId = $scope.summoner.id;
console.log($scope.summonerId); //returns id successfuly
console.log(response);
}, function errorCallback(error) {
console.log(error);
console.log(response);
},
//if I do.. APIAcces.getSummonerSummary('21694436').then( // it works!
APIAcces.getSummonerSummary($scope.summonerId).then(
function successCallback(response) {
$scope.summoner2 = response.data[$scope.summonerId];
console.log(response);
},function errorCallback(error) {
console.log(error);
console.log(response);
}
) //End APIAcces.getSummonerSummary
); //End APIAcces.getSummonerByName
}
]);
I pass the argument summonerId and this factory it doesn't recognize it.
I use this method:
angular.module('mean.system').factory('APIAcces',['$http','API_KEY',
function($http,API_KEY){
return {
getSummonerByName:function(summonerName){
return $http.get('https://euw.api.pvp.net/api/lol/euw/v1.4/summoner/by-name/'+summonerName+'?api_key='+API_KEY.KEY);
},
getSummonerSummary:function(summonerId){
return $http.get('https://euw.api.pvp.net/api/lol/euw/v1.3/stats/by-summoner/'+summonerId+'/summary?season=SEASON2016&api_key='+API_KEY.KEY);
},
}
}]).value('API_KEY',{
KEY: 'foo-bar-foo-bar-foo-bar'
});
I don't know, maybe it is an order of factories or something?

From your code, it's a typical async callback problem. you may have to first understand javascript callback and async architecture by reading somewhere else.
The reason is because
APIAcces.getSummonerSummary()
is being called when
APIAcces.getSummonerByName()
has not finished fetching, so the summonerId is undefined, it's just an async programming's nature.
so to correct this, you have chain the call together like this:
APIAcces.getSummonerByName($scope.summonerName).then(
function(response){
var summonerId; //extract the id from response
APIAcces.getSummonerSummary(summonerId).then(
function(response){
//response should contain the summary
},
function(error){
//error of getting by id
}
);
},function(error){
//error of getting by name
});

There are two problems. Trimming the contents of your functions shows the first problem:
APIAcces.getSummonerByName($scope.summonerName).then(
function successCallback(response) {
// success
},
function errorCallback(error) {
// failure
},
APIAcces.getSummonerSummary($scope.summonerId).then(
function successCallback(response) {
// inner success
},
function errorCallback(error) {
// outer success
}
)
);
The third parameter is the finally parameter but you are not passing in a function. You should do this:
APIAcces.getSummonerByName($scope.summonerName).then(
function successCallback(response) {
// success
},
function errorCallback(error) {
// failure
},
function finallyCallback() {
APIAcces.getSummonerSummary($scope.summonerId).then(
function successCallback(response) {
// inner success
},
function errorCallback(error) {
// inner failure
}
)
}
);
The second problem is that you probably don't want this in the finally block anyway. If your request fails, you will not have an appropriate summoner id to work with. It should be moved to the success block:
APIAcces.getSummonerByName($scope.summonerName).then(
function successCallback(response) {
// success
APIAcces.getSummonerSummary($scope.summonerId).then(
function successCallback(response) {
// inner success
},
function errorCallback(error) {
// inner failure
}
)
},
function errorCallback(error) {
// failure
}
);

Related

Why am I geting TypeError: onSuccess is not a function in my angularjs service

I have an angularjs services that leverages restangular to make calls.
angular.module('app.userSvc', [])
.factory('userSvc', ['localStorageService', '$rootScope', 'Restangular',
function(localStorageService, $rootScope, Restangular) {
function checkIfLoggedIn() {
return localStorage.getItem('token');
}
function login(email, password, onSuccess, onError) {
Restangular.all('api/authenticate')
.post({
email: email,
password: password
})
.then(
function(response) {
localStorage.setItem('token', response.token);
onSuccess(response);
},
function(response) {
onError(response);
});
}
function logout() {
localStorageService.remove('token');
}
function getCurrentToken() {
return localStorage.getItem('token');
}
function getAuthenticatedUser(onSuccess, onError) {
Restangular.one('api/authenticated_user?token=' + getCurrentToken()).get().then(function(response) {
onSuccess(response.user);
}, function(response) {
onError(response);
});
}
Restangular.setDefaultHeaders({
'Authorization': 'Bearer ' + getCurrentToken()
});
return {
checkIfLoggedIn: checkIfLoggedIn,
login: login,
logout: logout,
getCurrentToken: getCurrentToken,
getAuthenticatedUser: getAuthenticatedUser
};
}
]);
But when i call the userSvc.getAuthenticatedUser() method in a controller i get TypeError: onSuccess is not a function
This is how am calling it.
console.log(userSvc.getAuthenticatedUser());
What am i doing wrong. I am using angular 1.6
Error is expected, As your service method expected callback method so pass them
function getAuthenticatedUser(onSuccess, onError){
}
So you need to pass the callback methods
userSvc.getAuthenticatedUser(function() {}, function() {})
Or, You can check whether argument is defined and a angular.isFunction
function getAuthenticatedUser(onSuccess, onError) {
Restangular.one('api/authenticated_user?token=' + getCurrentToken()).get().then(function (response) {
!!onSuccess && angular.isFunction(onSuccess) && onSuccess(response.user);
}, function (response) {
!!onError && angular.isFunction(onError) &&
onError(response);
});
}
You HAVE to set a callback function as the first argument, because inside getAuthenticatedUser(), there is no check to see whether the onSuccess argument is set or not. (The same is actually true for the onError callback, so you need to provide that second argument, too)
So, either implement such a check (e.g. onSuccess && onSuccess()) inside the getAuthenticatedUser function, or provide a callback function (even if it's an anonymous one that doesn't to anything) in order to prevent that error.
userSvc.getAuthenticatedUser(function() {}, function() {});

Promise Angularjs 1.6 How can I force function to wait for api response

Im using this plugin: http://www.codingdrama.com/bootstrap-markdown/
I want to hook the onPreview
So on onPreview i try to make my api call:
app.directive("markdowntextarea",function ($http, $q) { // inject $q
return {
link: function (el_scope, element, attr) {
element.markdown(
{
autofocus: false,
savable: false,
onPreview: function (e) {
var deferred = $q.defer();
if (e.isDirty()) {
var originalContent = e.getContent();
$http({
url: '/api/markdown/',
data: {"body": originalContent, "actual_format": "md"},
method: 'POST'
}).then(function successCallback(response) {
console.log("successCallback", response.data.content);
deferred.resolve(response.data.content);
}, function errorCallback(response) {
console.log("errorCallback");
deferred.reject("error");
});
} else {
deferred.resolve("");
}
return deferred.promise;
}
}
);
}
}
});
Console:
successCallback from api!!!
I got success response from api, the response.data.content is what I want to use. The problem here is the return deferred.promise; always return the original value. What can I do here? I'm really new in angularjs
With promises you can't return values at once, usually you return promise object called promise handle and using 'then' clause you wait for promise to resolve(successfully) or reject(failure).
In your case if you want to wait for response and then do something I suggest you call onPreview and use its then clause like:
onPreview(e).then(function(response){}, function(error){});
onPreview is already returning promise which should be thenable.
After Edit:
So onPreview is API method and is expecting a text not promise, now what you can do is define a function like makePreview or something:
function makePreview(e) {
var deferred = $q.defer();
if (e.isDirty()) {
var originalContent = e.getContent();
$http({
url: '/api/markdown/',
data: {"body": originalContent, "actual_format": "md"},
method: 'POST'
}).then(function successCallback(response) {
console.log("successCallback", response.data.content);
deferred.resolve(response.config.data.body);
}, function errorCallback(response) {
console.log("errorCallback");
deferred.reject("error");
});
} else {
deferred.resolve("");
}
return deferred.promise;
}
and then your onPreview should look like this:
autofocus: false,
savable: false,
onPreview: function (e) {
makePreview(e).then(function(response){
e.setContent(response);
return response;
}, function(error){
return error;
});
}
I hope this helps :)
You cannot write angular in absolute sync way. Use the response in callback.
If you need to control api requests' order, or wait for multiple requests, see doc of $q

AngularJS - Pass Error from Service to Controller

Service.js
myService.serviceName = function (userId) {
return $http({
method: 'POST',
url: '/someUrl'
}).then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
console.log('Service errorCallback');
console.log(response);
});
};
Controller.js
myService.ControllerName(data.id)
.then(function successCallback(data) {
//do processing here
}, function errorCallback(response) {
toaster.pop({
type: 'error',
title: 'Display Error Message!'
});
});
In service, we are getting error status code in console viz -1, -2 and based on that code we are displaying custom error message to the user.
How do I pass error (message/code) from service to controller ?
The first thing that comes to my mind is to accept callbacks from the Controller.
myService.serviceName = function (userId) {
return $http({
method: 'POST',
url: '/someUrl'
})
};
And in your Controller:
myService.serviceName(123).then(function(response) {
// Do the processing.
}, function(error) {
// Check the error and change the UI accordingly.
});
If you need to make the processing within the service, you might want to use the $q service. In this case, you would need to inject it into your Service.
myService.serviceName = function (userId) {
var defer = $q.defer();
$http({
method: 'POST',
url: '/someUrl'
}).then(function (response) {
// Do the processing.
defer.resolve(response);
}).catch(function (error) {
defer.reject(error);
});
return defer.promise;
};
In your controller:
myService.serviceName(123).then(function(response) {
// Indicate that everything went OK.
}, function(error) {
// Check the error and change the UI accordingly.
});
You could add functionality to add callbacks to the service.
This way the controller can register its own callback method to the service. Which in its turn will call said callback method when the error occurs. Notifying the controller of the occurred error and desired message.

How do you deal with asynchronous return from $http.post in angularJS?

Stuck with a simple basic login problem here. My AuthService factory has following code inside of it (2 relevant functions and a local variable):
var username = '';
function login(uname, upwd, utype) {
// create a new instance of deferred
var deferred = $q.defer();
$http({
method: 'POST',
url: '/root',
headers: {
'Content-Type': 'application/json'
},
data: {
username: uname,
password: upwd,
type: utype
}
}).success(function(data, status, headers, config) {
if (status === 200) {
user = true;
username = data.username;
usertype = data.usertype;
deferred.resolve();
} else {
user = false;
deferred.reject();
}
})
.error(function(data, status, headers, config) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function getusername() {
return username;
}
My controller looks like this:
angular.module('smApp').controller('rootloginController', ['$scope', '$location', 'notificationFactory', 'AuthService',
function($scope, $location, notificationFactory, AuthService) {
$scope.submit = function() {
AuthService.login($scope.rEmail, $scope.rootPassword, 'root')
if (AuthService.isLoggedIn()) {
$location.url('/dashboard');
notificationFactory.success('Logged in as ' + rootEmail);
} else {
//ngNotifier.notifyError($scope.rEmail);
notificationFactory.error('Invalid username & password combination');
}
};
};
}]);
I am calling my getusername() in the if statementright after login() and since login has $http post it's asynchronous and I think im hitting a wall here.
So my main problem here is the first click always gives me error message and the second clicks logs me in. I am assuming this has to do with the promise not being fulfilled right away and taking some time to execute. I was wondering if there was anyway around this? I really dont have any other code to execute beside wait since this is a login page and using a timeout doesnt seem like the proper way to do it.
In this case you need to use the Promise API. Calls to the server made via the $http service return a promise, which allow binding of .success and .error methods.
The .then method may be used as a shorthand for both .success and .error. It accepts two functions that it executes in success and error scenarios respectively. Returning a promise in those functions allows chaining calls to the server.
In most cases, this should suffice:
// In service
login: function () {
return $http.post('your:url').then( // `then` here is optional, but possible
function () {}, // update service values without having to involve the controller (and/or transform the response object)
function () {} // throw error log mesages
)
}
// In controller
$scope.submit = function () {
AuthService.login().then(
function () {
// success logic: redirect, assign scope variables, etc
},
function () {
// error logic: allow retry
}
);
}
You have to call AuthService.isLoggedIn() after the login request has been completed. For this, first return the promise of the deferred object you created.
function login(uname, upwd, utype) {
// create a new instance of deferred
var deferred = $q.defer();
$http({
method: 'POST',
...
return deferred.promise;
}
Now, you can wait for the request to complete.
AuthService.login($scope.rEmail, $scope.rootPassword, 'root').finally(function() {
if (AuthService.isLoggedIn()) {
$location.url('/dashboard');
notificationFactory.success('Logged in as ' + rootEmail);
} else {
//ngNotifier.notifyError($scope.rEmail);
notificationFactory.error('Invalid username & password combination');
}
});

AngularJS $resource success and error callback

I have a simple code here that works however I wanted to display the success and error callbacks. This is my code:
angular
.module('studentInfoApp')
.factory('Student', Student)
.controller('StudentsController', StudentsController)
.config(config);
function config($routeProvider) {
$routeProvider
.when('/', {
controller: 'StudentsController',
templateUrl: 'app/views/student-list.html'
})
}
function Student($resource) {
return $resource('/students');
}
function StudentsController(Student, $scope, $http) {
Student.query(function(data) {
$scope.students = data;
});
}
As you can see the function Student() just returns the resource but I can't get the success and error callback if I use the .then for example. Am I missing something here? Thank you!
Here is a solution:
Student.query(function(data, headers) {
// OK
$scope.students = data;
}, function(response) {
// KO
});
Another one using the promise directly:
Student.query().$promise.then(function(response) {
// OK
$scope.students = response.data;
}, function(response) {
// KO
});
When using angular $resources you can just save the query directly to your variable. It will then hold the promise and when data returns the data itself.
If you need to handle success/error you can just use the saved promise and add success and error callbacks like below.
$scope.students = Student.query();
$scope.students.$promise.then( function(response) {
console.log('success');
}, function (error) {
console.log('error');
});
Managed to get it working, but if there are better ways to do it please feel free to post it also. This is my code now:
function StudentsController(Student, $scope) {
Student.query(function(data) {
$scope.students = data;
})
.$promise.then(function successCallback(response) {
console.log('success');
}, function errorCallback(error) {
console.log('error');
});
}

Categories

Resources