I have read several examples on the web and issues here on SO but I'm still missing something.
I have a service to fetch order data from my API. I want to resolve the promise inside the service. The console.log inside the service logs the correct data.
However, in my controller i get "TypeError: Cannot read property 'then' of undefined"
I thought the controller function would wait for the data to be resolved?
Service
angular.module('app')
.factory('orderService', function($http) {
// DECLARATIONS
var baseUrl = 'http://api.example.com/';
var method = 'GET';
var orderData = null;
return {
getOrderData: getOrderData
};
// IMPLEMENTATIONS
function getOrderData(ordernumber) {
// order data does not yet exist in service
if(!orderData) {
dataPromise = $http({
url: baseUrl + 'order/' + ordernumber,
method: method,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
// success
}).then(function(response) {
orderData = response.data;
console.log('Received data: ' + JSON.stringify(response.data));
return orderData;
},
// faliure
function(error) {
console.log("The request failed: " + error);
});
// order data exist in service
} else {
console.log('Data present in service: ' + orderData);
return orderData;
}
} // end: getOrderData function
}); // end: customerService
Controller
app.controller('orderController', function($scope, $stateParams, orderService) {
$scope.ordernumber = $stateParams.order;
orderService.getOrderData($scope.ordernumber)
// success
.then(function(response) {
$scope.order = response;
console.log('Controller response: ' + response);
},
// faliure
function(error) {
console.log("The request failed: " + error);
});
});
your function getOrderData doesn return a promise
function getOrderData(ordernumber) {
var deferred = $q.defer();
// order data does not yet exist in service
if(!orderData) {
dataPromise = $http({
url: baseUrl + 'order/' + ordernumber,
method: method,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
// success
}).then(function(response) {
orderData = response.data;
console.log('Received data: ' +
JSON.stringify(response.data));
deferred.resolve(orderData);
},
// faliure
function(error) {
deferred.reject(error);
console.log("The request failed: " + error);
});
// order data exist in service
} else {
console.log('Data present in service: ' + orderData);
deferred.resolve(orderData);
}
else {
deferred.reject('Not set!');
}
return deferred.promise;
} // end: getOrderData function
Related
I am relatively new to JavaScript and seems to have a problem getting success/error callback functions to work while using Axios.
For example, running the following integration test code using jest (npm test command), I get the output listed below. I am wondering why the message 'my-ping-2 success.' or 'my-ping-3 error: ...' are not being printed on the console. I am trying to make sure that the caller of the inner functions can optionally pass-in callback functions for success and error situations. What am I doing wrong? Thanks in advance!!
Details:
I know that the local API server works fine, it returns HTTP status 200 if I visit URL http://localhost:9090/api/v1/ping and tests via Postman. I have listed the full source code below that can reproduce the problem on my machine (MacOS, nodejs version v12.16.1, npm version 6.13.4).
I am using the generic axios(config) method in the inner function because I am using the same inner function for HTTP get/post calls. I hope that is OK.
jest console output
PASS src/__tests__/01_my.test.js
● Console
console.log src/__tests__/01_my.test.js:14
my-ping-1...
console.log src/__tests__/01_my.test.js:20
my-ping-4 done.
Source code for reproduction of problem
import axios from "axios";
import { isEmpty, merge } from 'lodash';
const baseURL = 'http://localhost:9090/api/v1/';
const headers = {
Accept: 'application/json',
};
const source = axios.CancelToken.source();
test('Test my-appcode', done => {
console.log('my-ping-1...');
fw_get_1('/ping', function(response) {
console.log('my-ping-2 success.');
}, function(error) {
console.log('my-ping-3 error: ' + fw_jsonFormatter(error));
} );
console.log('my-ping-4 done.');
done();
});
function fw_get_1(url, successCallback = null,
errorCallback = null) {
return fw_get_2(url, {}, successCallback, errorCallback);
}
function fw_get_2(url, configs = {},
successCallback = null,
errorCallback = null) {
url = encodeURI(url);
return fw_request_3('get', url, configs, successCallback, errorCallback);
}
function fw_request_3(method, url, configs = {},
successCallback = null,
errorCallback = null) {
let inputCfgs = {
params: configs.params,
data : configs.data,
headers : configs.headers
};
const axiosOptions = merge(
{},
{
method,
url,
baseURL,
headers,
cancelToken: source.token
},
inputCfgs
);
return axios(axiosOptions).then( function(response) {
if (successCallback) {
console.log('fw_request_internal success-1 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
successCallback(response);
} else {
console.log('fw_request_internal success-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
}
}).catch(function (error) {
if (errorCallback) {
console.log('Calling input errorCallback method: ' + axiosOptions.method + ' url: ' + axiosOptions.url + fw_jsonFormatter(error));
errorCallback(error);
} else {
console.log('fw_request_internal error-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
console.log(fw_jsonFormatter(error));
}
});
}
function fw_jsonFormatter(obj) {
return JSON.stringify(obj, null, 1);
}
For anyone who saw similar problem, it looks like the issue was that the test harness finished running before the callbacks could be called. I added a wait for a few seconds at the end of the test to confirm and now I am able to see all the console logs as expected. See the revised code below. I am writing some JS wrapper functions around REST APIs. Just want to make sure that the users of the wrapper functions can optionally override the callback functions. Please let me know if there is a better way to do this. Thank you!!
New console log
PASS src/__tests__/01_my.test.js (5.722s)
● Console
console.log src/__tests__/01_my.test.js:14
my-ping-1...
console.log src/__tests__/01_my.test.js:20
my-ping-4 done.
console.log src/__tests__/01_my.test.js:61
fw_request_internal success-1 method: get url: /ping
console.log src/__tests__/01_my.test.js:16
my-ping-2 success.
console.log src/__tests__/01_my.test.js:83
Waited 4 seconds
console.log src/__tests__/01_my.test.js:84
Finished test wait
Revised source code with wait at the end of tests.
import axios from "axios";
import {merge } from 'lodash';
const baseURL = 'http://localhost:9090/api/v1/';
const headers = {
Accept: 'application/json',
};
const source = axios.CancelToken.source();
test('Test my-appcode', done => {
console.log('my-ping-1...');
fw_get_1('/ping', function(response) {
console.log('my-ping-2 success.');
}, function(error) {
console.log('my-ping-3 error: ' + fw_jsonFormatter(error));
} );
console.log('my-ping-4 done.');
fw_test_end_wait(done, 'Finished test wait', 4 );
});
function fw_get_1(url, successCallback = null,
errorCallback = null) {
return fw_get_2(url, {}, successCallback, errorCallback);
}
function fw_get_2(url, configs = {},
successCallback = null,
errorCallback = null) {
url = encodeURI(url);
return fw_request_3('get', url, configs, successCallback, errorCallback);
}
function fw_request_3(method, url, configs = {},
successCallback = null,
errorCallback = null) {
let inputCfgs = {
params: configs.params,
data : configs.data,
headers : configs.headers
};
const axiosOptions = merge(
{},
{
method,
url,
baseURL,
headers,
cancelToken: source.token
},
inputCfgs
);
return axios(axiosOptions).then( function(response) {
if (successCallback) {
console.log('fw_request_internal success-1 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
successCallback(response);
} else {
console.log('fw_request_internal success-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
}
}).catch(function (error) {
if (errorCallback) {
console.log('Calling input errorCallback method: ' + axiosOptions.method + ' url: ' + axiosOptions.url + fw_jsonFormatter(error));
errorCallback(error);
} else {
console.log('fw_request_internal error-2 method: ' + axiosOptions.method + ' url: ' + axiosOptions.url);
console.log(fw_jsonFormatter(error));
}
});
}
function fw_jsonFormatter(obj) {
return JSON.stringify(obj, null, 1);
}
function fw_test_end_wait(done, msg, waitSecs) {
setTimeout(() => {
console.log('Waited ' + waitSecs + ' seconds');
console.log(msg);
done();}, waitSecs * 1000);
}
I have a simple login, once user is logged in I have added a call back to run another post so that I have access to the post json to use in my system.
I think the way I have done it is correct however I am getting error
GetData is not defined
Is this the correct way to do this
JavaScript
$scope.LogIn = function () {
$http({
url: "http://www.somesite.co.uk/ccuploader/users/login",
method: "POST",
data: $.param({'username': $scope.UserName, 'password': $scope.PassWord}),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
// success
console.log('success');
console.log("then : " + JSON.stringify(response));
GetData();
// location.href = '/cms/index.html';
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
};
$scope.UserData = function ($scope) {
$scope.UserName = "";
$scope.PassWord = "";
};
$scope.GetData = function () {
$http({
url: " http://www.somesite.co.uk/ccuploader/campaigns/getCampaign",
method: "POST",
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
// success
console.log('you have received the data ');
console.log("then : " + JSON.stringify(response));
location.href = '/cms/index.html';
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
};
You need to update your code to be $scope.GetData();.
Currently you are using GetData() which doesn't reference the same method. In fact it is undefined as per the error message.
I have a factory which returns token as
var accessToken = Restangular.all(url);
accessToken.one('token').get()
.then(function(res) {
deferred.resolve(res.data);
})
.catch(function(errRes) {
deferred.reject(errRes);
});
return deferred.promise;
In my header Interceptor, I need to get the token. Tried below code :
var accessToken;
$injector.get('tokenService').accessToken().then(function(res) {
accessToken = res.access_token;
}, function(e) {
// error
});
req.headers = _.extend({
'Authorization': 'Bearer ' + accessToken
}, req.headers);
Every time I get accessToken as undefined. There should be an easy way to achieve this.
Because you are forming req.headers outside accessToken()'s ajax call. Which tend to make your res.headers object with undefined access_token. Ideally you should wait until accessToken() ajax gets complete & set your res.headers code inside accessToken().then.
Factory
var accessToken = Restangular.all(url);
return accessToken.one('token').get()
.then(function(res) {
return res.data;
})
.catch(function(errRes) {
return errRes;
});
}
Interceptor
var accessToken;
$injector.get('tokenService').accessToken().then(function(res) {
accessToken = res.access_token;
req.headers = _.extend({
'Authorization': 'Bearer ' + accessToken
}, req.headers);
}, function(e) {
// error
});
I have a problem and don´t know how to solve it...
I have to authenticate a user in my IonicApp through a token based authentication. So i have to store the token inside the app, which shouldn´t be a problem...
The Problem is: How can i get the token?
Here´s my code:
// Alle Aufrufe an die REST-Api werden hier durchgeführt
var httpCall = {
async : function(method, url, header, params, data) {
// if (url != 'login') {
// header['X-Auth-Token'] = userTokenFactory.getUserToken();
// }
//console.log(header['X-Auth-Token']);
var ipurl = "IPURL";
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http({
method : method,
url : ipurl + url,
//headers : header,
params : params,
data : data,
config : {
timeout : 5000
}
}).then(function successCallback(response) {
//console.log("data:" + response.data);
//console.log("header:" + response.headers);
console.log("token:" + response.headers['X-AUTH-TOKEN']);
//console.log(response.data.token);
console.log("token" + repsonse.token);
// TRY TO READ THE X_AUTH_TOKEN HERE !!!!!!!!!!!!
return response;
}, function errorCallback(response) {
return response;
});
// Return the promise to the controller
return promise;
}
};
return httpCall;
});
And here´s a picture of the Response from the Server (from Firefox). As you can see, the X-Auth-Token is there...
here´s the x-auth-token
Thanks for the help!!
There are lot of articles are available over handling authentication in AngularJS. This article is the one perfect suitable in your case.
So you can get token from your request as,
}).then(function successCallback(response) {
console.log("data:" + response.data);
$window.sessionStorage.token = response.data.token;
return response;
}, function errorCallback(response) {
return response;
});
Now we have the token saved in sessionStorage. This token can be sent back with each request by at least three ways
1. Set header in each request:
`$http({method: 'GET', url: url, headers: {
'Authorization': 'Bearer ' + $window.sessionStorage.token}
});`
2. Setting defaults headers
`$http.defaults.headers.common['X-Auth-Token'] = 'Bearer ' + $window.sessionStorage.token;`
3. Write Interceptor:
Interceptors give ability to intercept requests before they are
handed to the server and responses before they are handed over to the
application code that initiated these requests
myApp.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
Refer AngularJS $http guide for detailed explanation.
As you are getting response.data null and image demonstrates that headers are being returned, I would suggest you to check if you are getting data with
response.headers(),
if then try with response.headers()["X_AUTH_TOKEN"].
I've impelmented the httpInterceptor found here.
If my Basic Auth header has valid credentials, everything works fine, but if I get back a 401, then the application just hangs and I never receive a response.
Here's my interceptor:
angular.module('TDE').factory('httpInterceptor', function httpInterceptor ($q, $window, $location) {
return function (promise) {
var success = function (response) {
//window.logger.logIt("httpInterceptor received a good response: " + response);
return response;
};
var error = function (response) {
//window.logger.logIt("httpInterceptor received an error: " + response);
if (response.status === 401) {
$location.url('/login');
}
return $q.reject(response);
};
return promise.then(success, error);
};
});
Declaring the httpInterceptor in app.js
angular.module('TDE', []);
var TDE = angular.module('TDE', ['ux', 'ngRoute', 'ngResource', 'TDE', 'hmTouchEvents', 'infinite-scroll', 'ui.bootstrap', 'ui.sortable']);
TDE.config(['$routeProvider', '$locationProvider', '$httpProvider', function ($routeProvider, $locationProvider, $httpProvider) {
$httpProvider.responseInterceptors.push('httpInterceptor');
$routeProvider
.when('/', {})
.when('/login', { templateUrl: "Views/Login/login.html", controller: "LoginController" })
And my authenticate method
authenticate: function (user, password) {
// window.logger.logIt("serviceAccount: " + $rootScope.serviceAccount);
window.logger.logIt("In authenticate...");
var deferred = $q.defer();
var encoded = encoder.encode($rootScope.serviceAccount);
//var encoded = encoder.encode(user + ":" + password);
if (user && password) {
window.logger.logIt("We've got a username and password...");
$http.defaults.headers.common.Authorization = 'Basic ' + encoded;
sessionStorage.setItem('Authorization', $http.defaults.headers.common.Authorization);
var url = $rootScope.serviceBaseUrl + "login/authenticate";
window.logger.logIt(url);
$http({
method: "POST",
url: url,
data: {
"Username": user,
"Password": password,
"AccessToken": ""
},
headers: {
"Content-Type": "application/json"
}
})
.success(function (data, status, headers, config) {
window.logger.logIt("We've got a response (success)...");
if (data.IsAuthenticated) {
deferred.resolve(data);
session.setSession();
} else {
deferred.reject(status);
}
})
.error(function (data, status, headers, config) {
window.logger.logIt("We've got a response (error)...");
$dialogservice.showMessage("Authentication Error", "Return Code: " + status);
deferred.reject(status);
});
} else {
window.logger.logIt("We've got a response...");
deferred.reject(401);
}
return deferred.promise;
},
You'll see that in my Authenticate method, there are two lines that I'm testing:
var encoded = encoder.encode($rootScope.serviceAccount);
and
var encoded = encoder.encode(user + ":" + password);
We are REQUIRED to use Basic Authentication (which is over SSL). Right now, all I'm testing is that I can receive a 401 back. If I use the $rootScope.serviceAccount (which is working), I get a 200 response right away. But if I purposely send a bad username/password, I NEVER get a response, the application just sits there.
Edit: Ok, I've updated my code to the following, and still getting the same behavior:
angular
.module('TDE')
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (config) {
window.logger.logIt("Request is being sent...");
var headers = config.headers;
if (!headers.Authorization) {
headers.Authorization = sessionStorage.getItem('Authorization');
}
return config || $q.when(config);
},
'response': function (response) {
window.logger.logIt("got a good response...");
return response;
},
'responseError': function (rejection) {
window.logger.logIt("responseError error...");
return $q.reject(rejection);
},
};
});
}]);
Well, again, PhoneGap is the issue!!!!!!!!!!!!!!!!!
Example 1
Example 2
Example 3
Example 4
Try this interceptor:
.factory('httpInterceptor', function(){
return {
request : function(yourRequestConfig){
var returnPromise = $q.defer();
$http(yourRequestConfig).then(function(response) {
console.log("successful response from server ", response);
returnPromise.resolve(response);
}, function(someReason) {
console.log("failure response from server", reason);
returnPromise.reject(reason);
});
return returnPromise.promise;
}
}
});
used as
httpInterceptor.request(request config).then(returnValue){
console.log('inside controller', returnValue);
});
where request config is something like:
var requestConfig = {
method: "GET",
url: "localhost:8080/getStuff"
};