Skip to content

Instantly share code, notes, and snippets.

@jplwood
Last active May 17, 2017 16:41
Show Gist options
  • Save jplwood/b00e8d01302169468f1d114d578c6600 to your computer and use it in GitHub Desktop.
Save jplwood/b00e8d01302169468f1d114d578c6600 to your computer and use it in GitHub Desktop.
ng1 excel file upload and client side XLSX parsing
angular.module('acrSelect.portal.directives')
.directive('organizationImport', function () {
return {
restrict: 'E',
templateUrl: '/app/templates/organization/organizationImport.html?v=' + window.ApplicationBuildVersion,
scope: {
'organizations': '=',
'timeZones': '=',
'utils': '='
},
link: function ($scope, $element, attrs) {
},
controller: ['$scope', 'organizationChannel', 'organizationService', 'Upload', function ($scope, organizationChannel, organizationService, Upload) {
$scope.cancelImport = function () {
$scope.importFile = null;
organizationChannel.organizationImportStateChanged({ importing: false });
}
/*
* Function to import excel file and import an organization
* @param {Object} file - xlsx file selected by user
*/
function importOrganization(file, organizations) {
var reader = new FileReader();
reader.onload = function (e) {
try {
var data = e.target.result;
var arr = fixData(data);
var wb = XLSX.read(btoa(arr), { type: 'base64' });
if (wb.SheetNames && wb.SheetNames.length === 1 && wb.SheetNames[0] === "Organization") {
var importedOrg = extractJson(wb);
var errors = validateImportedOrganization(organizations, importedOrg);
if (errors) {
throw errors;
} else {
saveImportedOrganization(importedOrg);
}
} else {
throw "Incorrect file template. You may only use a file exported from this page in different environment."
}
} catch (ex) {
notifyErrorModal("Import was unsuccessful<br><small>" + ex + "</small>");
}
};
reader.readAsArrayBuffer(file);
}
$scope.importOrganization = importOrganization; // Add import fn to the scope
function saveImportedOrganization(organization) {
setLoadingState(true);
return organizationService.createOrganization(organization).then(function (data) {
//Not using service message to specify that this was an import, not just a create.
setLoadingState(false);
$scope.importFile = null;
notifySuccessModal(data.message + "<br><small>Successfully imported organization <br><span class='label label-primary'>ID: " + data.organizationId + "</span> " + organization.organizationName);
organizationChannel.organizationAdded({ organizationId: data.organizationId });
}, function (reason) {
notifyErrorModal(reason);
});
}
function validateImportedOrganization(orgs, org) {
var errors = "";
// Check that this org id isn't blank and doesn't already exist, throw error if it does
if (!org.organizationId) {
errors += "The organization ID cannot be blank.<br>";
} else if ($scope.utils.checkIdAvailable(orgs, org.organizationId)) {
errors += "The organization ID <code>" + org.organizationId + "</code> already exists in this environment.<br>"
}
// Check that this org name isn't blank and doesn't already exist, throw error if it does
if (!org.organizationName) {
errors += "The organization Name cannot be blank.<br>";
} else if ($scope.utils.checkNameAvailable(orgs, org.organizationName)) {
errors += "The organization name <code>" + org.organizationName + "</code>, already exists in this environment.<br>"
}
return errors;
}
/*
* Helper function to turn the excel workbook into a JS object
* @param {Object} workbook - XLSX object representing excel workbook
*/
function extractJson(workbook) {
var organizations = XLSX.utils.sheet_to_row_object_array(workbook.Sheets["Organization"]);
if (organizations.length > 0) {
var importOrg = organizations[0];
importOrg.integrationTypes = parseObjProp(importOrg, "integrationTypes");
importOrg.paidFeatures = parseObjProp(importOrg, "paidFeatures");
importOrg.timeZone = $scope.utils.getTimeZoneObj($scope.timeZones, importOrg.timeZoneId);
return importOrg;
} else {
return null;
}
}
/*
* Helper function to parse a JSON string from an object property
* @param {Object} obj - object to operate on
* @param {string} prop - property name on object that has value that should be parsed
*/
function parseObjProp(obj, prop) {
if (obj.hasOwnProperty(prop) && (typeof obj[prop] === "string" || obj[prop] instanceof String)) {
try {
return JSON.parse(obj[prop]);
} catch (ex) {
throw "The <i>" + prop + "</i> field is not in the correct format. It must be serialized JSON.";
}
}
}
/*
* Magical function taken from SheetJS example, cleans up data before XLSX reads it.
* @param {Array} data - data to be cleaned
*/
function fixData(data) {
var o = "", l = 0, w = 10240;
for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)));
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
return o;
}
/**
* Set the loading state of this org detail directive
* @param {boolean} isLoading - whether the details are loading or not
*/
function setLoadingState(isLoading) {
$scope.loading = isLoading;
}
}]
};
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment