余白

https://blog.lacolaco.net/ に移転しました

ng-sake #12 を開催しましたレポート

8月6日にng-sake #12を開催しました!

ng-sake.connpass.com

f:id:lacolaco:20180807220110p:plain

数字

今回の出席者数は 12人 + スタッフ 3人 という構成でした。

参加費は¥1,500、今回もKyashによる集金をオプションで用意しましたが、6人がKyashで送金してもらえました。ご協力ありがとうございました!

内容

会場

株式会社トレタさんのオフィスを使わせていただきました。初めてのトレタでしたが広くてよかったです。さすが五反田バレー。

jp.techcrunch.com

フード・ドリンク

お酒はいつもどおりカクヤスでプレミアムモルツと氷結やほろよいなどを用意しました。

フードは、今回は宅配ピザをやめて、サンドイッチを提供してみました。 みなさんピザはもう飽きてると思うんですよ。

写真撮ろうと思ったらいくつか食べられてました。 f:id:lacolaco:20180807221851p:plain

サンドイッチはフレッシュデリというお店のデリバリーを使いました。おいしかったです。

www.freshdeli.jp

ライトニングトーク

今回は3人がライトニングトークを発表しました。

  • kouさん: ngrx/effectsの話
  • t2さん: Angularをlit-htmlで書けるようにする
  • laco: jsx-to-ivyの続報

いつもよりもさらに一般受けしない話題で、ng-sakeらしいLTでした。

ディスカッション

今回のディスカッションボードはこちら

app.sli.do

反省点

  • YouTube配信しようと思ったけど機材の準備に失敗した
    • 家で練習したので次回はうまくいけばいいな…
  • ソフトドリンクを用意してなかった
    • カクヤスの注文にいれておきたい

最近ng-sakeの開催頻度が低下しているので、もともとの隔月くらいに戻したいなと思っています。 できれば10月〜11月くらいにまたやりますので、興味があれば ng-japan Slackの #ng-sake チャンネルで更新情報をウォッチしてください!

Join ng-japan on Slack!

Angularコンポーネントのスタイルにemotionを使う

追記

型安全にCSSのオブジェクトを書きたいというだけならNgStyleとcsstypeを使うだけでもよさそうだ。

github.com

f:id:lacolaco:20180728090201p:plain

emotionを使うことによる利点は、

  • CSSクラスにシリアライズされるので、テンプレート中で評価対象が文字列となり、Change Detectionのパフォーマンス上で有利

くらいなものか。


今日の境界遊び。CSS in JSをAngularでやりたかった。 常識のある方は真似しないほうがよい。

f:id:lacolaco:20180728002419p:plain

今回使ったのは https://emotion.sh/.

Angularで時々困るのはstylesの中にデータバインディングを置きたいケース。 たとえば、フォームの入力に応じて動的にフォントサイズを変えるようなケースを考える。

image

emotionの css 関数は、与えたCSSスタイルシートシリアライズしてユニークなCSSクラス名に変換してくれる。 AngularのコンポーネントはHTML要素と1:1に対応するので、 [className] プロパティにバインディングすればemotionで生成したクラスを適用できる。

helloClassName$ プロパティは、フォントサイズに応じたCSSスタイルシートCSSクラス名に変換したObservableである。

  helloClassName$ = this.form.valueChanges.pipe(
    map(({ fontSize }) => css({ fontSize }))
  );

これをテンプレート中で次のように使えば、emotionによって動的に生成されたクラスを任意の要素に適用できる。

<hello [className]="helloClassName$ | async" [name]="name"></hello>

ところで、Angularの CommonModule (@angular/common) は、 NgClassNgStyle という2つのディレクティブを提供している。

https://angular.io/api/common/NgStyle

https://angular.io/api/common/NgClass

classNameを使わずとも、次のように書くこともできる。NgClassは文字列以外にも文字列の配列やオブジェクトを受け取れる以外には、本質的にclassNameと何も違いはない。

