Access events of XHR2 in Collection - javascript

I have a Collection that sends files successfully to the server with XMLHttpRequest.
But I cannot figure out how to attach functions to the XHR2 events.
It only seems to be working when the code is directly inside of send():
var Photos = Backbone.Collection.extend({
url: config.url,
/**
* Send file to server.
* #todo Should Backbone.sync be overwritten instead?
*/
send: function (file) {
var data = new FormData(),
xhr = new XMLHttpRequest();
// ======> Doesn't work:
xhr.addEventListener('load', this.onLoad(xhr));
// ======> Doesn't work either:
xhr.onload = this.onLoad(xhr);
// ======> But this works:
xhr.onload = function () {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Works!
};
data.append('file', file);
xhr.open('POST', this.url);
xhr.send(data);
},
/**
* Respond to XHR2 'onload' event.
*/
onLoad: function (xhr) {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Doesn't work!
}
});
Why is that so, and how can I move the code outside of send() and into a separate function?

You're calling the function with this.onLoad(xhr) rather than passing a function reference. Try
var self = this;
xhr.onload = function () {
self.onLoad(xhr);
};

So, thanks to Musa and Jonathan Lonowski I now have following, working code:
var Photos = Backbone.Collection.extend({
url: config.url,
/**
* Send file to server.
* #todo Should Backbone.sync be overwritten instead?
*/
send: function (file) {
var data = new FormData(),
xhr = new XMLHttpRequest();
xhr.addEventListener('load', this.onLoad);
data.append('file', file);
xhr.open('POST', this.url);
xhr.send(data);
},
/**
* Respond to XHR2 'onload' event.
*
* No need to pass in the xhr object, since addEventListener
* automatically sets 'this' to 'xhr'.
*/
onLoad: function () {
var response = $.parseJSON(xhr.responseText);
console.log(response); // Works now!
}
});

Related

Running then method after XMLHttpRequest done

I have a method acting like an async method. After the request sends to the function that was called this request, I want to run something like the then method but then there is no then method for XMLHttpRequest.
the caller function in below code has no then method
let result = dataService.exportfile('get', '/api/overtimeedari/exporttoexcle/', model).
then(() => {
self.loading(false);//غیرفعال کردن حالت لود شدن گرید
buttonListSearch.Excel.loading(false); //غیرفعال کردن حالت لود شدن دکمه اکسل
});
the function called
function exportfile(mehtodtype, url, model) {
debugger;
var qs = "?";
model.map((item) => {
qs = `${qs}${item.name}=${item.value}&`;
});
var request = new XMLHttpRequest();
request.open(mehtodtype, url + qs, true);
request.setRequestHeader('Authorization', "Bearer " + window.localStorage.getItem('token'));
request.responseType = 'blob';
request.onload = function (e) {
if (this.status === 200) {
var blob = this.response;
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, fileName);
}
else {
var downloadLink = window.document.createElement('a');
var contentTypeHeader = request.getResponseHeader("Content-Type");
downloadLink.href = window.URL.createObjectURL(new Blob([blob], { type: contentTypeHeader }));
downloadLink.download = "Export.xls";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
};
request.send();
return request;
}
Given the constraint of not changing exportfile function as per comment
the case is I don't have this ability to change theexportfile function because it has side affects on other functions
the best way to handle this is as follows
let req = dataService.exportfile('get', '/api/overtimeedari/exporttoexcle/', model);
req.addEventListener('loadend', () => {
// do what's needed here
});
since exportfile returns the XMLHttpRequest object, you can listen for the loadend event and do whatever it you're doing there
Note, the loadend event is triggered regardless of success or failure
You could do the above with the load event if you want too - but, I'm unsure what order
x.onload=() => {};
x.addEventListener('load', () => {});
are fired ... also note, do NOT
req.onload=() => {};
since that would overwrite the onload callback inside the function

Add header to XMLHttpRequest (even when encapsulated in IIFE)

In this question I asked for a way to automatically add headers to XMLHttpRequest objects. I tried to implement this solution:
(function(open) {
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
this.addEventListener("readystatechange", function() {
if (this.readyState == 4)
{
console.log(this.status);
}
}, false);
open.call(this, method, url, async, user, pass);
this.setRequestHeader("Authorization", "Token 123")
};
})(XMLHttpRequest.prototype.open);
This is working working in principle, but not in my case: I want to add a header to some vendor code that is encapsulated using the IIFE pattern:
H5PEditor.FileUploader = (function ($, EventDispatcher) {
// ...
self.upload = function (file, filename) {
var formData = new FormData();
formData.append('file', file, filename);
// ...
var request = new XMLHttpRequest();
// ...
request.open('POST', H5PEditor.getAjaxUrl('files'), true);
request.send(formData);
self.trigger('upload');
};
// ...
})(H5P.jQuery, H5P.EventDispatcher);
If I add the code from the first code sample inside this IIFE, the header is added. But is there a way to accomplish the same without changing vendor code?

Match a curl request to NancyFx service from browser js

