Trouble with running function before FileReader - javascript

I've got some code running within a Vue.js component, but I get the following error:
TypeError: Cannot read property 'then' of undefined
I just want to run loadingAnimation() before the filereading, but searching on the internet for this error didn't really help me. What is the best way of doing this?
This is my code:
openFileBrowse() {
var vm = this;
var input = document.getElementById("filebutton");
if (input.files[0].type != "application/vnd.ms-excel"){
alert("You have uploaded a wrong file type. We require a .csv file not a " + input.files[0].type + " file.");
} else {
//Update loader text
vm.updateLoader('parsing csv');
//Start loadscreen
vm.loadingAnimation().then(function () {
//Start reading data
var reader = new FileReader();
var csvData = "";
var jsonData;
var iconv = require('iconv-lite');
reader.onload = function(){
csvData = iconv.decode(reader.result, 'latin1');
jsonData = vm.tsvJSON(csvData);
vm.addFiles(jsonData);
};
reader.onloadend = function(){
//Go to visualization page
router.push({ name: 'Visualization' });
};
reader.readAsText(input.files[0]);
});
}
},
loadingAnimation() {
//Make loading screen visible with animation
var target = document.getElementById('loadBox');
target.classList.remove('hidden')
setTimeout(function () {
target.classList.remove('visuallyhidden');
}, 20);
}

loadingAnimation isn't an async function and doesn't explicitly return a promise, so calling it doesn't give you a promise. In fact, it doesn't have any explicit return value, so calling it will always give you undefined.
If you want it to return a promise that will be fulfilled when the timer callback is fired, you have to code it. For instance:
loadingAnimation() {
return new Promise(resolve => {
//Make loading screen visible with animation
var target = document.getElementById('loadBox');
target.classList.remove('hidden')
setTimeout(function () {
target.classList.remove('visuallyhidden');
resolve();
}, 20);
});
}

Related

waiting for Base64 image to return value in javascript async loop

I'm having a nightmare getting an async loop to work.
I need to retreive an image as data to do something with BUT all I can return is 'undefined' and it doent wait for that result!!
Simply put I need to retreive the data image and pass it to the next function but cant get it to wait!!
heres the code
function getImages(aID){
async function getFileContentAsBase64(path, callback){
window.resolveLocalFileSystemURL(path, gotFile, fail);
function fail(e) {
alert('Cannot found requested file');
}
function gotFile(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var content = this.result;
console.log('content looks as it should:::', content);
callback(content);
};
reader.readAsDataURL(file);
});
}
}
console.log("aData uploadImages:", aData);
console.log("aData.length:", aData.length);
(async () => {
console.log("in sync length:::" + aData.length);
for (let i = 0; i < aData.length; i++) {
console.log("in loop:::")
// toastSuccess("fetching image.--.")
var fileURL = 'filepath/filename.jpg';
console.log(i + "-LOOKING FOR:::", fileURL);
const getImage = await getFileContentAsBase64(fileURL, function(base64Image {//<<<<HERE HERE HERE --THIS IS THE ISSUE
console.log("base64Image LOOKS AS IT SHOULD:::", base64Image);
});
console.log(i + "-IMAGEDATA COMES back as undefined", getImage);//<<<HERE HERE HERE - this is the issue. I need to get this value before the loop continues!!!
console.log(i + "-s::: GOT IMAGE");
// toastSuccess("sending image. .-")
var oParams = {id:1}//,body:getImage
var sendImage = await doSomethingElse(getImage, oParams);
}
})();
}
got it: it needed a promise, also removed the async from "function getFileContentAsBase64(. ." at the top of the page
const getImage = new Promise((resolve, reject) => {
getFileContentAsBase64(sfileURL, function(base64Image){ resolve(base64Image); reject('error on return');})
});
thanks .--. . .- -.-. .

Comparaison between different input files in javascript, cannot reach global variable

I'm trying to open different JSON files and compare each others values.
To do it, I used this to read the files. But I'm trying to save every data in a global variable 'data'. I think it's a asynchronous mistake but I'm pretty new to javascript and I didn't understand where the error come from.
Here is my code :
var data = {}
function readmultifiles(files) {
var reader = new FileReader();
function readFile(index) {
if( index >= files.length ) return;
var file = files[index];
reader.onload = function(e) {
// get file content
var bin = e.target.result;
bin = JSON.parse(bin);
for(task in bin['values']){
addData(bin['info']['date'],task,bin['values'][task]);
}
// do sth with bin
readFile(index+1);
}
reader.readAsBinaryString(file);
}
readFile(0);
console.log("readmultifiles");
console.log(data);
return data;
}
function addData(date, task, value){
if(data[task] == undefined){
data[task] = {};
}
data[task][date] = value;
}
var fileInput = document.querySelector('#file');
fileInput.addEventListener('change', function() {
console.log(fileInput);
var files = fileInput.files;
readmultifiles(files);
console.log("index");
console.log(data);
console.log(data['task_1']); // I can't display this object because 'undefinned'
});
What happens? When I'm trying to watch 'data', firefox console display me the object but I cannot watch inside the object.
Firefox display
What do I need to do to make a good solution. Should I use timers to wait?

