こんにちは、フロントエンド寄りのエンジニアの @1derful です。
2018年モバイルファクトリーアドベントカレンダー 15日目の記事です。
前日は mattak さんの「フリーランスになって変化したこと」でした。
はじめに
フロントエンド分野の技術は移り変わりが激しいですね。 とはいえ、流行の速いトレンドもあれど、W3Cが策定したWeb標準技術はメンテナンスが止まらない限り使い続けられます。 そこで、Vue.js や TypeScript などの新しい技術に FileReader と IndexedDB の古くからあるWeb APIを織り交ぜて使ってみたいと思います。
まず開発環境を作ります
今回は Vue CLI を使います。 Vue.js の迅速な開発環境構築のため、TypeScript から JavaScript に変換するトランスパイラや、各モジュールを束ねるバンドラのインストールを一括で行い、雛形をスキャフォールディングしてくれるツール群です。 また、TypeScript を公式サポートしていますので Vue CLI の設定時に TypeScript 使用するよう選択します。
それでは、
- Vue CLI をインストール
- Dexie.js をインストール
- PapaParse をインストール
- PapaParse の型定義ファイルをインストール*1
- プロジェクトを作る
の手順で行います。
ちなみに、IndexedDB はクライアントサイドで使える NoSQL のデータベースです。
IndexedDB API をそのまま使うと複雑な作りになるのでシンプルに扱える Dexie.js を使うことにします。他にも IndexedDB のライブラリはあるので、使い比べてみるのがよいと思います。
そして、CSV のパースを自力で行うこともできますが、CSVライブラリがあるので楽するために使いましょう。 そこでメンテナンスが継続されていて、使用方法が簡単で思った形に整形しやすい PapaParse を使うことにしました。
それでは、以下のコマンドでインストールしてプロジェクトを立ち上げます。
yarn add @vue/cli --dev yarn add dexie --dev yarn add papaparse --dev yarn add @types/papaparse --dev vue create csv-hangar vue serve
結果、以下のバージョンの環境が作れました。
node: 11.4.0 vue-cli: 3.2.1 typescript: 3.2.2
実際にコードを書く
<!-- Bootstrapを使ってます --> <template> <div class='excel-uploader'> <div class='input-group-btn'> <label class='btn btn-primary'> アップロード <input type='file' accept='.csv,text/csv' @change='handleUpload' class='file-upload'> </label> </div> </div> </template>
それでは Dexie を使って IndexedDB にデータベースを作っていきます。
@Component export default class CSVUploader extends Vue { /** data: メンバ変数 */ public csv: string = ''; public db: AppDatabase = new Dexie('AppDatabase') as AppDatabase; /** Vueのライフサイクルフック */ public created(): void { /** テーブル定義 */ this.db.version(1).stores({ customer: '++id', // auto_increment }); /** デフォルトとしてダミーのリストをIndexedDBに追加 */ this.db.customer.bulkAdd([ { name : '名無しの権兵衛', age: 67, email: 'foo@foo.foo' }, { name : 'ジョン・ドウ', age: 32, email: 'bar@bar.bar' }, ]); }
ここで Chrome をお使いならば Developer Tool を開きます。 Developer Tool の Application タブ から IndexedDB を見てみると、命名指定した「AppDatabase」が作られているのがわかります。 無事に「customer」テーブルも作成されているのが確認できました。
以下でアップロード時の処理を追加していきます。
/** methods: CSVアップロード時の処理 */ public handleUpload(ev: HTMLElementEvent<HTMLInputElement>): void { const files = ((ev.target as any) as HTMLInputElement).files!; if (!files.length) { return; } this.createCSV(files[0]); } /** methods: CSVをパース */ public createCSV(file: File): void { const reader = new FileReader(); const vm = this; reader.onload = (ev: ProgressEvent): void => { const csv: string = (ev.target as any).result; const collection: any = Papa.parse(csv, { header: true }); vm.addRecord(collection); }; reader.readAsText(file); } /** methods: IndexdDBに追加 */ public addRecord(collection: any): void { this.db.customer.bulkAdd(collection.data); } }
以下のようなCSVファイルをアップロードしてみます。
name,age,email 山田太郎,35,hoge@hoge.hoge ジョン・スミス,42,fuga@fuga.fuga イワン・イワノヴィッチ・イワノフ,21,piyo@piyo.piyo
アップロード後に再びDeveloper Toolを開きます。
更新されていない場合は、Developer Toolを再起動するといいです。
型定義ファイル
TypeScript で作る際は、以下の型定義ファイルをインポートする必要があります。
import { Component, Vue } from 'vue-property-decorator'; import { AppDatabase, HTMLElementEvent } from '../libs/definitions';
前者は Vue.js 上で TypeScript を使うのに必ず必要なデコレータです。
Vue CLI のインストールで TypeScript を選択すると、自動でインストールされます。
後者は、自作するしかないので以下に内容を示します。
/** IndexedDB */ import Dexie from 'dexie'; export type DexieDatabase = {[P in keyof Dexie]: Dexie[P]}; /** データベースの型定義 */ export interface AppDatabase extends DexieDatabase { customer: Dexie.Table<ICustomer, number>; } /** テーブルの型定義 */ export interface ICustomer { id?: number; name: string; age: number; email: string; } /** Event */ export interface HTMLElementEvent<T extends HTMLElement> extends Event { target: T; }
TypeScript: Documentation - TypeScript 2.1 等を参考にしています。
HTMLElementEvent
は定義されておらず TypeScript のコンパイラでコケますので定義します。
それでは駆け足でしたが、お元気で!
明日は tenmihi です。お楽しみに!!
*1:TypeScript のコンパイラに型を教えてあげないとビルドできないので、型定義ファイルが必要になります。