<hello [ngClass]="helloClassName$ | async" [name]="name"></hello>

NgStyleを使う場合は、emotionではなく生のスタイルシートっぽいオブジェクトを渡すことになる。 本来AngularだけでCSS-in-JSやろうとするとこのAPIになるわけだが、emotionだとcss関数の引数オブジェクトにTypeScript型定義もあるし嬉しいのでは?という目論見がある。 あとemotionなら同じスタイルなら同じクラスになり、キャッシュの仕組みが強いっぽいので、パフォーマンス良くなるかもしれない。

https://emotion.sh/docs/typescript

  helloStyle$ = this.form.valueChanges.pipe(
    map(({ fontSize }) => ({ fontSize: `${fontSize}px` }))
  );

<hello [ngStyle]="helloStyle$ | async" [name]="name"></hello>

すべて同じ動きとなるので好みで選べばよいが、個人的にはReactとの対称性を考えて[className]でよいのでは?と感じる。

実際に動くサンプルは次の通り。

stackblitz.com

Reactの場合、classNameはHTML要素に対応するコンポーネントにしか使えないが、Angularの場合すべてのコンポーネントはHTML要素に対応付けられるので、テンプレート中で親からclassNameプロパティにバインディングするだけで子コンポーネント側でなにもしなくてもよいのは、比較的楽だなと思った。 しかしemotionで一番やりたいstyled-componentがReactしか使えないので、これをどうにかしてみたい。

GraphQLとRESTfulについて今日考えてたこと Backend for Usecase/Resourceについて

DISCLAIMER: これは本当にただのメモ書きで、これがベストプラクティスだとかいう話ではないので、同じようなことを考えてる人いたら今度議論しましょうよ、って程度の話の種。

GraphQLを使うべきスポット、RESTfulが好ましいスポットについて今日ぼんやり考えていて、なんとなく言語化ができる気がするので文字起こししてみる。

Backend for UsecaseとBackend for Resource

バックエンドのAPIには2種類あって、

  • 「データ」を構成する「リソース」を提供するもの
  • アプリケーションの「ユースケース」がもつシナリオのなかで登場する「データ」部分を埋めるためのもの

を区別することが必要そう、と思っている。

まず前者を Backend for Resource (BFR)と呼ぶことにする。これはわかりやすくて、これはまさしくRESTfulそのもの。 RDBやそうじゃないDB、あるいはファイルストレージかもしれない永続化されたリソースにURIを付与し、外部からアクセス可能にするのが役目。 BFRの設計はリソースのスキーマに依存する。

次の後者を Backend for Usecase (BFU)と呼ぶことにする。これがGraphQLが向いていそうなところ。 フロントエンドのユースケースのなかで、永続化されたデータが必要になった時の問い合わせ先。 つまり、BFUの設計はフロントエンドのユースケースに依存する。

f:id:lacolaco:20180712230727p:plain

なんで分けたいのか

BFFの文脈でいろんな理由が挙げられているとは思うが、個人的にはそのシステム自体のライフサイクルの違いに合わせた分割が、柔軟な開発サイクルを支えてくれるんじゃないかと思っている。

フロントエンドはユーザーが触れる最前線にあるので、UXの改善、ユースケースの再設計は成長段階のプロダクトにおいて頻繁に発生する。 フロントエンドについては一旦諦めるとして、バックエンドの中には、そうしたユースケースの再設計に巻き込まれてもしょうがない部分とそうでもなさそうな部分がある。 前者が、「ユースケースに対するデータプロバイダー」としてのバックエンド、つまり上述のBFUで、後者が「リソースコンテナ」としてのバックエンド、つまりBFRじゃないかと思う。

ユースケースが再設計されたとしても、リソース自体に変化があるわけじゃないなら、その部分は残したい。ユースケースに依存する範囲を明確に分けたい、というのが僕のBFU/BFRに対するモチベーションの大きな部分。 その境界でバックエンドのサーバーごと分けてしまうというのは、昨今のコンテナ化の流れにもマッチしてそうな気がする。

