angular-file-upload async filter in angularjs

Issue

I’m using angular-file-upload to upload files, where I’m writing a filter in which I need to take headers from the csv file selected for upload & match them with headers from some API. I’m able to get headers from both csv & API and comparing them, but uploader callback functions (like uploader.onWhenAddingFileFailed(), uploader.onAfterAddingFile()) do not wait for my filter to execute and they executes asynchronously.

So, uploader callback functions should wait till my filter function’s execution and then, depending on response (true/false) further actions will be happened.

My current code for filter is –

uploader.filters.push({
    name: 'fileTemplateFilter',
    fn: function(item /*{File|FileLikeObject}*/ , options, deferred) {

        var getCsvHeaders = function() {
            // $http.post(csvHeadersUrl + vm.vendorWithSearchItem.selected.vendorid + '&fileName=' + item.name)
            return $http.get('app/pages/utilities/inventoryMapper/csvHeader.csv')
                .then(function(success) {
                    vm.tableHeaders = success.data;
                    return true;
                }, function(error) {
                    vm.tableHeaders = undefined;
                    return false;
                }).catch(function(e){
                    return false;
                });
        }

        var getFileHeaders = function() {
            var r = new FileReader();
            r.onload = function(e) {
                var contents = e.target.result;
                contents = contents.split(/\r\n|\n/);
                var i = 0,
                    flag = false,
                    headers;
                while (i < contents.length && flag == false) {
                    if (contents[i] != '') {
                        headers = contents[i]
                        flag = true;
                    }
                    i++;
                }
                vm.fileHeaders = headers.split(",").map(function(item) { return item.trim() && item.replace(/['"]+/g, ''); });
                return true;
            };
            r.readAsText(item);
            return false;
        }

        var isTemplateSame = function() {
            return Promise.all([getCsvHeaders(), getFileHeaders()]).then(function() {
                if(vm.fileHeaders && vm.tableHeaders) {
                    var is_same = (vm.fileHeaders.length == vm.tableHeaders.length) && vm.fileHeaders.every(function(element, index) {
                        return element === vm.tableHeaders[index];
                    });
                    return is_same == true ? true : false;
                }
            })
        }

        return Promise.resolve(isTemplateSame()).then(function(value) {
            return value;
        });
        // setTimeout(deferred.resolve, 1e3);
    }
});

Here, I need following –

1) my filter reads whole csv & then returns headers; Please suggest code improvements so that only headers will be read instead of whole file.

2) The correct implementation of promise in this filter

Solution

I got the solution for the promise implementation. I used deferred promise & it worked for me.
And at this time, also found quick parser – PapaParse for csv parsing.

So, my updated filter is as follows –

uploader.filters.push({
    name: 'fileTemplateFilter',
    fn: function(item /*{File|FileLikeObject}*/ , options, deferred) {
        vm.tableHeaders = undefined;
        vm.fileHeaders = undefined;
        var getCsvHeaders = function() {
            // return $http.get('app/pages/utilities/inventoryMapper/csvHeader.csv')
            return $http.post(csvHeadersUrl + vm.vendorWithSearchItem.selected.vendorid + '&fileName=' + item.name)
                .then(function(success) {
                    vm.tableHeaders = success.data;
                    return vm.tableHeaders;
                }, function(error) {
                    vm.tableHeaders = undefined;
                    throw error.status + " : " + error.data;
                }).catch(function(e){
                    return false;
                });
        }

        var getFileHeaders = function() {
            var defer = $q.defer();
            Papa.parse(item, {
                worker: true,
                skipEmptyLines: true,
                step: function(results, parser) {
                    parser.abort();
                    vm.fileHeaders = results.data[0] || undefined;
                    results = null;
                    defer.resolve(vm.fileHeaders);
                },
                complete: function(results){
                    results = null;
                }
            });
            return defer.promise;
        }

        $q.all([getCsvHeaders(), getFileHeaders()]).then(function() {
            if(vm.fileHeaders && vm.tableHeaders) {
                var is_same = (vm.fileHeaders.length == vm.tableHeaders.length) && vm.fileHeaders.every(function(element, index) {
                    return element === vm.tableHeaders[index];
                });
                if(is_same == true) {
                    deferred.resolve(is_same);
                } else {
                    toastr.error('file template doesn\'t match for this file - ' + item.name + ' ...!', 'Error');
                    deferred.reject(false);
                }
            } else {
                deferred.reject(false);
            }
        }, function(error) {
            deferred.reject(error);
        })

        return deferred.promise;
    }
});

Answered By – Tushar Walzade

Answer Checked By – Candace Johnson (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.