unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

AngularJSでCSVファイルを出力する

管理画面などでよく見るCSVエクスポート機能ですが、たいていはサーバーサイドでCSVファイルを生成してブラウザにダウンロードさせる、という方法で実装されていると思います。 今回はクライアントサイドで編集したデータをダウンロードする必要があったため、クライアントで完結してCSVファイルを出力する方法を調べてみました。

ポイント

  • Blob API(HTML5)でCSVデータをBlobオブジェクトに変換する。
    • Internet Expolorerでは、Blobをwindow.navigator.msSaveBlob(IE独自API)で出力する。
    • その他のブラウザでは、a要素のdownload属性にファイル名、href属性にBlobで生成した値を指定する。

実装例(AngularJS Service)

(function() {
    'use strict';

    angular
        .module('app')
        .service('CsvExporter', CsvExporter);

    /** @ngInject */
    function CsvExporter($window) {
        this.export = function(object, keys) {

            // Serialize a header.
            var content = keys.join(',') + "\n";
            // Serialize rows.
            angular.forEach(object, function(properties){
                var row = '';
                angular.forEach(keys, function(key, index){
                    if (index != 0) { row += ','; }
                    if (properties[key]) { row += properties[key]; }
                });
                content += (row + "\n");
            });

            // Convert text to blob object with Blob API.
            var blob = new Blob([ content ], { "type" : "text/csv" });

            // For Internet Expolorer
            if ($window.navigator.msSaveBlob) { 
                $window.navigator.msSaveBlob(blob, "export.csv"); 

            // For other browsers
            } else {
                var link = $window.document.getElementById("csv_exporter");

                if (link == null) { 
                    link = $window.document.createElement("a");
                    link.setAttribute("id", "csv_exporter");
                    link.setAttribute("style", "display:none;");
                    link.setAttribute("download", "export.csv");
                }

                link.setAttribute("href", $window.URL.createObjectURL(blob));
                link.click();
            }
        };
    }
})() 

DOM操作(a要素の追加)

  • AngularJSではDirectiveの利用が推奨されますが、今回はファイル出力のために一時的にa要素を追加するため直接DOMを操作しています。 (ただし、$window Serviceを通してdocumentを参照するようにしています。)
  • angular.element(jQuery/jqLite)を利用した場合は下記のように記述できます。
var link = angular.element("#csv_exporter");

if (link.length == 0) { 
    link = angular.element("<a>");
    link.attr("id", "csv_exporter");
    link.attr("style", "display:none;");
    link.attr("download", "export.csv");
}

link.attr("href", $window.URL.createObjectURL(blob));
link[0].click();

参考