I've got a microservice (in NancyFx) that works as expected for the following cURL command:
curl --verbose --form file=#"C:\test\image.png" http://localhost:8080/file/upload
The file shows up inside Nancy as expected at:
context.Request.Files[0] //The file bytes are here, yeah!!
Now I've got to get my web-client to send a selected file to the same service, in the same way.
TLDR; You can skip my failure examples below if you'd like
I've tried several versions of the following with no success:
var uploadForm = new FormData();
// Add the file to the request.
uploadForm.append("file", file, name);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:8080/file/upload', true);
xhr.onload = function() {
if (xhr.status === 200)
alert('File ' + name + 'Upload successfully!!!');
else errorFn("Upload Failure on file " + name);
};
//Send the data
xhr.send(uploadForm);
and several versions of the following, also with no success:
var postConfig = {
url: 'http://localhost:8080/file/upload',
data: uploadForm,
processData: false, //tries without this as well
type: "POST",
dataType: "application/x-www-form-urlencoded; charset=UTF-8",
cache: false,
success: successFn,
error: errorFn
}
$.ajax(postConfig);
I've tried the above with
file = direct reference to $('MyFileSelectorControl').files[0]
file being set with the following code:
var reader = new FileReader();
reader.onload = function (result) {
var fileContent = new Uint8Array(result.target.result);
var encContent = new SP.Base64EncodedByteArray();
for (var b = 0; b < fileContent.length; b++) {
encContent.append(fileContent[b]);
}
<Set file = encContent and send AJAZ as above>
};
reader.readAsArrayBuffer(fileInput);
Setting file = to fileContent above (IOW, after it has been through the reader, but not encoded.)
How do I submit this file from javascript (jQuery or standard) so that it works like it does from cURL?
Here is the code that finally worked. (Admittedly, I had thought I had already tried this and said so in my question)
var file = $('MyFileSelectorControl').files[0];
var uploadForm = new FormData();
// Add the file to the request.
uploadForm.append("file", file, name);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:8080/file/upload', true);
xhr.onload = function() {
if (xhr.status === 200)
alert('File ' + name + 'Upload successfully!!!');
else errorFn("Upload Failure on file " + name);
};
//Send the data
xhr.send(uploadForm);

How to I refer to the `postMessage()` function from my XHR callbacks?

The file uploads work perfectly, I can't get the progess and load events to callback
I have the following in a JavaScript file as my WebWorker code:
UploadFileWorker.js
function uploadFile(url, m) {
var f = m.file;
var fd = new FormData();
fd.append('file', f, f.name);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
m.set('status', (e.loaded / e.total) * 100 );
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.upload.addEventListener('load', function (e) {
m.set('status',100);
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.open('POST', url, true);
xhr.send(fd);
}
function getUploadURL(m) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
var url = this.responseText;
uploadFile(url,m);
});
xhr.open('GET', '/upload', false);
xhr.send();
}
var q = [];
onmessage = function(e) {
postMessage(e.data);
q.push(e.data);
while (q.length > 0) {
var m = q.pop();
getUploadURL(m);
}
};
The postMessage(e.data); in the onmessage function work perfectly.
The postMessage(m); in the xhr.upload.addEventListeners() callbacks never happen. They worked fine before I tried and move these functions into WebWorker code.
Is this a scope issue? If so, how do I prefix the calls to get them to work.?
For completeness here is how I am defining my instance of Worker and kicking it off.
onButtonClick: function(button, e, eOpts) {
var ugv = this.getUploadGridView();
var fs = this.getFileUploadStore();
var worker = new Worker('/js/UploadFileWorker.js');
worker.onmessage = function(e) {
console.log(e);
};
var selected = ugv.getSelectionModel().getSelection();
Ext.each(selected, function(m) {
var o = {};
o.name = m.get('name');
o.size = m.get('size');
o.status = m.get('status');
o.file = m.file;
worker.postMessage(o);
});
},
Again the actual uploading of the files works great, I can't figure out how to call postMessage(); from inside the xhr callbacks.
This is apparently a bug that has been fixed just recently.
Issue 71433002: Enable XHR upload progress events for Workers. (Closed)
Workaround until Chrome gets updated
xhr.addEventListener(
instead of
xhr.upload.addEventListener(
This has the drawback that progress only gets called for every 1MB, files smaller than 1MB never get a progress event fired.

Download Binary Data as a File Via Javascript

I'm making an ajax call to an API that returns binary data. I'm wondering if its possible to take that binary data and display it for the client in a new window? This is what I'm doing right now. The problem is, the document opens up, but its completely blank.
$.ajax({
type: "POST",
url: apiURL,
data: xmlRequest,
complete: function(xhr, status) {
var bb = new window.WebKitBlobBuilder();
// Append the binary data to the blob
bb.append(xhr.responseText);
var blobURL = window.webkitURL.createObjectURL(bb.getBlob('application/pdf'));
window.open(blobURL);
}
});
Any ideas?
Okay, I figured it out. I had to specify the responseType as 'array buffer':
function downloadPDF() {
var xhr = new XMLHttpRequest();
xhr.open('POST', API_URL, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var bb = new window.WebKitBlobBuilder();
bb.append(this.response); // Note: not xhr.responseText
var blob = bb.getBlob('application/pdf');
var blobURL = window.webkitURL.createObjectURL(blob);
window.open(blobURL);
}
};
xhr.send(createRequest());
}

Categories

Resources