B-Teck!

お仕事からゲームまで幅広く

【振り返り】2022年4月

前月の振り返り
【振り返り】2022年3月 - B-Teck!

学習

今月やったこと

所感

読書やアウトプットできた量が少なく、先月に引き続き今月もあまり奮わなかった一ヶ月だな〜という感じでした。
生活リズムも乱れがちだったので、GW挟んじゃいますがちょっとずつ整えていこうかなという気持ち。

しばらくは個人的にフロントエンド強化期間ということもあり、Angular After Tutorial を参考にしつつアーキテクチャ周りをサンプル実装してみたり、手元の実装のアーキテクチャを変えてみたりしていました。
データフローや責務の分割という面ではだいぶ理解が進んできましたが、コンポーネント指向のフレームワークでテンプレートやCSSをどう組み合わせていくのかの勘所が弱いので、来月はそこを中心にやっていけたらなと思ってます。

相変わらず仕事でどういうキャリア設計をしていきたいのかわからんな〜という気持ちになっているものの、悩んでもしょうがないなと思えるようにようやく消化できてきたので、ある程度踏ん切りはついた気も。
ネット上だと強い人とかキラキラした人の事例ばかりが目につくので不安になりがちだけど、実際みんなこういうのどう考えてるんですかね。

【TypeScript】CSVをパースする

csv-parseのインストール

$ npm install csv-parse

そのままだとエラーが発生してうまく動かないので対応する。

Warning: Module not found: Error: Can't resolve 'stream' の対応

$ npm i stream-browserify
"paths": {
  "stream": [
    "./node_modules/stream-browserify"
  ]
}

polyfills.tsの修正

この状態でビルドすると下記のようなエラーがいくつか出る。

  • Uncaught ReferenceError: global is not defined
  • Uncaught ReferenceError: Buffer is not defined
  • Uncaught ReferenceError: process is not defined
  • ERROR TypeError: process.nextTick is not a function

これらに対応するためにpolyfills.tsの末尾に下記を追加する

(window as any).global = window;
global.Buffer = global.Buffer || require('buffer').Buffer;
(window as any).process = {
    env: { DEBUG: undefined },
    nextTick: function() {
        return null;
    }
};

csv-parseの利用

import {parse} from 'csv-parse';
// readAsTextとかで読み込んだFileをparseの引数にする
// パース結果は `data` ハンドラで取得でき、1レコードごとに処理される
// 下記のコードの場合は1行ずつconsoleに出力される
parse(file).on('data', (data) => {
    console.log(data);
});

参考

[Angular] Angular 12 (Webpack 5)アップデート記念ビルドエラー対処メモ - Qiita Angular6で”global is not defined”が出た時の回避策 | DevelopersIO
javascript - Angular 6 - process is not defined when trying to serve application - Stack Overflow

【TypeScript/Angular】Formでファイルを読み込む

ReactiveFormsModuleの追加

imports: [
    environment.production ? [] : AkitaNgDevtools.forRoot(),
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
]

Templateにファイル読み込み用のFormを追加

<form [formGroup]="form" (ngSubmit)="submit()">
  <div class="form-group">
    <label for="file">File</label>
    <input
      formControlName="file"
      id="file"
      type="file"
      class="form-control"
      (change)="onFileChange($event)">
>
<div*ngIf="f['file'].touched && f['file'].invalid" class="alert alert-danger">
      <div*ngIf="f['file'].errors?.['required']">File is required.</div>
    </div>
  </div>
  <button class="btn btn-primary" type="submit">Submit</button>
</form>
{{input}}

Componentに読み込む処理を追加

  • ファイルの選択時に、選択したファイルをeventオブジェクトから取り出す
  • FileReaderのread結果をTemplateに反映している
  • 今回は読み出したいCSVがSJISだったのでEncodeを指定している
import {Component, OnInit} from '@angular/core';
import {FormBuilder, Validators} from '@angular/forms';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
    form = this.fb.group({file: ['', [Validators.required]]});
    file: File | null = null;
    input = "";

    constructor(private fb: FormBuilder) {
    }

    get f() {
        return this.form.controls;
    }

    ngOnInit() {
    }

    onFileChange(event: any) {
        if (event.target.files.length > 0) {
            this.file = event.target.files[0];
        }
    }

    submit() {
        this.readAsText(this.file!).then(result => this.input = result);
    }

    private readAsText(file: File): Promise<string> {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = () => {
                resolve(reader.result as string);
            };
            reader.onerror = () => {
                reject(reader.error);
            };
            reader.readAsText(file, 'shift-jis'); // デフォルトのEncodeはUTF-8
        });
    }
}