なんでBFUはGraphQLがよさそうか

フロントエンドのユースケースの中では目的が必ず存在してデータ要求が生まれる。 すごく単純化したら「ID=2のUserのデータがほしい」ではなく「ID=2のユーザーの名前を表示するためのデータがほしい」という話で、そのユースケースにとってはユーザーの名前がUserテーブルのNameカラムに保存されているとかそんなことはどうでもよく、ただユーザーの名前がほしい、というニーズだけがある。

BFUがない場合は、フロントエンドのなかでBFRから受け取ったデータをユースケースに合った形に自分で整形することになる。あるいはBFRが拡張されてユースケースを受け入れはじめるかのどちらか。

BFUがある場合は、フロントエンドはBFUにユースケースを投げつける。ユースケースを定義するのはフロントエンドの仕事なので、GraphQLのようにフロントエンドでほしいデータの形を定義するのがマッチするはず。

フロントエンド、BFU、BFRが分かれたとき、それぞれのシステムのライフサイクルは次のようになる。

f:id:lacolaco:20180713000156p:plain

悲しいことにフロントエンドはまっさきに壊されるが、それは宿命として諦めるとして、BFUがあることでBFRは長生きすることができる。

やっちゃいけないこと

BFUは「ユースケースに対するデータプロバイダー」としてのバックエンドであるから、常に実装の要件はフロントエンドのユースケースによって決定される。 そのため、次のことはやってはいけない。

  • 「DBからデータが取れるから」というボトムアップで実装すること
    • 本来依存しなくてもよい部分にBFRへの依存が発生する。
  • 複数のユースケースをひとつのqueryで対応しようとする
    • たまたまスキーマが似ているだけでDRY的にまとめるのは本来関係をもつはずのないユースケース間の関係を生んでしまう

BFUは常にアプリケーションのために存在するが、BFRはアプリケーションが無くても成立する。


とりあえずここまで。今日10分くらい会社でryopekoさんと雑談してたネタなのであまりしっかり練った考えではないけど、こんな感じのシステムを作って試してみたい。

Kaizen Platformに入社して1年が経ちました(在職エントリ)

Inspired by

punchdrunker.hatenablog.jp

おはようございます、lacoです。 早いもので、Kaizen Platformに入社してから1年になります。

これは退職したときの記事です。

lacolaco.hatenablog.com

そしてこれが入社後3ヶ月の記事ですね。こんな記事書いてたこと完全に忘れてました。

lacolaco.hatenablog.com

1年間なにやったか

AngularJSアプリケーションのチューニング

入社後の初めてのプルリクエストはAngularJSのチューニングで、めっちゃ遅かったドロップダウンリストの描画を7倍くらい速くしました。

f:id:lacolaco:20180626233219p:plain

その後もしばらく、パフォーマンスに問題がある画面の計測と改修に手を付けていました。

フロントエンド開発チームの体制づくり

僕が入って4人になったフロントエンドチームですが、新規の小規模アプリ開発をいくつか並行して進めることにしました。 4人がそれぞれ個人戦のようになる上で、守るべき約束事を決めようということで、「フロントエンドマイクロアプリケーション憲章」というものを制定しました。

その中で定めたコードフォーマットの決まりのために、Prettierの設定を共通化して公開しました。 いまでも大活躍しています。

lacolaco.hatenablog.com

この憲章は4月に行ったフロントエンドチームの福岡合宿で半年ぶりにアップデートされました。その記事はKaizen Platformのエンジニアブログでまとめられています。

developer.kaizenplatform.com

憲章があることで、逆に心理的に安全に冒険できる環境が作れているんじゃないかと思っています。「公園には柵があるから自由に遊べる」、というのが最近の口癖。

AngularでSPA開発

