From 8b3707a125277b27fede8f5b333acb2ef1839b18 Mon Sep 17 00:00:00 2001 From: dan costelloe Date: Thu, 15 Oct 2015 19:59:34 +0100 Subject: [PATCH 1/3] feat(skip-empty) added new option to permit the skipping downloads of empty data / blank CSVs --- README.md | 1 + src/ng-csv/directives/ng-csv.js | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 52e7239..efc56c2 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ ngCsv attributes * decimal-separator: Defines the decimal separator character (default is .). If set to "locale", it uses the [language sensitive representation of the number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString). * text-delimiter: If provided, will use this characters to "escape" fields, otherwise will use double quotes as deafult * quote-strings: If provided, will force escaping of every string field. +* skip-empty: If provided, will not trigger the download of empty data (default is false). * lazy-load: If defined and set to true, ngCsv will generate the data string only on demand. See the lazy_load example for more details. * add-bom: Add the Byte Order Mark, use this option if you are getting an unexpected char when opening the file on any windows App. * charset: Defines the charset of the downloadable Csv file. Default is "utf-8". diff --git a/src/ng-csv/directives/ng-csv.js b/src/ng-csv/directives/ng-csv.js index bfb80f9..90d5ee4 100644 --- a/src/ng-csv/directives/ng-csv.js +++ b/src/ng-csv/directives/ng-csv.js @@ -21,7 +21,8 @@ angular.module('ngCsv.directives'). addByteOrderMarker: "@addBom", ngClick: '&', charset: '@charset', - label: '&csvLabel' + label: '&csvLabel', + skipEmpty: '@skipEmpty' }, controller: [ '$scope', @@ -84,6 +85,10 @@ angular.module('ngCsv.directives'). ], link: function (scope, element, attrs) { function doClick() { + + // drop out for empty data + if (!scope.csv.trim() && scope.skipEmpty === "true") {return;} + var charset = scope.charset || "utf-8"; var blob = new Blob([scope.csv], { type: "text/csv;charset="+ charset + ";" From bf8036dcb8f2e27e94ed2beba6c125f351e5709e Mon Sep 17 00:00:00 2001 From: dan costelloe Date: Thu, 15 Oct 2015 19:59:34 +0100 Subject: [PATCH 2/3] feat(skip-empty) added new option to permit the skipping downloads of empty data / blank CSVs --- README.md | 1 + build/ng-csv.js | 7 ++++++- build/ng-csv.min.js | 4 ++-- src/ng-csv/directives/ng-csv.js | 7 ++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52e7239..efc56c2 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ ngCsv attributes * decimal-separator: Defines the decimal separator character (default is .). If set to "locale", it uses the [language sensitive representation of the number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString). * text-delimiter: If provided, will use this characters to "escape" fields, otherwise will use double quotes as deafult * quote-strings: If provided, will force escaping of every string field. +* skip-empty: If provided, will not trigger the download of empty data (default is false). * lazy-load: If defined and set to true, ngCsv will generate the data string only on demand. See the lazy_load example for more details. * add-bom: Add the Byte Order Mark, use this option if you are getting an unexpected char when opening the file on any windows App. * charset: Defines the charset of the downloadable Csv file. Default is "utf-8". diff --git a/build/ng-csv.js b/build/ng-csv.js index 80432ec..0393417 100644 --- a/build/ng-csv.js +++ b/build/ng-csv.js @@ -221,7 +221,8 @@ angular.module('ngCsv.directives'). addByteOrderMarker: "@addBom", ngClick: '&', charset: '@charset', - label: '&csvLabel' + label: '&csvLabel', + skipEmpty: '@skipEmpty' }, controller: [ '$scope', @@ -284,6 +285,10 @@ angular.module('ngCsv.directives'). ], link: function (scope, element, attrs) { function doClick() { + + // drop out for empty data + if (!scope.csv.trim() && scope.skipEmpty === "true") {return;} + var charset = scope.charset || "utf-8"; var blob = new Blob([scope.csv], { type: "text/csv;charset="+ charset + ";" diff --git a/build/ng-csv.min.js b/build/ng-csv.min.js index 94dcd7f..1ea0e75 100644 --- a/build/ng-csv.min.js +++ b/build/ng-csv.min.js @@ -1,2 +1,2 @@ -/*! ng-csv 10-10-2015 */ -!function(a){angular.module("ngCsv.config",[]).value("ngCsv.config",{debug:!0}).config(["$compileProvider",function(a){angular.isDefined(a.urlSanitizationWhitelist)?a.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/):a.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/)}]),angular.module("ngCsv.directives",["ngCsv.services"]),angular.module("ngCsv.services",[]),angular.module("ngCsv",["ngCsv.config","ngCsv.services","ngCsv.directives","ngSanitize"]),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ngCsv"),angular.module("ngCsv.services").service("CSV",["$q",function(a){var b="\r\n",c="",d={"\\t":" ","\\b":"\b","\\v":" ","\\f":"\f","\\r":"\r"};this.stringifyField=function(a,b){return"locale"===b.decimalSep&&this.isFloat(a)?a.toLocaleString():"."!==b.decimalSep&&this.isFloat(a)?a.toString().replace(".",b.decimalSep):"string"==typeof a?(a=a.replace(/"/g,'""'),(b.quoteStrings||a.indexOf(",")>-1||a.indexOf("\n")>-1||a.indexOf("\r")>-1)&&(a=b.txtDelim+a+b.txtDelim),a):"boolean"==typeof a?a?"TRUE":"FALSE":a},this.isFloat=function(a){return+a===a&&(!isFinite(a)||Boolean(a%1))},this.stringify=function(d,e){var f=a.defer(),g=this,h="",i="",j=a.when(d).then(function(a){if(angular.isDefined(e.header)&&e.header){var d,j;d=[],angular.forEach(e.header,function(a){this.push(g.stringifyField(a,e))},d),j=d.join(e.fieldSep?e.fieldSep:","),i+=j+b}var k=[];if(angular.isArray(a)?k=a:angular.isFunction(a)&&(k=a()),angular.isDefined(e.label)&&e.label&&"boolean"==typeof e.label){var l,m;l=[],angular.forEach(k[0],function(a,b){this.push(g.stringifyField(b,e))},l),m=l.join(e.fieldSep?e.fieldSep:","),i+=m+b}angular.forEach(k,function(a,c){var d,f,h=angular.copy(k[c]);f=[];var j=e.columnOrder?e.columnOrder:h;angular.forEach(j,function(a){var b=e.columnOrder?h[a]:a;this.push(g.stringifyField(b,e))},f),d=f.join(e.fieldSep?e.fieldSep:","),i+=c'),h=angular.element(g.children()[0]);h.attr("href",a.URL.createObjectURL(d)),h.attr("download",b.getFilename()),h.attr("target","_blank"),e.find("body").append(g),f(function(){h[0].click(),h.remove()},null)}}c.bind("click",function(){b.buildCSV().then(function(){d()}),b.$apply()})}}}])}(window,document); \ No newline at end of file +/*! ng-csv 15-10-2015 */ +!function(a,b){angular.module("ngCsv.config",[]).value("ngCsv.config",{debug:!0}).config(["$compileProvider",function(a){angular.isDefined(a.urlSanitizationWhitelist)?a.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/):a.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/)}]),angular.module("ngCsv.directives",["ngCsv.services"]),angular.module("ngCsv.services",[]),angular.module("ngCsv",["ngCsv.config","ngCsv.services","ngCsv.directives","ngSanitize"]),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ngCsv"),angular.module("ngCsv.services").service("CSV",["$q",function(a){var b="\r\n",c="\ufeff",d={"\\t":" ","\\b":"\b","\\v":" ","\\f":"\f","\\r":"\r"};this.stringifyField=function(a,b){return"locale"===b.decimalSep&&this.isFloat(a)?a.toLocaleString():"."!==b.decimalSep&&this.isFloat(a)?a.toString().replace(".",b.decimalSep):"string"==typeof a?(a=a.replace(/"/g,'""'),(b.quoteStrings||a.indexOf(",")>-1||a.indexOf("\n")>-1||a.indexOf("\r")>-1)&&(a=b.txtDelim+a+b.txtDelim),a):"boolean"==typeof a?a?"TRUE":"FALSE":a},this.isFloat=function(a){return+a===a&&(!isFinite(a)||Boolean(a%1))},this.stringify=function(d,e){var f=a.defer(),g=this,h="",i="",j=a.when(d).then(function(a){if(angular.isDefined(e.header)&&e.header){var d,j;d=[],angular.forEach(e.header,function(a,b){this.push(g.stringifyField(a,e))},d),j=d.join(e.fieldSep?e.fieldSep:","),i+=j+b}var k=[];if(angular.isArray(a)?k=a:angular.isFunction(a)&&(k=a()),angular.isDefined(e.label)&&e.label&&"boolean"==typeof e.label){var l,m;l=[],angular.forEach(k[0],function(a,b){this.push(g.stringifyField(b,e))},l),m=l.join(e.fieldSep?e.fieldSep:","),i+=m+b}angular.forEach(k,function(a,c){var d,f,h=angular.copy(k[c]);f=[];var j=e.columnOrder?e.columnOrder:h;angular.forEach(j,function(a,b){var c=e.columnOrder?h[a]:a;this.push(g.stringifyField(c,e))},f),d=f.join(e.fieldSep?e.fieldSep:","),i+=c'),h=angular.element(g.children()[0]);h.attr("href",a.URL.createObjectURL(d)),h.attr("download",b.getFilename()),h.attr("target","_blank"),e.find("body").append(g),f(function(){h[0].click(),h.remove()},null)}}}c.bind("click",function(a){b.buildCSV().then(function(a){g()}),b.$apply()})}}}])}(window,document); \ No newline at end of file diff --git a/src/ng-csv/directives/ng-csv.js b/src/ng-csv/directives/ng-csv.js index bfb80f9..90d5ee4 100644 --- a/src/ng-csv/directives/ng-csv.js +++ b/src/ng-csv/directives/ng-csv.js @@ -21,7 +21,8 @@ angular.module('ngCsv.directives'). addByteOrderMarker: "@addBom", ngClick: '&', charset: '@charset', - label: '&csvLabel' + label: '&csvLabel', + skipEmpty: '@skipEmpty' }, controller: [ '$scope', @@ -84,6 +85,10 @@ angular.module('ngCsv.directives'). ], link: function (scope, element, attrs) { function doClick() { + + // drop out for empty data + if (!scope.csv.trim() && scope.skipEmpty === "true") {return;} + var charset = scope.charset || "utf-8"; var blob = new Blob([scope.csv], { type: "text/csv;charset="+ charset + ";" From 90bb64df8da6260fc9d509a61f5083c8b2f1eadd Mon Sep 17 00:00:00 2001 From: dan costelloe Date: Thu, 15 Oct 2015 20:48:46 +0100 Subject: [PATCH 3/3] feat(skip-empty) added unit test --- test/unit/ngCsv/directives/ngCsv.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/test/unit/ngCsv/directives/ngCsv.js b/test/unit/ngCsv/directives/ngCsv.js index 5862708..f1c4054 100644 --- a/test/unit/ngCsv/directives/ngCsv.js +++ b/test/unit/ngCsv/directives/ngCsv.js @@ -134,14 +134,14 @@ describe('ngCsv directive', function () { $rootScope.testDelim = [ {a:1, b:2, c:3}, {a:4, b:5, c:6} ]; var element = $compile( '
')($rootScope); - + $rootScope.$digest(); var scope = element.isolateScope(); // Check that the compiled element contains the templated content expect(scope.$eval(scope.data)).toEqual($rootScope.testDelim); - + scope.buildCSV(scope.data).then(function() { expect(scope.csv).toBe('a,b,c\r\n1,2,3\r\n4,5,6\r\n'); @@ -192,6 +192,29 @@ describe('ngCsv directive', function () { scope.$apply(); }); + it('Accepts optional skip-empty attribute', function () { + + $rootScope.noData = []; + var element = $compile( + '
' + + '
')($rootScope); + + $rootScope.$digest(); + + var scope = element.isolateScope(); + + spyOn(scope, 'buildCSV'); + + // Check that the compiled element contains the templated content + expect(scope.$eval(scope.data)).toEqual($rootScope.noData); + expect(scope.skipEmpty).toBe('true'); + + scope.$apply(); + + expect(scope.buildCSV).not.toHaveBeenCalled(); + }); + it('Accepts optional text-delimiter attribute (input object)', function (done) { $rootScope.testDelim = [{a: 1, b: 'a', c: 2}, {a: 'b', b: 'c', c: 3}]; var element = $compile(