I have an AngularJS (v1.5) client that is used to schedule an Operating Room at a hospital. The app has a single controller and two functions within that controller. These two functions are both $http POST requests.
The first method called (requestAccessToken) passes a username, password, and grant_type to receive back an OAuth 2 access token which is then assigned to a variable on the scope. This works fine.
The second method (scheduleORSuite) makes an API call passing the access token obtained from the first method (requestAccessToken) . The problem is that when the method () executes the access token is null. I know I am receiving back a valid access token because when I call the method requestAccessToken directly I get back a valid access token. When I step through the debugger in Chrome it looks like the method (scheduleORSuite), that uses the access token, does not even wait for the method that obtains the access token to return.
<script type="text/javascript">
var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);
scheduleORSuiteApp.controller('ScheduleORSuiteController', function ($scope, $http) {
var viewModel = this;
viewModel.accessToken = null;
viewModel.userName = 'theUserName';
viewModel.password = 'thePassword';
// This method requests the access token
viewModel.requestAccessToken = function () {
$http({
method : 'POST',
url: 'https://api.myserver.net/oauth/token',
data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(_requestAccessTokenSuccess, _requestAccessTokenError);
};
// This method contacts the API endpoint the schedule an OR suite
viewModel.scheduleORSuite = function() {
viewModel.requestAccessToken();
if (viewModel.accessToken) {
return; // viewModel.accessToken is null. Exit the method
}
$http({
method : 'POST',
url: 'https://api.myserver.net/api/scheduleOrSuite',
data : angular.toJson(viewModel.form),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + viewModel.accessToken
}
}).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
};
// Called when request for access token is successful
function _requestAccessTokenSuccess(response) {
viewModel.tokenDisplay = response.data;
};
// Called when request for access token results in error
function _requestAccessTokenError(response) {
viewModel.tokenDisplay = 'An error occured: ' + response.status;
};
// Called when scheduling of operating suite is successful
function _scheduleORSuiteSuccess(response) {
viewModel.accessToken = response.data.access_token;
};
// Called when scheduling of operating suite results in error
function _scheduleORSuiteError(response) {
viewModel.tokenDisplay = 'An error occured: ' + response.data;
};
});
</script>
Here is the HTML form that makes use of the controller.
<form ng-submit="viewModel.scheduleORSuite()" novalidate>
...
...
</form>
Is there a way to make the second method (scheduleORSuite) wait until the first method (requestAccessToken) completes? The access token is required in order to call the API to schedule an OR suite.
$http is an async method, so you need to use callbacks
var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);
scheduleORSuiteApp.controller('ScheduleORSuiteController', function($scope, $http) {
var viewModel = this;
viewModel.accessToken = null;
viewModel.userName = 'theUserName';
viewModel.password = 'thePassword';
viewModel.requestAccessToken = function() {
viewModel._requestAccessToken().then(_requestAccessTokenSuccess, _requestAccessTokenError);
};
viewModel.scheduleORSuite = function() {
if (viewModel.accessToken) {
viewModel._scheduleORSuite.then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
} else {
viewModel._requestAccessToken().then(function(response) {
viewModel.tokenDisplay = response.data;
viewModel._scheduleORSuite.then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
}, _requestAccessTokenError);
}
};
// This method contacts the API endpoint the schedule an OR suite
viewModel._scheduleORSuite = function() {
return $http({
method: 'POST',
url: 'https://api.myserver.net/api/scheduleOrSuite',
data: angular.toJson(viewModel.form),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + viewModel.accessToken
}
});
};
// This method requests the access token
viewModel._requestAccessToken = function() {
return $http({
method: 'POST',
url: 'https://api.myserver.net/oauth/token',
data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
};
// Called when request for access token is successful
function _requestAccessTokenSuccess(response) {
viewModel.tokenDisplay = response.data;
};
// Called when request for access token results in error
function _requestAccessTokenError(response) {
viewModel.tokenDisplay = 'An error occured: ' + response.status;
};
// Called when scheduling of operating suite is successful
function _scheduleORSuiteSuccess(response) {
viewModel.accessToken = response.data.access_token;
};
// Called when scheduling of operating suite results in error
function _scheduleORSuiteError(response) {
viewModel.tokenDisplay = 'An error occured: ' + response.data;
};
});
It's because your requestAccessToken() method contains a promise, and JavaScript won't wait for this to complete before carrying on with the rest of the code.
The best thing to do would be to return the promise from requestAccessToken() for use in scheduleORSuite()
viewModel.requestAccessToken = function () {
return $http({
method : 'POST',
url: 'https://api.myserver.net/oauth/token',
data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
};
viewModel.scheduleORSuite = function() {
viewModel.requestAccessToken().then(function(response) {
viewModel._requestAccessTokenSuccess(response);
if (viewModel.accessToken) {
return; // viewModel.accessToken is null. Exit the method
}
$http({
method : 'POST',
url: 'https://api.myserver.net/api/scheduleOrSuite',
data : angular.toJson(viewModel.form),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + viewModel.accessToken
}
}).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
}, _requestAccessTokenError);
};
I've also noticed though that the variable you're checking for your access token, viewModel.accesstoken, is being set until the _scheduleORSuiteSuccess() function? I'm presuming this is an error and it should be set in _requestAccessTokenSuccess() instead?
What you want todo is make sure the request returns with the access token first, then make the second request. Todo that, use a promise. the $q library works well for this. See viewModel.requestAccessTokenfor the changes I made.
<script type="text/javascript">
var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);
scheduleORSuiteApp.controller('ScheduleORSuiteController', function($scope, $http, $q) {
var viewModel = this;
viewModel.accessToken = null;
viewModel.userName = 'theUserName';
viewModel.password = 'thePassword';
// This method requests the access token
viewModel.requestAccessToken = function() {
var defer = $q.defer();
$http({
method: 'POST',
url: 'https://api.myserver.net/oauth/token',
data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(response) {
viewModel.tokenDisplay = response.data;
defer.resolve(); //fullfills the promise
}, function(err) {
viewModel.tokenDisplay = 'An error occured: ' + response.status;
defer.reject(); //rejects the promise
});
return $q; //returns a promise
};
// This method contacts the API endpoint the schedule an OR suite
viewModel.scheduleORSuite = function() {
viewModel.requestAccessToken().then(function() {
//On fullfillment of the promise from requestAccessToken...
if (viewModel.accessToken) {
return; // viewModel.accessToken is null. Exit the method
}
$http({
method: 'POST',
url: 'https://api.myserver.net/api/scheduleOrSuite',
data: angular.toJson(viewModel.form),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + viewModel.accessToken
}
}).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
}, function() {
//Error occured in requestAccessToken();
})
};
// Called when scheduling of operating suite is successful
function _scheduleORSuiteSuccess(response) {
viewModel.accessToken = response.data.access_token;
};
// Called when scheduling of operating suite results in error
function _scheduleORSuiteError(response) {
viewModel.tokenDisplay = 'An error occured: ' + response.data;
};
});
</script>
Related
I am sending an object using AJAX to a Django view. The data I am sending is mouse movement which is sent every 10 seconds to the server to be saved. Now, I have no problem reading my data from the client and server sides. The data gets saved in the database, but I get a 500 error message every time the function sends to the server gets executed. I tried to use fetch and got this error message:
POST error: SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
I searched about this error, and my understanding is that the problem is with the data type, but I am unsure how I can trace the problem and fix it. Could someone help me troubleshoot this issue?
here is my js function:
var target_id = '';
var eventsLog = {"mouse": []};
function logMouse(event){
target_id = event.target.id;
currDate = new Date()
start_time = currDate.getHours() + ':' + currDate.getMinutes() + ':' + currDate.getSeconds() + ':' + currDate.getMilliseconds();
var insert = [start_time, target_id];
(eventsLog.mouse).push(insert);
}
var timesPerSecond = 5;
var wait = false;
$(document).on('mousemove', function (event) {
if (!wait) {
logMouse(event);
wait = true;
setTimeout(function () {
wait = false;
}, 1000 / timesPerSecond);
}
});
const post_url = server_url;
function sendMovement() {
/* fetch(post_url, {
method: 'POST',
body: JSON.stringify(eventsLog),
credentials: 'include',
headers: {'Content-Type': 'application/json'}
}).then(res => res.json()).then(response => {
console.log('POST response:', response);
}).catch(error => {
console.log('POST error:', error);
});*/
$.ajax({
type: "POST",
url: server_url,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(eventsLog),
dataType: "json",
success: function () {
},
error: function (req, textStatus, errorThrown){
console.log('Ooops, something happened: ' + textStatus + ' ' +errorThrown)
}
});
and this is my Django view:
movement = json.loads(request.body.decode('utf-8'))
and I checked the data type received in Django and it is a dictionary.
Retrieve csrftoken from cookies and attach it on the request header. Use setInterval() to send the request every ten seconds. Of course you can implement with Fetch API, but since you were using jQuery on your movement function, I also used it to send an AJAX.
get_mouse_movement.html
(just the script part)
<script>
var target_id = '';
var eventsLog = {"mouse": []};
var timesPerSecond = 5;
var wait = false;
function getCookie(name) {
...
}
function logMouse(event){
target_id = event.target.id;
currDate = new Date()
start_time = currDate.getHours() + ':'
+ currDate.getMinutes() + ':'
+ currDate.getSeconds() + ':'
+ currDate.getMilliseconds();
console.log([start_time, target_id])
eventsLog.mouse.push([start_time, target_id]);
};
$(document).on('mousemove', function (event) {
if (!wait) {
logMouse(event);
wait = true;
setTimeout(function () {
wait = false;
}, 1000 / timesPerSecond);
}
});
function sendLog() {
$.ajax({
type: "POST",
url: '/your/url/',
headers: {'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json'},
data: JSON.stringify(eventsLog),
success: function(data) {
console.log(data.message);
eventsLog.mouse = [];
},
});
};
setInterval(function(){
sendLog();
}, 10000);
</script>
views.py
import json
from django.http import JsonResponse
def mouse_movement(request):
if request.method == 'POST':
data = json.loads(request.body)
print(data)
return JsonResponse({'message': 'log is saved'})
return render(request, 'get_mouse_movement.html')
.loads() accepts, str, bytes or bytearray. You do not need to decode it.
The config file contains an object:
var config = {
uri: 'https://localhost:1234',
postcodeTimeoutMillsecs: process.env.POSTCODE_TIMEOUT || 3000
};
module.exports = config;
My http call gives me an error saying my must be a string, which I'm pretty sure it is. The console.log tells me I'm passing 'http://localhost:1234', which is what I expect.
const fs = require('fs');
const config = require('../config/config');
const defaultHttp = require('axios');
const ono = require('ono');
exports.submitApplication = (data) => {
console.log('config.uri=');
console.log(config.uri);
return new Promise((resolve, reject) => {
let http = defaultHttp;
http.options({
method: 'post',
url: config.uri,
data: data,
headers: {
'Content-Type': 'application/json'
},
}).then(function (response) {
resolve(response);
}).catch(function (error) {
var submissionErr;
if (error.type === 'http') {
// error received from DRSI Communicator
submissionErr = ono({ status: error.code },
'Submission failed - ' + error.toString() +
' - ' + (error.body.message || error.body.errors.join()));
console.log(submissionErr);
} else {
submissionErr = ono({ status: 503 },
'Submission failed - internal connectivity problem - ' + error.toString());
}
submissionErr.errorType = 'submission-failure';
reject(submissionErr);
});
});
};
The full error message is:
message: 'Submission failed - internal connectivity problem - TypeError [ERR_INVALID_ARG_TYPE]: The "url" argument must be of type string. Received an instance of Object'
Any idea where I’m going wrong?
It seems like you are using the wrong method.
The argument list for http.options is: axios.options(url[, config]).
http.options({
method: 'post',
url: config.uri,
data: data,
headers: {
'Content-Type': 'application/json'
},
})
should probably be
http({
method: 'post',
url: config.uri,
data: data,
headers: {
'Content-Type': 'application/json'
},
})
or
http.post(config.uri, data {
headers: {
'Content-Type': 'application/json'
},
})
Source:
https://github.com/axios/axios#axiosoptionsurl-config
I am working on an angular app where I want to integrate OfflineJS functionality.
I have created a general service for getting and posting data to/from an API,and a specific service for each module.
Here is the code
app.service('MethodProvider', function ($http) {
var self = this;
self.get = function (url) {
var obj = {
url: url,
method: 'GET',
async: true,
headers: {
'Content-Type': 'application/json'
}
};
return $http(obj);
};
self.post = function (url, data) {
var obj = {
url: url,
method: 'POST',
async: true,
data: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
};
return $http(obj);
};
self.put = function (url, data) {
var obj = {
url: url,
method: 'PUT',
async: true,
headers: {
'Content-Type': 'application/json'
}
};
if (typeof data != 'undefined' && data != null) {
obj.data = JSON.stringify(data);
}
return $http(obj);
};
self.delete = function (url) {
var obj = {
url: url,
method: 'POST',
async: true,
headers: {
'Content-Type': 'application/json'
}
};
return $http(obj);
};
return self;
});
And a specific module service like User module
app.service('UserSrvc', function (MethodProvider) {
var self = this;
self.create = function (data) {
var url = apiUrl + '/user/add';
return MethodProvider.post(url, data);
};
return self;
});
How do I integrate OfflineJS in this code , I want to intercept HTTP request when network connectivity is down and resume requests when network connectivity is up . I have studied this example but unable to integrate this in angular need an example to get started.
hope this helps the future users:
Offlinejs adds Offline object to the global window object.
Offlinejs does not trigger check() by itself (it does it once if checkOnLoad option is set to true).
You may call Offline.check() just before you make an ajax request to your server and check for connection.
You can also do polling, if your app is xhr intensive.
var run = function(){
if(Offline.state=="up")
Offline.check();
};
setInterval(run, 20000); // check after 20sec
Offline.on('down',function(){ /**code to handle network down*/ });
As pointed by #Adnan Umer You need to set Offline.options = { interceptRequests: true, requests: true }
to intercept failed requests and try again once connection is back.
One caveat here is that the requests resent by Offline will be out of the scope of Angular, accordingly, all the GET requests will result to nothing. Which is actually fine, usually the user initiates the get requests.
Offline.options = {
checks: {xhr: {url: "put_url_to_check_status_of_your_server"}}
}
By default, Offlinejs checks for favicon.ico.
I'm trying to make an API call to Zendesk's API and I keep getting a 401 auth code even though the same thing works when I do a cURL in terminal. How can I make this work in Angular?
function dataservice($http) {
var service = {
getMacros: getMacros
};
return service;
/////////////////////
function getMacros() {
var client = {
username: window.btoa('myEmail'),
token: window.btoa('/token:myToken'),
remoteUri: 'https://myCompany.zendesk.com/api/v2/macros.json'
};
console.log('Loading...');
return $http({
method: 'GET',
url: client.remoteUri,
headers: {
'Authorization': client.username + client.token
}
})
.then(getMacrosComplete)
.catch(function (message) {
exception.catcher('Failed to getMacros')(message);
});
function getMacrosComplete(response) {
console.log('Done');
var data = response.data;
return data;
};
};
The code above always returns a 401 while this works:
curl myEmail/token:myToken https://myCompany.zendesk.com/api/v2/macros.json
Seems to work well. Probably something obvious.
The thing is you need to set Authorization headers properly, they should be Base64 encoded and you have to state "Basic" authentication (this is the main part most people miss).
So this should work:
function dataservice($http) {
var service = {
getMacros: getMacros
};
return service;
/////////////////////
function getMacros() {
var client = {
username: 'myEmail',
token: 'myToken',
remoteUri: 'https://myCompany.zendesk.com/api/v2/macros.json'
};
console.log('Loading...');
return $http({
method: 'GET',
url: client.remoteUri,
headers: {
'Authorization': 'Basic ' + window.btoa(client.username + '/token:' + client.token)
}
})
.then(getMacrosComplete)
.catch(function (message) {
exception.catcher('Failed to getMacros')(message);
});
function getMacrosComplete(response) {
console.log('Done');
var data = response.data;
return data;
};
};
Of course you have to have token authentication enabled in your Zendesk account, otherwise you can authenticate via user + password by setting up a password instead and doing:
'Authorization': 'Basic ' + window.btoa(client.username + ':' + client.password)
I am trying to send the http response as a JSON body to an error handler if an error occurs. I am not really sure how to do this as I am a little inexperienced in this area. Here is the relevant code that I have currently:
Controller:
for (var prop in $scope.applicants) {
var applicant = $scope.applicants[prop];
$scope.sendApplicantsToSR(applicant).then(null, $scope.sendATSError.bind(null, applicant));
}
$scope.sendATSError = function (applicant, error) {
return AtsintegrationsService.applicantErrorHandling(applicant.dataset.atsApplicantID);
};
$scope.sendApplicantsToSR = function(applicant) {
return AtsintegrationsService.sendApplicantsToSR(applicant);
};
Service:
srvc.sendApplicantsToSR = function (applicant) {
var applicantURL = {snip};
return $http({
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
url: applicantURL,
data: applicant
});
};
srvc.applicantErrorHandling = function (applicantID, error) {
var url = srvc.url + {snip};
return $http({
method: 'POST',
url: url,
data: { "error_message": error }
});
};
So, ideally, I would like to pass the result of $scope.sendApplicantsToSR to $scope.sendATSError only when an error occurs.
Inside your controller
YourService.getdatafromservice().then(function(SDetails) {
//response from service
console.log(SDetails);
});
Inside your service
return {
getData: getData
};
function getData() {
var req = $http.post('get_school_data.php', {
id: 'id_value',
});
return req.then(handleSuccess, handleError);
function handleSuccess(response) {
response_data = response.data;
return response_data;
}
function handleError(response) {
console.log("Request Err: ");
}
}