当然といえば当然ですが僕もSPA開発やってまして、さっき書いた 新規の小規模アプリ開発 ってやつをAngular使って2つほど開発してます。 設計を試行錯誤したり、アプリケーションの要件が変わったりいろいろありますが、TypeScriptでよかった〜となる場面がとても多いですね。 コンパイラによる静的検査がなければ安心してリファクタリングできません。

ワークフローづくり

2つのうち今年の頭から始まったものは、実験的にバックエンドとフロントエンドの間の通信にGraphQLを採用しています。 API Spec中心開発と銘打って、リモートワークを前提とした非同期的な分業をスムーズにするためのワークフローを考えて、実践してみたのは楽しかったし学びも多かったです。

developer.kaizenplatform.com

同じような文脈で、フロントエンドとバックエンドの開発者間だけじゃなく、デザイナーとの分業においても仕事を型化してワークフローを定義するとよいんじゃないか、ということでそういったことも今年に入って取り組んでいます。 デザイナーの中に閉じてブラックボックス化していたデザインプロセスを分解、整理して、プロダクト設計原則という形に再構成しました。

developer.kaizenplatform.com

ここ1ヶ月くらいは、この設計原則に従って、いままでだったらデザイナーに頼んでいたタスクを、僕が代わりに原則に沿って進めることを試しています。 ステップごとに進めばいいのでデザイン初心者でも安心して仕事ができます。やはり型があると安心。

ver 1.0としているので、うまくいくところ、いかないところをもとにバージョンアップを続けていくつもりです。

オープンコミュニティ活動

おそらくお気づきかとは思いますが1年間まったく滞ることなくAngular日本ユーザー会をはじめとしたいろんなオープンコミュニティの活動つづけてました。 大きめのイベントだけ拾ってみてもこんだけあちこちいろいろやってて、露出の多い1年でした。

f:id:lacolaco:20180627001517p:plain

本の監修なんかも経験しました。

lacolaco.hatenablog.com

やりたかったこと

1年前の記事で、今後やりたいことを書いていました。

Angular日本ユーザー会をはじめとしたコミュニティ活動は今後も継続したい

できていますね

OSSコントリビューション増やしていきたい

できているんじゃないかと思います

JavaScriptを駆逐したい

きびしい

Flutterに夢を見たい

リリースプレビューおめでとう!!!!!!!!!!🎉🎉🎉🎉

ということで概ね達成している気がします。

1年やってきて

思ってた以上にKaizen Platformという会社は馴染んでいて、日々楽しく仕事をしています。 プログラミングするのも好きなんだけど、どちらかというとエンジニアリングというか、どうやったら生産性を向上できるのかっていうのを考えるのが好きなんだなーというのに、この1年で気づきました。 知らない自分を発見できた感じ。

これはチームのマネージャーになりたいとかそういうわけじゃないんですよね。 むしろデベロッパーがどうすれば生産性上がるかってことは、デベロッパー自身も当事者として考えなきゃいけないところだと思うし、考えなきゃいけないっていうか、考えると楽しいですよ。 自分を歯車に見立てて、うまい回し方や他の歯車との噛み合わせ方を考える、パズルゲームみたいな感じがします。

って感じで、脳みその使い方が1年間でだいぶ変わったなーと、自分なりに思います。

これから

特になにか節目ということでもないので今やってることは引き続きやっていきます。 あと、自分の働き方をいろいろ実験してみようと思ってて、有り体に言えば副業っぽいんですが、今年のテーマとして「OSSのように働く」ってのを目指してます。

OSSにコントリビューションするのって、面白そうなリポジトリを見つけて、自分が貢献できそうな部分を見つけて、そこにアプローチしていくじゃないですか。 そんな感じで、面白そうな事業を見つけて、自分が貢献できそうかどうか自分からアプローチして、コントリビューションするように仕事ができたら楽しいんじゃないかって考えてます。

せっかく新しい自由な働き方を推進してる会社に所属しているので、自分自身で試行錯誤していきます。