Issue with syntax for javascript promise

I am using pdfjs to read a pdf file and get its pages as images. After all the images are loaded, I need to call an ajax to send and get details from the server. The code for iterating pages in the pdf has been taken from here: https://ourcodeworld.com/articles/read/405/how-to-convert-pdf-to-text-extract-text-from-pdf-with-javascript
I am having problem writing the syntax for the promise that will call the ajax function after all the required details mentioned above has been fetched.
Here is my code:
getDataUrlsAndSizesFromPdf(file).then(proceedAndCheckOnServer(file));
const getDataUrlsAndSizesFromPdf = function(file) {
PDFJS.disableWorker = true;
fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
return new Promise(function(resolve, reject) {
fileReader.onload = function(ev) {
PDFJS.getDocument(fileReader.result).then(function (pdf) {
var pdfDocument = pdf;
var pagesPromises = [];
for (var i = 0; i < pdf.pdfInfo.numPages; i++) {
var pageNum = i + 1;
pagesPromises.push(getImageUrl(pageNum, pdfDocument));
}
Promise.all(pagesPromises).then(function () {
console.log(pdfPagesInfo);
resolve();
}, function () {
console.log('failed');
reject();
});
}, function (reason) {
console.error(reason);
});
}
});
}
function getImageUrl() {
return new Promise(function (resolve, reject) {
PDFDocumentInstance.getPage(pageNum).then(function (pdfPage) {
var scale = 1;
var viewport = pdfPage.getViewport(scale);
var canvas = document.getElementById('dummy-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
var task = pdfPage.render({canvasContext: context, viewport: viewport})
task.promise.then(function(){
var sizesArr = {
height : viewport.height,
width : viewport.width
}
pdfPagesInfo.sizes[pageNum.toString()] = sizesArr
pdfPagesInfo.images[pageNum.toString()] = canvas.toDataURL('image/jpeg');
resolve();
});
});
});
}
function proceedAndCheckOnServer() {
....
}
What I want is "proceedAndCheckOnServer()" to be executed when all the details have been fetched from "getImageUrl()". But presently the execution directly goes to "proceedAndCheckOnServer()" without waiting for the promise from "getDataUrlsAndSizesFromPdf" to be resolved. I am new to javascript promises. Please help me with the syntax.
You are calling your function instead of using a callback function.
proceedAndCheckOnServer is invoked, and the result of that function is used as the parameter to then.
getDataUrlsAndSizesFromPdf(file).then(proceedAndCheckOnServer(file));
Try one of these:
getDataUrlsAndSizesFromPdf(file).then(()=>proceedAndCheckOnServer(file));
getDataUrlsAndSizesFromPdf(file).then(function(){ proceedAndCheckOnServer(file) });
Or resolve your getDataUrlsAndSizesFromPdf promise with file and use the function without () to pipeline the result.
getDataUrlsAndSizesFromPdf(file).then(proceedAndCheckOnServer);

Error: Firebase.set failed: First argument contains undefined in property 'achv_img'

I followed this post
Having form using angular and firebase achievementsapp.firebaseapp.com, you can register and then if you click on the green plus it should pop the form, upload button is not working and when click on Add button it gives me this error Error: Firebase.set failed: First argument contains undefined in property 'achv_img'
controller code:
myApp.controller('MeetingsController',
function($scope, $rootScope, $firebase, Uploader,
CountMeetings, FIREBASE_URL) {
var ref = new Firebase(FIREBASE_URL + '/users/' +
$rootScope.currentUser.$id + '/meetings');
var meetingsInfo = $firebase(ref);
var meetingsObj = meetingsInfo.$asObject();
meetingsObj.$loaded().then(function(data) {
$scope.meetings = data;
}); //make sure meetings data is loaded
$scope.addMeeting = function() {
meetingsInfo.$push({
name: $scope.meetingname,
description: $scope.meetingdescription,
achv_type: $scope.achvtype,
achv_date: $scope.achvdate,
achv_img : $scope.achvimg,
date: Firebase.ServerValue.TIMESTAMP
}).then(function() {
$scope.meetingname='';
$scope.achvtype = '';
$scope.meetingdescription='';
$scope.achvdate='';
$scope.achvimg= $scope.meetingsInfoImgData;
});
Uploader.create($scope.meetingsInfo);
$scope.handleFileSelectAdd = function(evt) {
var f = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var filePayload = e.target.result;
$scope.meetingsInfoImgData = e.target.result;
document.getElementById('pano').src = $scope.meetingsInfoImgData;
};
})(f);
reader.readAsDataURL(f);
};
document.getElementById('file-upload').addEventListener('change', $scope.handleFileSelectAdd, false);
}; //addmeeting
$scope.deleteMeeting = function(key) {
meetingsInfo.$remove(key);
}; //deleteMeeting
}); //MeetingsController
In reader.onload() you take the image data from the file and put it into $scope.meetingsInfoImgData:
reader.onload = (function(theFile) {
return function(e) {
var filePayload = e.target.result;
$scope.meetingsInfoImgData = e.target.result;
document.getElementById('pano').src = $scope.meetingsInfoImgData;
};
})(f);
My original answer then uses its equivalent of this $scope.meetingsInfoImgData later to populate the JavaScript object that it sends to Firebase:
$scope.episode.img1 = $scope.episodeImgData;
var episodes = $firebase(ref).$asArray();
episodes.$add($scope.episode);
Your code does nothing with $scope.meetingsInfoImgData when it tries to create the object in Firebase. You'll probably need something akin to $scope.episode.img1 = $scope.episodeImgData; in your code too.
If for some reason you can not fix it at the source, to make sure your object does not contain any undefined props use this simple trick:
JSON.parse( JSON.stringify(ObjectToSave ) )
For more info take a look at this codePen: http://codepen.io/ajmueller/pen/gLaBLX

