ng-csv
Version:
Simple directive that turns arrays and objects into downloadable CSV files
167 lines (135 loc) • 4.74 kB
JavaScript
/**
* Created by asafdav on 15/05/14.
*/
angular.module('ngCsv.services').
service('CSV', ['$q', function ($q) {
var EOL = '\r\n';
var BOM = "\ufeff";
var specialChars = {
'\\t': '\t',
'\\b': '\b',
'\\v': '\v',
'\\f': '\f',
'\\r': '\r'
};
/**
* Stringify one field
* @param data
* @param options
* @returns {*}
*/
this.stringifyField = function (data, options) {
if (options.decimalSep === 'locale' && this.isFloat(data)) {
return data.toLocaleString();
}
if (options.decimalSep !== '.' && this.isFloat(data)) {
return data.toString().replace('.', options.decimalSep);
}
if (typeof data === 'string') {
data = data.replace(/"/g, '""'); // Escape double qoutes
if (options.quoteStrings || data.indexOf(',') > -1 || data.indexOf('\n') > -1 || data.indexOf('\r') > -1) {
data = options.txtDelim + data + options.txtDelim;
}
return data;
}
if (typeof data === 'boolean') {
return data ? 'TRUE' : 'FALSE';
}
return data;
};
/**
* Helper function to check if input is float
* @param input
* @returns {boolean}
*/
this.isFloat = function (input) {
return +input === input && (!isFinite(input) || Boolean(input % 1));
};
/**
* Creates a csv from a data array
* @param data
* @param options
* * header - Provide the first row (optional)
* * fieldSep - Field separator, default: ',',
* * addByteOrderMarker - Add Byte order mark, default(false)
* @param callback
*/
this.stringify = function (data, options) {
var def = $q.defer();
var that = this;
var csv = "";
var csvContent = "";
var dataPromise = $q.when(data).then(function (responseData) {
//responseData = angular.copy(responseData);//moved to row creation
// Check if there's a provided header array
if (angular.isDefined(options.header) && options.header) {
var encodingArray, headerString;
encodingArray = [];
angular.forEach(options.header, function (title, key) {
this.push(that.stringifyField(title, options));
}, encodingArray);
headerString = encodingArray.join(options.fieldSep ? options.fieldSep : ",");
csvContent += headerString + EOL;
}
var arrData = [];
if (angular.isArray(responseData)) {
arrData = responseData;
}
else if (angular.isFunction(responseData)) {
arrData = responseData();
}
// Check if using keys as labels
if (angular.isDefined(options.label) && options.label && typeof options.label === 'boolean') {
var labelArray, labelString;
labelArray = [];
angular.forEach(arrData[0], function(value, label) {
this.push(that.stringifyField(label, options));
}, labelArray);
labelString = labelArray.join(options.fieldSep ? options.fieldSep : ",");
csvContent += labelString + EOL;
}
angular.forEach(arrData, function (oldRow, index) {
var row = angular.copy(arrData[index]);
var dataString, infoArray;
infoArray = [];
var iterator = !!options.columnOrder ? options.columnOrder : row;
angular.forEach(iterator, function (field, key) {
var val = !!options.columnOrder ? row[field] : field;
this.push(that.stringifyField(val, options));
}, infoArray);
dataString = infoArray.join(options.fieldSep ? options.fieldSep : ",");
csvContent += index < arrData.length ? dataString + EOL : dataString;
});
// Add BOM if needed
if (options.addByteOrderMarker) {
csv += BOM;
}
// Append the content and resolve.
csv += csvContent;
def.resolve(csv);
});
if (typeof dataPromise['catch'] === 'function') {
dataPromise['catch'](function (err) {
def.reject(err);
});
}
return def.promise;
};
/**
* Helper function to check if input is really a special character
* @param input
* @returns {boolean}
*/
this.isSpecialChar = function(input){
return specialChars[input] !== undefined;
};
/**
* Helper function to get what the special character was supposed to be
* since Angular escapes the first backslash
* @param input
* @returns {special character string}
*/
this.getSpecialChar = function (input) {
return specialChars[input];
};
}]);