まだ実際にできているわけじゃないんですけど、もしそういうことができたら楽しいなーと思ってるし、続報があればまたブログ書きます。

ところで、試しにPatreon登録してみました。$1から投げられるのでぜひよろしくおねがいします。

www.patreon.com

はじめた経緯などはこちらの記事に書いています。

lacolaco.hatenablog.com

それでは。

Angular CDKのPortalを使ったローディングラッパーの実装

今回はAngular CDK(Component Dev Kit)の Portal 機能を使って、ローディングラッパコンポーネントを実装する例の紹介です。 Angularの基本的な書き方はわかっている前提の内容になります。


ローディングラッパーとは次のようなテンプレートで、ローディング中はローディング表示を、ローディングが終わったら子要素を表示するようなコンポーネントを指しています。 たとえばこのようなテンプレートです。

<mat-card>
    <loading-wrapper [loading]="isLoading$ | async">
        <div>Done!</div>
    </loading-wrapper>
</mat-card>

このように、ローディング状態によってビューが差し替わります。

f:id:lacolaco:20180701162327g:plain

CdkPortalの使い方

@angular/cdk/portalからインポートできるPortalModuleによって、cdkPortalOutletなどのいくつかのディレクティブが有効になります。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { PortalModule } from '@angular/cdk/portal';

import { AppComponent } from './app.component';
import { LoadingWrapperComponent } from './loading-wrapper.component';

@NgModule({
  imports: [
    BrowserModule,
    PortalModule
  ],
  declarations: [AppComponent, LoadingWrapperComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

cdkPortalOutletディレクティブは、渡されたCdkPortalに紐づくビューをその位置に表示します。

https://material.angular.io/cdk/portal/api#CdkPortalOutlet

  <ng-template [cdkPortalOutlet]="contentPortal"></ng-template>

つまり、ローディングラッパコンポーネントがおこなうことは、ローディング状態に応じてcontentPortalの中身を差し替えることです。

TemplatePortalの作成

CdkPortalはいくつかの種類がありますが、今回はTemplateRefをビューとして保持するTemplatePortalを使います。 ローディング状態のテンプレートをloadingContent、親コンポーネントから渡されるコンテンツ要素をcontentとして、それぞれViewChildコンポーネントから参照できるようにします。

<ng-template #loadingContent>
    <div>
        <div>Loading...</div>
        <mat-spinner color="accent"></mat-spinner>
    </div>
</ng-template>

<ng-template #content>
    <ng-content></ng-content>
</ng-template>

<ng-template [cdkPortalOutlet]="contentPortal"></ng-template>

コンポーネント側では、初期化時と、ローディング状態を制御するloadingプロパティが変わったときにビューをスイッチするようにします。 次のコードにおけるswitchViewメソッドが、TemplateOutletを作成している部分です。

@Component({
  selector: 'loading-wrapper',
  templateUrl: './loading-wrapper.component.html'
})
export class LoadingWrapperComponent implements OnInit, OnChanges {
  @Input() loading: boolean;

  @ViewChild('loadingContent') loadingContentTemplate: TemplateRef<any>;
  @ViewChild('content') contentTemplate: TemplateRef<any>;

  contentPortal: CdkPortal;

  constructor(private vcRef: ViewContainerRef) { }

  ngOnInit() {
    this.switchView();
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.hasOwnProperty('loading')) {
      this.switchView();
    }
  }

  // 現在のローディング状態から適切なTemplatePortalを作成する
  switchView() {
    this.contentPortal = new TemplatePortal(this.getTemplate(), this.vcRef);
  }

  private getTemplate() {
    if (this.loading) {
      return this.loadingContentTemplate;
    }
    return this.contentTemplate;
  }
}

まとめ

  • CdkPortalを使って、状態に応じたビューの差し替えの実装が簡単にできる
  • TemplatePortalを使って、ng-templateから取り出したTemplateRefCdkPortalに変換できる

完成形がこちらです。

stackblitz.com