How do I write FileReader test in Jasmine?

I'm trying to make this test work, but I couldn't get my head around how to write a test with FileReader. This is my code
function Uploader(file) {
this.file = file;
}
Uploader.prototype = (function() {
function upload_file(file, file_contents) {
var file_data = new FormData()
file_data.append('filename', file.name)
file_data.append('mimetype', file.type)
file_data.append('data', file_contents)
file_data.append('size', file.size)
$.ajax({
url: "/upload/file",
type: "POST",
data: file_contents,
contentType: file.type,
success: function(){
// $("#thumbnail").attr("src", "/upload/thumbnail");
},
error: function(){
alert("Failed");
},
xhr: function() {
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){
myXhr.upload.addEventListener('progress',showProgress, false);
} else {
console.log("Upload progress is not supported.");
}
return myXhr;
}
});
}
return {
upload : function() {
var self = this,
reader = new FileReader(),
file_content = {};
reader.onload = function(e) {
file_content = e.target.result.split(',')[1];
upload_file(self.file, file_content);
}
}
};
})();
And this is my test
describe("Uploader", function() {
it("should upload a file successfully", function() {
spyOn($, "ajax");
var fakeFile = {};
var uploader = new Uploader(fakeFile);
uploader.upload();
expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/upload/file");
})
});
But it never gets to reader.onload.
The problem here is the use of reader.onload which is hard to test. You could use reader.addEventListener instead so you can spy on the global FileReader object and return a mock:
eventListener = jasmine.createSpy();
spyOn(window, "FileReader").andReturn({
addEventListener: eventListener
})
then you can fire the onload callback by yourself:
expect(eventListener.mostRecentCall.args[0]).toEqual('load');
eventListener.mostRecentCall.args[1]({
target:{
result:'the result you wanna test'
}
})
This syntax changed in 2.0. Code below gives an example based on Andreas Köberle's answer but using the new syntax
// create a mock object, its a function with some inspection methods attached
var eventListener = jasmine.createSpy();
// this is going to be returned when FileReader is instantiated
var dummyFileReader = { addEventListener: eventListener };
// pipe the dummy FileReader to the application when FileReader is called on window
// this works because window.FileReader() is equivalent to new FileReader()
spyOn(window, "FileReader").and.returnValue(dummyFileReader)
// your application will do something like this ..
var reader = new FileReader();
// .. and attach the onload event handler
reader.addEventListener('load', function(e) {
// obviously this wouldnt be in your app - but it demonstrates that this is the
// function called by the last line - onloadHandler(event);
expect(e.target.result).toEqual('url');
// jasmine async callback
done();
});
// if addEventListener was called on the spy then mostRecent() will be an object.
// if not it will be null so careful with that. the args array contains the
// arguments that addEventListener was called with. in our case arg[0] is the event name ..
expect(eventListener.calls.mostRecent().args[0]).toEqual('load');
// .. and arg[1] is the event handler function
var onloadHandler = eventListener.calls.mostRecent().args[1];
// which means we can make a dummy event object ..
var event = { target : { result : 'url' } };
// .. and call the applications event handler with our test data as if the user had
// chosen a file via the picker
onloadHandler(event);
I also faced similar problem and was able to achieve it without use of addeventlistener. I had used onloadend, so below is what I did.
My ts file had below code:-
let reader = new FileReader();
reader.onloadend = function() {
let dataUrl = reader.result;
// Some working here
};
reader.readAsDataURL(blob);
My spec file (test) case code :-
let mockFileReader = {
result:'',
readAsDataURL:(blobInput)=> {
console.log('readAsDataURL');
},
onloadend:()=> {
console.log('onloadend');
}
};
spyOn<any>(window, 'FileReader').and.returnValue(mockFileReader);
spyOn<any>(mockFileReader, 'readAsDataURL').and.callFake((blobInput)=> {
// debug your running application and assign to "encodedString" whatever
//value comes actually after using readAsDataURL for e.g.
//"data:*/*;base64,XoteIKsldk......"
mockFileReader.result = encodedString;
mockFileReader.onloadend();
});
This way you have mocked the FileReader object and returned a fake call to your own "readAsDataURL". And thus now when your actual code calls "reasAsDataURL" your fake function is called in which you are assigning an encoded string in "result" and calling "onloadend" function which you had already assigned a functionality in your code (.ts) file. And hence it gets called with expected result.
Hope it helps.
I think the best way is to use the real FileReader (don't mock it), and pass in a real File or Blob. This improves your test coverage and makes your tests less brittle.
If your tests don't run in IE, you can use the File constructor, e.g.
const fakeFile = new File(["some contents"], "file.txt", {type: "text/plain"});
If you need to be compatible with IE, you can construct a Blob and make it look like a file:
const fakeFile = new Blob(["some contents"]);
fakeFile.name = "file.txt";
fakeFile.type = "text/plain";
The FileReader can read either of these objects so there is no need to mock it.
i found easiest for myself to do next.
mock blob file
run reader.onload while in test environment.
as result - i do not mock Filereader
// CONTROLLER
$scope.handleFile = function (e) {
var f = e[0];
$scope.myFile = {
name: "",
size: "",
base64: ""
};
var reader = new FileReader();
reader.onload = function (e) {
try {
var buffer = e.target.result;
$scope.myFile = {
name: f.name,
size: f.size,
base64: XLSX.arrayBufferToBase64(buffer)
};
$scope.$apply();
} catch (error) {
$scope.error = "ERROR!";
$scope.$apply();
}
};
reader.readAsArrayBuffer(f);
//run in test env
if ( typeof jasmine == 'object') {reader.onload(e)}
}
//JASMINE TEST
it('handleFile 0', function () {
var fileContentsEncodedInHex = ["\x45\x6e\x63\x6f\x64\x65\x49\x6e\x48\x65\x78\x42\x65\x63\x61\x75\x73\x65\x42\x69\x6e\x61\x72\x79\x46\x69\x6c\x65\x73\x43\x6f\x6e\x74\x61\x69\x6e\x55\x6e\x70\x72\x69\x6e\x74\x61\x62\x6c\x65\x43\x68\x61\x72\x61\x63\x74\x65\x72\x73"];
var blob = new Blob(fileContentsEncodedInHex);
blob.type = 'application/zip';
blob.name = 'name';
blob.size = 11111;
var e = {0: blob, target: {result: {}}};
$scope.handleFile(e);
expect($scope.error ).toEqual("");
});
I struggled to figure out how to test onloadend when it gets called from readAsDataURL.
Here is a dump of what I ended up with.
Production code:
loadFileDataIntoChargeback(tempFileList) {
var fileNamesAndData = [];
for (var i = 0, f; f = tempFileList[i]; i++) {
let theFile = tempFileList[i];
var reader = new FileReader();
reader.onloadend = ((theFile) => {
return (fileData) => {
var insertionIndex = this.chargeback.fileList.length;
this.chargeback.fileList.push({ FileName: theFile.name, Data: fileData.target.result, FileType: theFile.type });
this.loadFilePreviews(theFile, insertionIndex);
}
})(f);
reader.readAsDataURL(f);
}
this.fileInputPath = "";
}
Test code:
describe('when the files are loaded into the chargeback', () => {
it('loads file previews', () => {
let mockFileReader = {
target: { result: '' },
readAsDataURL: (blobInput) => {},
onloadend: () => {}
};
spyOn(chargeback, "loadFilePreviews");
spyOn(window, 'FileReader').and.returnValue(mockFileReader);
spyOn(mockFileReader, 'readAsDataURL').and.callFake((blobInput) => {
mockFileReader.onloadend({ target: { result: "data:image/jpeg;base64,/9j/4QAYRXh" } });
});
var readFileList = chargeback.getArrayFromFileInput([getImageFile1()]);
chargeback.loadFileDataIntoChargeback(readFileList);
expect(chargeback.loadFilePreviews).toHaveBeenCalled();
});
});

Categories