Quantcast
Channel: Yohei Isokawa
Viewing all 357 articles
Browse latest View live

【Angular】@angular/fire/functions が動かない場合の対処方法

$
0
0

@angular/fire v5でCloud Functionsが使えるAPIが追加されました。

早速ドキュメントどおりに使ってみたんですが動かない。

httpsCallableを呼ぶとエラーになります。

https://null-[projectId].cloudfunctions.net/someFunction
Access to fetch at 'https://null-[projectId].cloudfunctions.net/someFunction' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

エラー内容を見るとCloud FunctionsのAPIエンドポイントがうまく設定できてない模様です。
「null-」が頭についてる。

@angular/fire/functionsのSpecを見ると、Functionsのリージョン指定が出来るようです。

import { AngularFireFunctions, AngularFireFunctionsModule, FunctionsRegionToken } from '@angular/fire/functions';

...

      providers: [
        ...
        // これじゃね…
        { provide: FunctionsRegionToken, useValue: 'asia-northeast1' },
      ]

なので、NgModuleのprovidersに以下を追加しました。

import { AngularFireFunctionsModule, FunctionsRegionToken } from '@angular/fire/functions'; // FunctionsRegionTokenを追加

...
  providers: [
    { provide: FunctionsRegionToken, useValue: 'us-central1' }, // 関数がデプロイされているリージョンを追加
  ]

動きましたとさ、おしまい。


サンワサプライの液晶画面クリーナーが最強説

$
0
0

いままでPC用の液晶クリーナーを買い続けてきて満足の行く商品に巡り会えなかったのですが、ようやくこれだと思う商品を見つけました!

それがサンワサプライのスプレータイプの液晶画面クリーナーです。

サンワサプライ 液晶画面クリーナー 「CD-KSP1」がすごい

サンワサプライ液晶画面クリーナー

ぼくは今までスプレータイプのクリーナーはあまり信頼してなかったんです。
というのも、前にエレコムのスプレータイプのものを使っていたんですけど、拭き跡がめちゃくちゃついてて使えねぇなと思っていました。

それでもサンワサプライ製のスプレータイプがネット上での評価が高いので試しに買ってみたのですが、これがまぁすごかったのですよ。

クロスにシュッとひと吹き

黒いクロスがセットでついているので、拭きたい場所に直接スプレーするのではなくクロスにシュッと一吹きします。

ディスプレイを優しく拭きます

そしてホコリや手のあぶらがついてしまったディスプレイをさっと拭きます。
そこまでゴシゴシ力を入れなくても優しく拭けば汚れが落ちます。
他の商品より力がいらない気がしますね。

拭き取り後の様子

すると拭き跡なんて皆無で簡単にきれいになりました!
サンワサプライすごいじゃん! そしてただただエレコムが憎い。

ホコリもしっかり取れています

見てください、クロスにはホコリがごっそりついています。
この汚れを絡め取るクロスがまた良いんでしょうねー。

そしてクロスは洗濯すれば繰り返し使えますので経済的です。

サンワサプライの液晶画面クリーナーはおすすめです

PCだけではなくスマホやタブレットにももちろん使えます。

液晶クリーナーはペーパータイプのものもありますが、使わないとカラカラになって使えなくなってしまうので、個人的にスプレータイプがおすすめです。

ぜひ購入検討の際は参考にしてみてください!

created by Rinker
サンワサプライ
¥943(2019/04/11 14:01:16時点 Amazon調べ-詳細)

【ドローン】屋外での充電にSmatreeの充電器がめちゃくちゃ使える件

$
0
0

ドローンのバッテリーを外でも充電したいときがあるじゃないですか。

ぼくは車で移動するのでカーチャージャーを使っていたのです。
でもカーチャージャーだとエンジンを常にかけている必要があるので、車から離れられないのは効率が悪い。
それに車のヒューズが切れてしまったり電気系統のトラブルも多かったです。

そこで持ち運びできる充電バッテリーを買いました。

Smatree急速充電器がコンパクトで便利

Smatree DJI Mavic Pro用急速充電器

Smatreeの急速充電器SP180はこれ自体がバッテリーで、ドローンの充電器を使って充電しておけばこれごと持ち運びできる代物です。
今回買ったMavic Pro用のモデルは充電スロットが2個ついていて、重さは約1kgくらい。
そして、満タンに充電しておけばMavic Proのバッテリーを5〜8個フル充電できます。

Mavic Pro用の他に、Spark・Mavic Air用のラインナップもあります。

大きさはペットボトルくらい

大きさはだいたいペットボトルサイズなので、非常にコンパクトです。
上にある取っ手は折りたたむことができません。

Smatree急速充電器の充電方法

バッテリーは上から押し込む

Mavic Proのバッテリー充電は簡単で、充電器を寝かした状態で上からバッテリーを押し込みます。
バッテリーの爪がカチッと鳴るまで押し込みます。
この時点ではまだ充電は始まりません。

横のボタンで電源をオン

そして本体横の電源ボタンを押します。
これで充電が始まります。

電源をオンにすると充電される

1本あたり大体1時間くらいで充電が終わる感覚です。

充電器には過充電保護や過熱保護機能があるのでとても安心ですよ。
飛行し終えてすぐは過熱保護機能が働くことがあるので冷ましてから充電するのをおすすめします!

青色の残量インジケータが光ります

側面の電源ボタンの脇に残量インジケータがあります。
Mavic Proのバッテリー残量と同じで4段階表示です。

2個同時充電が出来る

2つ同時充電が可能

Smatreeの充電器の最大の特徴は2個同時充電ができることです。
早く充電したい場合はこれがめちゃくちゃ重宝します。
2個同時だと70分前後でフル充電できます。

コントローラーやスマホの充電もできる

側面のUSB端子

側面にはUSB端子が2個ついています。
ドローンのコントローラーやスマホを充電できるので、これ1台あれば外で困ることが一切ありません。

外にいる時間が長い場合は検討を

2つ同時充電が可能

Smatreeの急速充電器はめちゃくちゃ良い買い物でした。
ぼくはMavic Proのバッテリー5個とSmatreeの充電器を使って1日中撮影を行っています。
使い終わったバッテリーは現場ですぐに充電することで、バッテリーがなくて良い画が撮れない状況が全く無くなりました。
仕事で撮影を行うような場合は最低限持っておくことをおすすめします!

Smatree DJI Mavic Proフライトバッテリー用25000mAh大容量急速充電器、同時にMavic Proバッテリー2個を充電でき 携帯式モバイルバッテリー
Smatree

地方エンジニアに必要なこと

$
0
0

この記事はex-KAYAC Advent Calendar 2018の7日目の記事です。

どうも、イソップです。
カヤックを退社後、地方で働き始めて丸3年が経ちました。
都会ではワークライフバランスを求めて地方移住を考えている方もまだまだいると思うので、今日はその辺りの話をしたいと思います。

自己紹介

2015年までクライアントワーク事業部のHTMLファイ部に所属していました。
当時はキャンペーンサイトのフロントエンド実装などのほかに、「しっかり系エンジニア」という立ち位置で自社コーポレートサイトのIR情報の実装などを行っていました。

その後は新潟県上越市に引っ越してフリーのフロントエンドエンジニアとして活動しています。
現在は主にAngularやReactアプリケーションのフロントエンド実装を行っています。いわゆるSPAおじさんですね。

プライベートではwriter.appというライター向けの文字起こしサービスの運営や、UdemyでAngularのレクチャー動画を公開してします。

地域の仕事をするか、都会の仕事をするか

さて、まず地方で働くと考えた場合に大きく分けて、地域の仕事をするか都会の仕事をするかという2つの選択肢があります。

地域の仕事をするには地元の企業に転職するのがスムーズですよね。もろに地域貢献ができます。
新潟県でもWeb系・アプリ開発・SIerなどレベルの高い会社はいろいろとあるようです。

一方で最近は都内の企業が地方にサテライトオフィスを作るケースが増えていて、ぼくの住む上越市では最近上場したテラスカイさんやAWSで有名なクラスメソッドさんらがリモートワークで業務を行っています。
また上越市のお隣の妙高市にはサイボウズの竹内さんがリモートワークされていたり、そのまたお隣の長野県信濃町ではWeb制作会社のLIGさんの野尻湖オフィスもあります。(いつもお世話になってます)

フリーランスのぼくはというと地域の仕事もするし、東京の仕事もお受けしています。
その時その時で楽しいと思える仕事を選んでる感じです。

インターネットがあれば地方でも自由な働き方が選択できる時代です。
自分がその地域で何をしたいのか、そこでどう暮らしたいのかを意識すると自分にあった働き方を選べるのではないかと割と本気で思っています。

リモートワークの注意点

リモートワークが出てきたのでここで少し触れておきましょう。

実はリモートワークではお互いの意思の疎通が難しい状況になりがちです。
その大体の場合がコミュニケーション不足。

ぼくは東京のパートナーさんと仕事をするときは、基本的にリモートでの対応になります。
その際やり取りの回数が少ないときほど、仕事がスムーズに進行しません。
メールやチャットの文字ベースでは、相手が何を考えているのかまで正確に把握できないからです。

なので、割と頻繁にビデオ通話で打ち合わせや進捗確認をするようにしています。
相手の顔を見て話せると相手の空気感もわかりますし、自分の状況をすべて話したほうがお互いに安心します。
何より口で質問したほうが早いのでストレスも少ないのです。
ビデオ通話の頻度は、毎日や2〜3日に1回、1週間に1回など状況に応じて設定しておくと安心を維持できます。

この辺は経験しながら慣れていくのがいいのかなと思います。
リモートワークにはそれなりの工夫も必要なのだと頭の隅にでも置いておいてもらえると嬉しいです。

地方の勉強会には顔を出すべし

都内にいた頃は勉強会に積極的に行っていましたが、地方だと残念ながら頻繁に開催されないのが現状です。
ともなると、新しい知識を仕入れたり、エンジニア同士の交流を深められないのでとてつもないデメリットになります。
技術情報の検索スキルは上がっても、ひとりは寂しいですよマジで。

悪いことは言わないので、参加できる勉強会があれば顔を出しておきましょう。
行ってみると色んな分野の優秀な方が多いので、場所はどこであろうが話を聞くのは面白いです。

そういえばぼくが住んでる場所では勉強会が皆無だったので、自分で開催しちゃってます。
そういうアプローチもありだと思います。「無ければ作る」の精神は大事。
その結果@Nkzn氏からReact Nativeの話をしてもらったのは貴重でした。

どこにいても発信力がカギ

で、結局地方にいて思うことは、どこにいても発信力が重要だということです。

ぼくはずっとブログを続けているのですが、ちょこちょこブログ経由でお仕事の相談を頂いたり、登壇のお誘いをもらうケースがあります。
それに勉強会で初めて会う方から「いつもブログ見てます」と言われることも多く、ブログの発信力はあなどれないなとしみじみ実感している次第です。
発信が苦手なエンジニアは多いと思いますが、アピールすることから逃げてはもったいない。

ぜひ文章を書くのが苦じゃないという人はブログなりTwitterなりで積極的に発信すると、地方にいても何かしらの変化があると思います。
それに小さくたって何でもいいから作ったら公開することも大事。誰かの目に留まるかもしれません。
地方勢のレベルの高さをアピールしようじゃありませんか。

さいごに

ということでぼくなりに地方で働く技術を書いてみました。
地方で働くこと自体は難しいことじゃないんですよ全然。
それよりも好きな場所で働きながら、どう生きていくかが重要じゃないかと考えています。
地方はいいぞ!

明日の担当はカロリくんです!

【React】styled-componentsで出力されるクラス属性にファイル名やdisplayNameを表示する

$
0
0

styled-componentsを利用したコンポーネントのクラス名は、次のようにランダムな値が割り当てられるためデバッグが超絶し辛いです。

<div class="sc-bwzfXH gJIwTT"><button class="sc-bdVaJa lngsml">Learn React</button><button class="sc-bdVaJa gcxEky">Learn React</button></div>

そこでbebelのプラグインとして配布されているbabel-plugin-styled-components を利用することで、コンポーネントが書かれているファイル名やコンポーネントのdisplayNameをクラス名に出力することができます。

babel-plugin-styled-components

https://github.com/styled-components/babel-plugin-styled-components

まず始めにインストールしましょう。

npm install --save-dev babel-plugin-styled-components

インストールできたらこのプラグインの設定を行います。
package.json もしくは .babelrc に設定を追加します。

package.jsonの場合

"babel": {"plugins": [
    [ "babel-plugin-styled-components" ]
  ]
}

.babelrcの場合

{"plugins": [
    [ "babel-plugin-styled-components" ]
  ]
}

これで出力を確認してみると……

<div class="App__Container-w3zbcp-1 iXTFHr">
  <button class="App__Button-w3zbcp-0 dUeHxi">Learn React</button>
  <button class="App__Button-w3zbcp-0 hVnQOu">Learn React</button></div>

App__Container-**** , App__Button-***のように、
ファイル名__displayName-*** の形式でクラス名が出力されます。

これですよ、これこれ。

オプション設定

bebel-plugin-styled-componentsの指定時にオプションを渡せます。

ファイル名を出力しない

"plugins": [
  ["babel-plugin-styled-components", {"fileName": false
    }
  ]
]

displayNameを出力しない

"plugins": [
  ["babel-plugin-styled-components", {"displayName": false
    }
  ]
]

CSSのminifyを行わない

"plugins": [
  ["babel-plugin-styled-components", {"minify": false
    }
  ]
]

デッドコードを除去する

"plugins": [
  ["babel-plugin-styled-components", {"pure": true
    }
  ]
]

他にも指定できるので、この記事の一番下にある公式ドキュメントのリンクから確認してみてください。

環境で切り替える

babelのenv設定で出力を切り替えるようにしておくとスマートです。

"env": {"development": {"plugins": [
      ["babel-plugin-styled-components", {"minify": false
        }
      ]
    ]
  },
  // 本番環境は出力しない
  "production": {"plugins": [
      ["babel-plugin-styled-components", {"fileName": false,"displayName": false,"pure": true,
        }
      ]
    ]
  },"test": {"plugins": [
      ["babel-plugin-styled-components", {"fileName": false
        }
      ]
    ]
  }
}

公式ドキュメント

https://www.styled-components.com/docs/tooling#babel-plugin

2019年やりたいことリスト

$
0
0

ぴょんくんの「私の #2019年やりたいことリスト」が良いなと思ったので、真似して作ってみました。

私の #2019年やりたいことリスト|Pから始まる名前のヒト|note

定期的にマインドマップなどで視覚化していたのですが、公開するのはこれが初めてです。

ルールはこちら↓

・2019年にやりたいことを思いつくだけ書く
・いつでも追記、削除OK

仕事

  • writer.appのアップデート
  • writer.appを事業化
  • バートナーさんを増やす
  • Dart入門
  • Flutterでアプリを1本作る
  • ネイティブアプリをストアで公開する
  • アプリ開発で仕事を受注する
  • TypeScript再入門
  • オライリー「はじめての自動テスト」を読む
  • フロントエンドのテスト入門
  • 実践TDD
  • 受託から離れる具体的な計画を考える
  • ノートへのメモ書きを増やす
  • 新しくハマれるものを1個見つける

個人

  • 上越TechMeetup開催(できたら3回)
  • 登壇回数最低2回
  • 運営者ギルドを利用する(登録は済み)
  • ホテルでひたすら個人開発する一人合宿をする
  • Udemyで新コースを2つ公開する
  • 【完了】ラテアートできれいなハートを描く
  • 【完了】ラテアートでリーフを描く
  • ラテアートでチューリップを描く
  • ラテアートでオリジナルの柄を描いてみる
  • 新しいエスプレッソマシンを買う
  • ショートケーキ(ホール)を作るのリベンジする
  • 【完了】パンを焼けるようになる
  • たこ焼きスキルを上げる
  • 【完了】 MIDIキーボード買う
  • ピアノ弾けるようになれたらいいな
  • 作曲できるようになる
  • DTM再開
  • ヨーヨー「ループアームストラップ」マスターする
  • ヨーヨー「タングラー・リストクロス・タングラー」マスターする
  • ヨーヨー「2ハンドイン・アウトバーチカルパンチ」マスターする
  • ブラックバス50upを釣る
  • 友達にブラックバス40up釣らせる
  • 畑仕事再開
  • 梅干し作り(4kg)リベンジする
  • 旅行用のスーツケース(4輪)を買う
  • 要らないものを捨てる・売る

家族・友達・知り合い

  • 生活拠点を考え直す
  • 引っ越し
  • 奥さんの誕生日ケーキを作る
  • 結婚記念日を2倍祝う
  • 【完了】夫婦で福岡へ旅行に行く
  • 夫婦で2回以上旅行に行く
  • 毎月できる限り貯金する
  • 自分で作ったお菓子を近所の人におすそ分けする
  • 自分で淹れたコーヒー・カフェラテを振る舞う
  • いろんな切り口の飲み会増やす
  • 【完了】近所でエンジニア飲み開催する
  • BBQの道具をそろえて焼く
  • スノピのタープ買う
  • 何人かでキャンプする

まとめ

とりあえずこんな感じでしょうかね。
たぶん増えたり減ったりするはずなのでちょこちょこ追記していきます〜。

自分の考えを可視化&シェアすることで、実現にものすごく近づく…!
そしていつも見えるところにあると気が引き締まりますね!

【告知】3/8に開催される新潟グラムさんのイベントに登壇します!

$
0
0

2019年3月8日(金)に新潟市で開催されるIT勉強会に、スピーカーとして登壇させていただきます!

新潟グラム2019 Vol.1「WEBに関わる人が知っておきたい2019最新トレンド」

新潟グラム2019 Vol.1「WEBに関わる人が知っておきたい2019最新トレンド」

※イベントページでぼくだけ「アー写感」がまったくないのは気にしないでほしい。

新潟市では毎年3月の2週目の土日に日本酒の祭典「にいがた酒の陣」が行われます。
その前日の夜に勉強会をするのが毎年の恒例でして、今年はなんとスピーカーとしてオファーをいただきました。

ぼくのセッションではフロントエンドエンジニアの視点から最近のWebアニメーション実装手法について30分お話しします。
デザイナーさん向けのイベントなのであまり難しい内容にならないように心がけたいと思いますが、、
そうでなくてもおそらく時間が足りなさそうな気がしてるので、参加してよかったと思ってもらえるようにできるだけ内容の濃い話をしたいと思います。

お近くの方はぜひ足を運んでもらいたいですし、遠方の方も酒の陣と合わせて参加いただけると嬉しいです!

#TDDBC 長岡に参加してTDDを入門しました

$
0
0

2/9(土)長岡でTDDBC(TDDブートキャンプ)が開催されました。

TDDBC 長岡 2019-02 – connpass

密かに憧れだった、@twadaさんが来るという事実に衝撃が走った。

ということで速攻参加登録しました。

なぜ参加したのか

ぼくはWeb制作会社上がりのフロントエンドエンジニアなわけですが、最近は主にWebアプリ開発の実装をしています。

で、フロントエンドといえど広告系とシステム開発系は全くの別分野です。サッカーとラグビーぐらい畑が違います。

広告系のお仕事は基本的に時間が無いのでわーーっと作って確認して公開って流れが普通でして、テストを書くことはバチクソ稀です。
しかもフロントのテストなんて特に書かない。

そういった業界的事情もあり、ちょっと規模の大きなアプリケーションでテストを書かずに実装を進めると、変更した部分と全然違うところで不具合が出たり、どうにかこうにか動いてる状態に手入れるのツラいわ……状態でにっちもさっちもいかないという。そんなこんなでエンジニアを辞めたくなるときがあるんですね。
テストを書くスタンスの畑で育たなかったおかげでそのつけが回ってきてるわけです。

テスト書かなきゃな、、とは思っててもとにかく腰が重い、、、と思ってたところでTDDBCの情報が! なんと長岡で!
ぼくはこの最大のチャンスを逃すわけにはいかなかったのです。

例のライオン現る

twadaさんとライオン

twadaさんの自己紹介にはあの有名なライオンが……。
いつも見るたびにごめんなさいって気持ちになるやつ。

こいつは「ワイルド・サバンナ」というスタンドらしい。
オフラインで見れて超感動ものです。

終始笑いを誘うtwadaさんのワードセンスがとても印象的でした。

何をしたのか

午前中はtwadaさんの基調講演でTDDとは何なのか、FizzBuzz問題でのTDDデモを見ながらひたすら話を聞きしました。

ざっくり要点をまとめると、

  • 実装者の不安を解消するのがテストである
  • 実装対象の問題を小さく分割する
  • TODOを作成して進める(GitHub Flavored Markdown がおすすめ)
  • 簡単なテストから始める(はじめは設計の目的が強いため。難しいものから始めると挫折する)
  • テストケースは日本語(母国語)がおすすめ
  • 1. テストを失敗させる。(Red)
  • 2. まずはとにかくテストを通す。(Green)
  • 3. リファクタリングはその後。(Refactor)
  • 歩幅を小さくする
  • テスト→仮実装→三角測量→実装
  • テスト→仮実装→実装
  • テスト→明白な実装
  • テストの構造化とリファクタリング
  • describeブロックでグルーピング&構造化する
  • テストが仕様書になるように
  • 全部「テスト駆動開発」に書いてあります。

という感じです。

お昼を挟んで午後、2人1組になってペアプロをしながらお題に沿った実装をTDDでテストを書きながら実践しました。
お題は「軽減税率」ということで、商品から軽減税率を求める実装を行いました。
軽減税率の世界 – gist

ぼくとペアになった方は日頃からテストを書いてるらしく(!)、Jestの使い方を教えてもらいつつ、、どうテストしていくかみたいな観点をリードしてもらいました。
おかげでTDDの実践はスムーズにできていたように思います。

最後は各チームごとに、どんなコードを書いてどんなことを意識したかなどを紹介しながら全員にコードレビューをしてもらい、twadaさんからもアドバイスをいただきました。

当日のコードはこちら。
https://github.com/yuhiisk/20190209-TDDBC/tree/master/lib

テストの書き方も人それぞれで、奥の深さを感じました。
最高に有益&楽しかったです。

感想

これまでにテストを書く習慣を身につけようとしましたが、テストを書く意味を理解できなかったり、そもそもこれで書き方合ってるのかとか、結局書くの面倒だなとかで導入のハードルがかなり高く感じて断念してました。
でもこういったイベントでテストの重要性を理解して手を動かすと、「あ、テストってこういうことか」と気づくことができて、テスト君とかなり友達になれた気がします。
「まずテストに触れてみる」というきっかけにはとても良いイベントだと思いました。

反省点としては、テストフレームワークのAPIを事前にもっと調べておくべきでした。
1年前にちょこっと書いてたので、行く前にざっと公式リファレンス読んでおけば大丈夫かな〜と思っていたのが間違いでした。。
Jestであればdescribe, test(it), assert文などはある程度自分で書いてみて、使い方を理解しておくことは重要です。

あと、ペアプロ中にTAの方から「この商品データにはIDはないの?」と聞かれ、「え、それって要らなくね?お題の本質的な話じゃなくね?」とペアで不安に駆られ、要らない思考をしてしまったのが少し残念でした。
別に言わなくても良かったんじゃないすかね、それは。

最後に

今回のTDDBCではとても有意義な時間を過ごすことができました。
2週間くらい経ってしまいましたが、未だテストを書く気満々です!

TDDBCが気になるそこのあなた。全国で開催されているのでぜひ参加してください。
http://devtesting.jp/tddbc/

(次は上越でやったら面白いかもしれないですね???)

twadaさん、主催の高野さん、スタッフの皆さんお疲れ様でした。

参考書籍

twadaさんが最後に紹介していたTDD・テストに関する参考書籍です。紹介されていたもの全てではないですが、自分用のメモ書きとして。

twadaさん翻訳の名著「テスト駆動開発」。イベント後に速攻でポチりました。

created by Rinker
¥3,024(2019/04/11 09:25:41時点 Amazon調べ-詳細)

 

こちらはテストの範囲について書かれた2つ。
どこまで書けばいいのかわからなかったので、次のステップとして読みたい。

 

テストのないレガシーなプロジェクトにどうテストを持ち込むかを戦略的に紹介している書籍。
ぼくが今一番必要としているトピック。

 

こちらはどちらかというと戦術的な内容だそう。

 

最後にモダンなテスト駆動開発手法に書かれた1冊。


#新潟グラム 2019 vol.1 でアニメーション実装の話をしてきました

$
0
0

3/8(金)新潟グラムさんで登壇してきました。

新潟グラム2019 Vol.1「WEBに関わる人が知っておきたい2019最新トレンド」

新潟では毎年3月に「にいがた酒の陣」という日本酒の祭典が開催されるのですが、新潟グラムはその前日に行っている非公式のプレイベント(?)です。

今回はアニメーションについてお話ししてほしいとオファーを頂いたのでお邪魔してきました。

内容の振り返り

新潟グラムの会場の様子

ぼくは「今どきのWebアニメーション実装」というテーマで、大きく3つに分けてお話をしました。

  • 今はどういった実装方法があるのか
  • ツールで作成したアニメーションをWebで表示させるための連携方法
  • アニメーション作成のポイント

参加者の属性がデザイナー寄りではあったものの、ディレクターの方もいたりとバラバラだったので何を話そうかと悩みました。
とはいえ新潟のコンテンツ制作のクオリティを上げるという視点で考えたときに、これまでの経験を踏まえてエンジニア目線で話したほうが何かと価値があるのかなと考えて内容を決めました。

当日のスライドはこちらです。

登壇はやっぱり緊張しましたが、終わった後にたくさんの方から参考になったと感想をいただけて安心しました。
これからさらに精進します。

フォローアップ

時間をオーバーしてしまうほどの内容でしたが、補足をここで紹介しておきます。

各アニメーションの使いどころについて

若干説明しきれなかったので補足です。
技術については把握したけど、じゃあ具体的にどんな状況で使えばいいのか。

CSSアニメーション

単方向のアニメーションという特性上、シンプルな動作にマッチします。
ボタンのホバーアクションやアイコンのマイクロインタラクション、テキストやオブジェクトの表示アニメーションなどちょっとしたアニメーションをつけたいときによく使われています。

CSSアニメーションは管理が煩雑になりやすいので「無理をしない」が鉄則。

Spriteアニメーション

Twitterのいいねボタンのアクションを始め、「パーティクルとかちょっと凝ったアニメーションをしたいけどコードを書いて表現するのはツラい」ような場合に相性が良いです。
After Effectsでサクッとアニメーションを作って、画像を連番で書き出して1枚の画像にまとめる流れが一番効率が良いと思います。

画像を1枚にまとめる手段は、spritesmithで自動でまとめたりPhotoshopのjsxファイルなどが配布されていたりするのでググってみてください。(ここだけ投げやりで申し訳ない)

SVGアニメーション

レスポンシブデザインのサイトだったらまずSVGアニメーションを検討してみましょう。
デバイス別の調整はCSSでSVG全体のサイズ調整をしてあげればいいだけの場合もあるので、鮮明さを保ったままコストをかけずに済みます。
またアイコンなどのマイクロインタラクションはSVGで実装されていることが多いです。

あとはパスアニメーションが必要な場合はSVGに頼らざるを得ないでしょう。

JavaScript(DOMアニメーション)

HTML要素をアニメーションさせる場合に利用しますが、CSSではできない制御をしたい場合になります。
例えばランダムな動きをつけたい場合やアニメーションさせた後に何かの処理をさせたい場合など、アニメーションを細かく制御したい場合に最適です。

ライブラリによってはタイムラインを管理できるものもあるので、アニメーションの開始位置や終了位置の制御も容易になります。

Canvasアニメーション

Canvasアニメーションの使い所は簡単なアニメーションというよりは、さらに複雑なものに適しています。
Flashコンテンツの代替と言えばわかりやすいのではないでしょうか。

Canvasアニメーション

たとえば西尾維新オフィシャルサイトのオープニングアニメーションのように、複雑な線画をアニメーションさせる場合や、一度に動かすオブジェクトが沢山ある場合などにCanvasアニメーションが威力を発揮します。
パーティクルの描画などはCanvasが必ず用いられる好例ですね。
これはDOMアニメーションの場合だと、多くの要素を一度に動かすとブラウザに負荷がかかって動作が重くなってしまうためです。

また参考ですが、ブラウザゲームなどの高負荷がかかるコンテンツではCanvasが用いられています。

作り手が,少しでも自由に,より新しいものにチャレンジしやすい環境を作りたかった――Yahoo! JAPANが満を持して放つ“第3のプラットフォーム”は,HTML5を最大限活用した「ゲームプラス」

WebGL

WebGLでは3Dコンテンツはもちろん、水の波紋を表現したり、奥行きを活かした質感を表現できる技術です。
サイトで採用すればインパクト間違いなしです。

これもCanvasアニメーション同様、オープニングアニメーションやサイト全体を3D空間に見せるなど比較的大きい範囲で使う場合に適しています。
以下のサイトではWebGLを使ったサイトが紹介されているのでアイデアが欲しいときに覗いてみると参考になります。

WebGL 総本山

Q. After EffectsやAnimate CCは使っていいの? 負けじゃないの?

これはぜひ使ってください!
アニメーション制作はWeb技術だとかなり非効率ですので、ツールに頼ったほうが良いと思っています。
細かい数値の変更でブラウザリロードを繰り返すのは結構な苦痛を伴う作業です。
WebGLなど高度なことをしたい場合はツールを使えない場合もありますが、ツールを有効に使うことは確実に制作の手助けになるはずです。

懇親会

新潟グラムの懇親会の様子

イベント後の懇親会の様子です。
1年ぶりに会ったトロさんがコロッとなっててさらに可愛さが増してた。(カメラ目線でピースしてる人)

なかなか会う機会が無い方ともお会いできてとても楽しいイベントでした!
また皆さんお会いしましょう!!

上越TechMeetup #3 を4/13(土)に開催します!

$
0
0

4月13日(土)に、新潟県上越市で第3回目となる「上越TechMeetup」を開催します!

最新のIT技術に触れたい方、エンジニア同士の交流をしたい方のご参加をconnpassにて絶賛募集中です。

登壇者にはJavaScript界の大御所エンジニアも!?

今回はなんと日本のJavaScript界の大御所、サイボウズの佐藤鉄平( @teppeis )さんが来てくれるのです!

佐藤さんはJavaScriptを書いてる人だったら1度は目にしたことのある「teppeis blog」での発信や、IT雑誌WEB+DB PRESSで連載をされていたりと常に最新の情報をキャッチしている方で、ぼくが尊敬する方でもあります!
上越市出身というご縁で東京から新潟に来てくださることになり、もうテンションが上がらずにはいられない!!!

当日、佐藤さんからは「JavaScript開発の効率化」についてお話し頂く予定です。
最新のフロントエンドの開発環境について知れるチャンスでもありますよ。
Web制作をしている方にも有益な内容だと思うので、気になる人はぜひ。

そして2人目は地元からクラスメソッド株式会社の植木和樹さんが「最近の情シス」についてお話してくれます!
情シスが最近のマイブームだそうで、どんな内容になるかは当日までのお楽しみ。

3人目は妙高市ご出身のRPAテクノロジーズの藤田さんから「RPA(Robotics Process Automation)」についてお話しいただきます!
ロボットによる自動化なんて、地方ではなかなか聞けないトピックスではないでしょうか!
こちらも楽しみでしょうがありません!

その他、10分のLT枠も募集しますので、おもしろネタ、聞いて欲しい話、辛かった話などなど何でもお待ちしています!!!

イベント概要

開催日: 2019年4月13日(土)
時間: 14:00〜18:00(開場13:30)
会場: ミュゼ雪小町 5F 多目的室2(えちごトキめき鉄道妙高はねうまライン 高田駅から徒歩5分)
参加申し込み:上越TechMeetup #3 – connpass

懇親会もありますので、ぜひお申込みお待ちしてます!

WordPressブログのフロントエンドをGatsbyJS + Netlifyで構築する

$
0
0

今までWordPressでブログを運用してきたのだが、HTMLとPHPを絡めたテーマ開発が煩雑で表示速度も遅かったため、フロントエンドにGatsbyJS, Netlify, WP REST APIを導入することで改善した。

あまり時間がない中、ガガガッと移行したので忘れないように備忘録を残しておきたいと思う。

ブログのシステム構成

GatsbyJS + Netlify + WordPressのシステム構成

フロントエンドのソースコードはGithubに置いておき、masterへのプッシュをNetlifyが検知してGatsbyのソースコードを自動ビルドする。
このビルドでWordPressから記事データを引っ張ってきて静的ファイルを生成してくれる。
ビルド後はそのままNetlify上でサイトデータをホスティングという流れになる。

ブログの更新はこれまでと変わらずWordPress上ですべて管理していくことにした。
WordPressのView(テーマ)は触りたくないけど、CMSの機能はなんだかんだ柔軟だから別に変える必要ないよね、と考えた。移行も面倒くさい。
サイト表示ではViewを一切使わずにREST APIからデータを取得する、いわゆるヘッドレスCMSという位置付けで使う。

ヘッドレスCMSとは、Viewは利用せずにAPI経由でデータ出力するCMSのこと。

記事の更新時にもwebhookでNetlifyのビルドを実行するため特別な操作は不要。
ただしNetlifyでのビルドに多少時間がかかるので、記事の追加や変更の即時反映が難しいのがデメリットとしてある。(約300記事でおよそ2〜3分)

GatsbyJSとは

GatsbyJSはReact製の静的サイトジェネレータです。(以下「Gatsby」と表記する)

https://www.gatsbyjs.org/

ViewがReactで、Markdownや外部API(WordPress REST APIなど)からGraphQLで記事データを取得してHTMLとJSONを生成する仕組み。
アクセス時は静的HTMLを表示するものの、ページ遷移では生成されたJSONを読むので表示が非常に速い。注意点は、ソースコードの修正やブログの記事を更新した際は再ビルドが必要になることだ。しかし今回はNetlifyがwebhookで更新をキャッチするので心配ご無用。

ずっとWordPressのView開発が嫌で嫌でしょうがなくて、REST API直接叩いても良かったけどコストに見合わず速いわけではなかったので、パフォーマンスも伴ってお手軽にSPA化できる技術をずっと待ってた。

代替案としてNuxt.jsでも実現できるけど、Vue.jsそもそもちゃんと触ってない&イチから覚えるのも面倒だし、ブログのリニューアルにそこまでコストかけたくないということでGatsbyなのである。今はReact書くほうがはるかにラク。

Netlifyとは

2018年に人気が爆発したみんな大好き高機能ホスティングサービス。

https://www.netlify.com/

ソースコードのビルド〜デプロイ〜ホスティングまで全てが無料で使える。サイト数の制限はない。
Githubのリポジトリと紐づけてpushからの自動ビルド、そしてそのままNetlify上でホスティングできる。
さらにカスタムドメイン・SSL設定、SSR用のプリレンダリング、CDNで通信パフォーマンスが良いなどとにかく高機能なのである。

ベーシック認証などは有料にしないと使えないところもあるけど、個人ブログ程度なら無料枠で十分賄えるのでNetlifyを選択した。

あとWordPressが面倒な人にはNetlify CMSが使える。管理画面操作の裏でGitリポジトリへcommit・pushを自動化してCMSの様に振る舞ってくれる。

https://www.netlifycms.org/

WordPressからデータを取得する準備

GatsbyJS単体ではWordPressからデータを取ってこれないので、いろいろ解決できるgatsby-source-wordpressを使う。

gatsby-source-wordpress

特徴は以下。

  •  All entities are supported (posts, pages, tags, categories, media, types, users, statuses, taxonomies, site metadata, …)
  •  Any new entity should be pulled as long as the IDs are correct.
  •  ACF Entities (Advanced Custom Fields)
  •  Custom post types (any type you could have declared using WordPress’functions.php)

記事、固定ページ、タグ、カテゴリ、メディア、ユーザー、ステータス、タクソノミー、サイトのメタ情報などが取得できる。
カスタムフィールドプラグインのACFにも対応しているので、バックエンド連携としては必要十分だと思う。

そして、これらを含んだ公式テンプレートを使用することにした。

gatsby-starter-wordpress

DEMO: https://gatsby-starter-wordpress.netlify.com/

導入方法は以下。

// はじめにGatsbyを操作するための gatsby-cli をインストール
$ npm i -g gatsby-cli

// gatsbyコマンドが使えるようになる
$ gatsby -v
-&gt; 2.4.17

// starterを使ってサイトデータの初期化
$ gatsby new PROJECT_NAME https://github.com/GatsbyCentral/gatsby-starter-wordpress

※PROJECT_NAMEは任意

できたディレクトリの中で yarn startを叩けば http://localhost:8000 が立ち上がる。簡単だ。

$ cd PROJECT_NAME
$ yarn start

WordPressとの紐付けは gatsby-config.js で行う。

plugins: [
  ...
  {
    resolve: 'gatsby-source-wordpress',
    options: {
      // The base url to your WP site.
      baseUrl: 'wp.yuhiisk.com', // WordPressのドメインを指定
      // WP.com sites set to true, WP.org set to false
      hostingWPCOM: false,
      // The protocol. This can be http or https.
      protocol: 'https',
      // Use 'Advanced Custom Fields' Wordpress plugin
      useACF: false,
      auth: {},
      // Set to true to debug endpoints on 'gatsby build'
      verboseOutput: false,
    },
  },

一旦は baseUrlprotocolを設定すればOK。
もう一度 yarn startし直すと自分のブログデータが入るはず。

あとはReactでガシガシViewを作っていく。
gatsby-starter-wordpressにはCSSフレームワークのbulmaも一緒に入ってて、今回はそれに乗っかった。

GraphQLを試す

GatsbyではGraphQLでデータを引っ張ってくる。

GraphQLはクエリ言語と呼ばれるWeb APIのための規格で、オブジェクト構文に似た記述でクエリを書くとAPI経由でデータを引ける。詳しくは割愛。

https://graphql.org/

yarn startしてローカルサーバーが立ち上がるとGraphQLのGUIクライアントも一緒に立ち上がり、クエリの実行を試せるようになる。WordPressのデータを取得できているかはここで一度試すと開発が捗る。

http://localhost:8000/___graphql

GraphQL editor

query内では、以下でデータを取得する。

  • allWordpressPostで全投稿
  • allWodpressPageで全固定ページ
  • wordpressPost で個別投稿
  • wordpressPageで個別固定ページ

Gatsbyのコンポーネントにすでに書かれているので一度みれば雰囲気は掴める。他にもフィルターできたりいろいろ機能がある。

https://www.gatsbyjs.org/packages/gatsby-source-wordpress/#how-to-query

ビルド・デプロイ設定

そしてサイトのビルドとデプロイ設定を解説する。
大きく分けて次の3ステップとなる。

  1. Netlifyの設定
  2. WordPressの設定
  3. Githubへのプッシュ、もしくは記事の作成・更新によるデプロイ手順

Netlify上でのビルド & ホスティング

今回はGithubを使ったので、Githubアカウントがある前提で進める。

はじめにNetlifyでアカウントを作成。

https://www.netlify.com/

あとはGithubでフロントエンドソース用のリポジトリを作っておき、Netlifyでリポジトリを指定する。ビルド設定はそのままで良い。

GithubからのDeploy設定

Settings > Build & Deploy > Build Hooks でWebhook URLを生成しておく。

Deploy & BuildでBuildHook URLを生成

参考:Gatsby + Netlify + WordPressでHeadless CMS化してみた。

WordPressの更新をフックする

同一ドメインで置き換えたい場合は別のURLでWordPressにアクセスできるようにしておく。

なぜ複数のドメイン設定をしておくかというと、既存のWordPressに向いているドメインをNetlifyに設定するのだが、URLがひとつだけだとWordPressの管理画面にアクセスできなくなるし、後から設定では遅いから。新規で用意する場合は複数のドメイン設定は必要ない。

設定できたらWordPressにJAMstack Deploymentsをインストールする。

wp-jamstack-deployments

JAMstack Deploymentsは何かと言うと、記事や固定ページの更新タイミングで、Netlifyのビルドを実行できるプラグインだ。
このプラグインのおかげでWordPressから自動デプロイが可能になる。

プラグインをzipでDL/インストールして有効化すると、WordPress 設定 > Deployments から設定できる。
Webhook URLには先ほどNetlifyで生成したWebhook URLをPOSTで設定。
タイプは投稿と固定ページ、コンタクトフォーム(wpcf7_contact_form)にしておいた。
※見づらいけど一番下のSave Settingsを押さないと反映されない。

これで作成・更新・削除のタイミングでWebhookが飛ぶ。

そして gatsby-config.jsなどで指定していた、フロントエンド側のWordPressのURLを書き換えておく。

Githubへのプッシュ、もしくは記事の作成・更新によるデプロイ

あとはmasterへプッシュするかWordPressで記事を更新すれば、Netlify側で自動でビルドが走る。

Deployの進行中

ビルド・デプロイ完了後は自動的にNetlify上にホスティング、公開される。

システムの本番化

本番化はドメインを変更するだけ。

Netlifyのカスタムドメイン設定は以下の記事が参考になった。

参考:【Netlify】カスタムドメインを設定する

そしてこのままだとWordPress内のURLが古いので、phpmyadminから全て新しいURLへ置換しておく。

UPDATE `wp_posts` SET guid=REPLACE (guid,'https://blog.yuhiisk.com','https://wp.yuhiisk.com')
UPDATE `wp_posts` SET post_content=REPLACE (post_content,'https://blog.yuhiisk.com','https://wp.yuhiisk.com');
UPDATE `wp_options` SET option_value=REPLACE (option_value,'https://blog.yuhiisk.com','https://wp.yuhiisk.com');
UPDATE `wp_postmeta` SET meta_value=REPLACE (meta_value,'https://blog.yuhiisk.com','https://wp.yuhiisk.com');
UPDATE `wp_posts` SET post_title=REPLACE (post_title,'https://blog.yuhiisk.com','https://wp.yuhiisk.com');

この置換手順の場合、本番化〜置換までの間は記事内のリンクが繋がっていなかったり画像が表示されない時間が発生する。これが嫌な場合は別途新規のWordPressサーバーを用意しておくと良い。

最後にビルドして完了。
これからGatsby + Netlify + WordPressでのブログ新生活がスタートする。

ケーススタディ

TypeScriptを使いたい

TypeScript本体とGatsbyのプラグインを追加する。

$ yarn add gatsby-plugin-typescript typescript

gatsby-config.jsの pluginsgatsby-plugin-typescriptを追加でOK。

plugins: [
  ...'gatsby-plugin-typescript’
]

src/templates内のファイルをtsにする場合、gatsby-node.js内で参照するファイル名も変更しておく。

GraphQLでエラーになる

GraphQL Error There was an error while compiling your site's GraphQL queries.
  Invariant Violation: GraphQLCompilerContext: Duplicate document named `IndexQuery`. GraphQL fragments and roots must have unique names.
    t: Duplicate document named `IndexQuery`. GraphQL fragments and roots must have unique names.

export const pageQuery = graphql`
  query IndexQuery($limit: Int!, $skip: Int!) {
    allWordpressPost(
      sort: { fields: date, order: DESC }
      limit: $limit
      skip: $skip
    ) {
      edges {
        node {
          ...PostListFields
        }
      }
    }
  }
`

IndexQueryと重複して名前をつけていた場合に発生した。プロジェクトを通してユニークな命名にする。

WordPressで設定したメニューを取得したい

WordPressにWP API Menusを入れるとREST APIからMenuが取れる。

"/wp-json/wp-api-menus/v2","/wp-json/wp-api-menus/v2/menu-locations","/wp-json/wp-api-menus/v2/menu-locations/(?P<location>[a-zA-Z0-9_-]+)","/wp-json/wp-api-menus/v2/menus","/wp-json/wp-api-menus/v2/menus/(?P<id>\\d+)",

GraphQLのクエリはこんな感じ。

query {  
  wordpressWpApiMenusMenusItems {
    name
    items {
      title
      url
    }
  }
}

WordPress Menus GraphQL Query

Twitterウィジェット表示したい

gatsby-plugin-twitterを入れる。

https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-twitter

$ yarn add gatsby-plugin-twitter

gatsby-config.jsの pluginsに追加して完了。

plugins: [
  // ・・・
  "gatsby-plugin-twitter”, // 追加
}

あとはTwitterウィジェットを自動的に見つけて処理してくれる。

前後の記事を取得したい

Markdownだとfrontmatterで前後の記事を取れるようだが、WordPressだとGraphQLで工夫が必要。

そこでallWordpressPostからpreviousとnextを取る。

{
  allWordpressPost {
    edges {
      previous {
        title
        content
        slug
      }
      next {
        title
        content
        slug
      }
      node {
        ...
      }
    }
  }
}

上記からgatsby-node.js内のallWordpressPostを書き換える。

.then(() => {
  return graphql(`
    {
      allWordpressPost {
        edges {
          next {
            slug
            title
            date
          }
          previous {
            slug
            title
            date
          }
          node {
            id
            slug
            status
            date
          }
        }
      }
    }
  `)
})

取得したデータをcontextに渡す。

_.each(posts, ({ node: post, next, previous }) => {
  // Create the Gatsby page for this WordPress post
  createPage({
    path: `/archives/${post.slug}/`,
    component: postTemplate,
    context: {
      id: post.id,
      prev: previous,
      next
    },
  })
})

このcontextのデータは templates/post.js#Postprops.pageContextに渡ってくる。

Disqusを導入する

disqus-react を使用する。

$ yarn add disqus-react

render() {
  ...
  const disqusShortname = "yourdisqusshortname";
  const disqusConfig = {
    identifier: post.id,
    title: post.frontmatter.title,
  };

  return (
    <div>
      {/* 任意の場所で埋め込み */}
      <DiscussionEmbed shortname={disqusShortname} config={disqusConfig} /></div>
  );
}

参考:Add Disqus comments to a Gatsby blog

Webフォントを利用する

gatsby-plugin-web-font-loaderでWebフォントを設定する。

$ yarn add gatsby-plugin-web-font-loader

gatsby-config.jsにプラグイン設定を追加。

plugins: [
  ...
  {
    resolve: 'gatsby-plugin-web-font-loader',
    options: {
      google: {
        families: ['Droid Sans', 'Droid Serif']
      }
    }
  }
]

オプションの指定は以下URLを参照してほしい。

https://github.com/typekit/webfontloader

Contact Form 7で作成したフォームはどうすればいい?

結論から言うと、REST APIにエンドポイントがあるのでAjaxでフォーム送信できる。

まずエンドポイントの確認のためにHomebrewで jqを入れる。これは必須ではない。

$ brew install jq

参考:https://wp-kyoto.net/check-wp-api-custom-endpoint-by-jq

するとcurlで以下のように /wp-json のエンドポイントが一覧表示できる。URLは自分のWordPressサイトを指定する。

$ curl https://wp.yuhiisk.com/wp-json | jq .routes | jq keys

[
  "/","/akismet/v1","/akismet/v1/alert","/akismet/v1/key","/akismet/v1/settings","/akismet/v1/stats","/akismet/v1/stats/(?P<interval>[\\w+])","/contact-form-7/v1","/contact-form-7/v1/contact-forms","/contact-form-7/v1/contact-forms/(?P<id>\\d+)","/contact-form-7/v1/contact-forms/(?P<id>\\d+)/feedback","/contact-form-7/v1/contact-forms/(?P<id>\\d+)/refill",
  ...

POST先は /wp-json/contact-form-7/v1/contact-forms/:id/feedbackになる。

URLのパラメータid はContact Form 7で作成したフォームの記事IDで、フォーム内の input[name="_wpcf7"]に入っているのでそれを拝借する。

そしてリクエストヘッダーのContent-typeを application/jsonで、formdataをポーンと送るだけ。
レスポンスが正常なら完了UIを表示すれば良い。

フォーム側でrecaptcha設定してるとspam判定でレスポンスが返ってきて失敗するので一旦オフっておく。recaptcha対応は追記予定。

useEffectがエラーになる

gatsbyのreact-hot-loaderのバージョンが古いことが原因だったので、gatsby本体をアップデートした。

参考:Uncaught TypeError: Cannot read property ‘expirationTime’ of undefined

その後はGraphQLでフィルターを使うクエリを修正する必要があった。(gatsby v2.0.7 → v2.3.2)
filter内に elemMatchが必要。

// src/templates/category.js
// allWordpressPostのfilter->category->elemMatchを足す。

export const pageQuery = graphql`
  query CategoryPage($slug: String!) {
    site {
      siteMetadata {
        title
      }
    }
    allWordpressPost(filter: { categories: { elemMatch: { slug: { eq: $slug } } } }) {
      totalCount
      edges {
        node {
          ...PostListFields
        }
      }
    }
  }
`

Analyticsの設定どうする?

gatsby-plugin-google-analyticsを入れる。

$ yarn add gatsby-plugin-google-analytics

plugins: [
  ...
  {
    resolve: `gatsby-plugin-google-analytics`,
    options: {
      // replace "UA-XXXXXXXXX-X" with your own Tracking ID
      trackingId: "UA-XXXXXXXXX-X",
    },
  },
],

参考:Adding analytics

Adsenseの設定どうする?

プラグインは無いので react-adsenseを使って設定する。

まずhtml.jsをキャッシュディレクトリからsrcにコピーする。

// プロジェクトルートで

$ cp .cache/default-html.js src/html.js

src/html.jsを用意すると、html全体をレンダリングするRoot Componentを上書きできる。

Customizing html.js

このファイルの中でadsenseの読み込みを追加。

<head><meta charSet="utf-8" /><meta httpEquiv="x-ua-compatible" content="ie=edge" /><meta
    name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no"
  /><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" /> // 追加
  <script dangerouslySetInnerHTML={{ __html: "(adsbygoogle = window.adsbygoogle || []).push({ google_ad_client: \"ca-pub-****************\", enable_page_level_ads: true });" }} /> // 追加
  {props.headComponents}
</head>

あとは適当に埋め込み用のAdsenseコンポーネントを作る。

import * as React from 'react';
import AdSense from 'react-adsense';

type Props = {
  client: string;
  slot: string;
  format: string;
};

const Adsense = ({ client, slot, format }: Props) => (<div><AdSense.Google
      client={client}
      slot={slot}
      format={format}
    /></div>
);

export default Adsense;

これを任意の場所で使えば良い。

<Adsense client="ca-pub-****************" slot="**********" format="auto" />

参考:GatsbyJSにGoogle AdSenseを導入する

ページのlocationを取得したい

Props.locationに入っているので、詳しくはgatsby-linkを参照してほしい。

Gatsby Link

SSRのカスタマイズどうする?

基本は不要だけど、プロジェクトルートに gatsby-ssr.jsを用意して設定可能。

Gatsby Server Rendering APIs

OGP対応

基本は react-helmetでmetaタグを設定する。

設定方法は公式で紹介されている。
Adding an SEO component

自分は以下のソースを参考にした。
https://github.com/jlengstorf/marisamorby.com/tree/master/src/components/SEO

react-helmetのAPIは目を通しておくと良い。
react-helmet

あとメタ情報を取得するためのStatic Query実行の際は useStaticQuery カスタムフックが使える。(Gatsby v2.1.0~, react v16.8~, react-dom v16.8~ が条件)
Querying data in components with the useStaticQuery hook

window参照できない問題

ローカルでは問題ないけど、Netlifyでビルドするとwindowオブジェクトがないと怒られる。

解決策は、Reactの componentDidMountuseEffectの中で参照する。

Netlifyのビルドエラー

GraphQLでデータ取得に失敗した場合のエラー。

GraphQLError: Cannot query field "allWordpressPage" on type "Query". Did you mean "allSitePage" or "allWordpressAcfOptions"?

REST APIにアクセス出来ない場合に発生する。自分の場合は国外からのREST APIアクセスを制限していたためGraphQLが弾かれていた。

一応URLが間違っていないかも確認しておいた方が良い。

Build時にstyled-componentsのスタイルが当たらない

Gatsby プラグインを入れることで回避した。
gatsby-plugin-styled-components

plugins: [
  ...
  {
    resolve: `gatsby-plugin-styled-components`,
    options: {
      // Add any options here
    },
  },
],

記事の日付が1日前になってしまう

日付の整形処理は実行環境のTimezoneに依存する。
NetlifyのNode.js環境はUTCになるので、特に指定しなければ-9時間される。

moment-timezonedate-fns-timezone を入れる。

$ yarn add moment-timezone

or

$ yarn add date-fns-timezone

// moment-timezone

const moment = require('moment-timezone');

const timeZone = 'Asia/Tokyo';
const date = moment(post.date).tz(timeZone).format('YYYY/MM/DD');

// date-fns-timezone
const { formatToTimeZone } = require('date-fns-timezone');

const timeZone = 'Asia/Tokyo';
const date = formatToTimeZone(post.date, 'YYYY/MM/DD', { timeZone });

記事タイトルが文字化けしたら

HTML Entitiesをパースする。

const unescape = (str: string): string => {
  return str.replace(/&#(\d+);/g, (match, dec) => {
    return String.fromCharCode(dec);
  });
};

unescape(post.title);

カテゴリの親子階層が認識されない

小カテゴリを廃止して1階層のみのフラット構造にする。

bulmaのスタイルが当たらないんだけど

利用していないCSSはgatsby-plugin-purgecssでビルドの際に除去されるが、一方でWordPressの記事データ内のHTMLクラス名までは解析されない。
そのためダミーのコンポーネントを用意して、その中で当てたいクラス名をつけたコンポーネントを定義しておく。

const Features = () => (<div><textarea className="textarea" name="" id="" cols="30" rows="10"></textarea><button className="button is-link">submit</button></div>
);

export default Features;

Gistが埋め込めない&WordPressサイトのメタ情報欲しい

後日追記予定です。

以上。

上越TechMeetup #3 を開催しました

$
0
0

4/13(土)に新潟県上越市で「上越TechMeetup #3」を開催しました。

今回は市外からイベントに参加される方々に、上越の桜の名所である高田公園にぜひとも足を運んでもらいたいと思い、桜が咲く時期に開催しました。

当日は見事に満開の桜になり、天気にも恵まれ最高のお花見日和。
参加者の方には午前中や、泊まりがけで翌日も桜を楽しんでいただけたようでした。

そんな絶好のお花見日和に開催した、上越TechMeetup#3のイベントレポートをお届けしたいと思います。

※当日の模様はYoutubeでご覧いただけます。

@teppeisさんが上越TechMeetupにやってきた

@teppeisさん

上越TechMeetupでどうしてもやりたいことのひとつに、サイボウズの@teppeisさんを呼びたいという願望がありました。
地元出身者が故郷で登壇するなんて、めちゃくちゃ素敵なことだと思いませんか?

ことの経緯は2月にTDDBC長岡に参加した時に、懇親会で「上越TechMeetupやってます!@teppeisさん呼びたいです!」と紹介したのがきっかけで、@t_wadaさんから「誘えば来てくれると思うよ!」と助言いただき、光の速さでオファーさせてもらったら快く引き受けてもらえました。

@t_wadaさん、TDDBC長岡を開催してくださった@masaru_b_cl さんにはとても感謝しております!

上越TechMeetup#3

というわけで、@teppeisさんからはJavaScriptのこれまでの歴史から最新情報についてお話し頂きました。
JavaScriptエンジニアとして仕事をしている自分でも知らない知識があって、ざっくり説明されてたように見えてとても実用的な話題が満載。

個人的にこれはと思った内容は、「**」の演算子で階乗を計算できたり、Optional catchなんて仕組みがあったなんて。。
お願いしたときに本人は一般層向けに話すと仰っていたんですけど、地方の勉強会ではなかなか聞くことができない素晴らしい内容でした。圧倒的感謝。

「働きやすい環境を作る情シス」の話に心打たれる

クラスメソッド株式会社植木さん

二人目に登場したのは、毎度おなじみクラスメソッドの植木さん。
情シスって何する仕事?、情シスの悩み、クラウド時代の情シス像とはなんぞや?という内容でお話し頂きました。

情シスって聞くと裏方でゴニョゴニョする部署でなんだかよくわからないっていうのがイメージだったけど、会社にある電気で動くもの全てを管理するようなめちゃくちゃ幅広い仕事なことがわかりました。
そして扱うトピックも多岐に渡るため、各技術知識を網羅的に知ってないと務まらない。
一方で直接的に利益を生む部署ではなく、比較的立場が低いという話を聞いて心苦しい気持ちになりました。縁の下の力持ちなのになぁ。

植木さんいわく、情シスが会社で働きやすい環境を整える立ち位置になれば、優秀なエンジニアが集まるとのこと。めちゃくちゃキラキラしたカッコいい話でした。
自分はフリーランスで、言ってみれば一人情シス状態。コードを書く以外にも環境づくりの面では参考にしたいとつくづく思いました。

バズワード化している「RPA」とは

RPAテクノロジーズ藤田さん

3人目はRPAテクノロジー藤田さんからRPAについて話して頂きました。藤田さんは妙高市(旧新井市)出身。
後日アンケート結果を集計したところ、一番注目のあったセッションです。
実は今回の上越TechMeetupで藤田さんに登壇のオファーをするまで、お恥ずかしながら「RPA」って言葉を知らなかった。。

RPAを紹介しておくと、「Robotic Process Automation」の略で、ロボットによる自動化を表す言葉です。
ロボットという単語を使うけど実体はソフトウェアで、繰り返し処理などをプログラムに任せて業務を効率化しようという取り組み。
簡単に言うとエクセルのマクロのようなシステムと考えてもらえればいいと思います。

RPAの詳細はこちらもどうぞ。
RPA(ロボットによる業務自動化)とは | RPA テクノロジーズ株式会社「BizRobo!(ビズロボ)」

自分でサービス運営していると、これって人がやる必要ないよな、、とストレスに思うことがすごく多くて、RPAを活用して作業を自動化したい要望はめちゃくちゃ多いんだろうなと思います。
個人で思うくらいだから、企業でのRPA活用は効果絶大なのでしょう。領域外だったけどとても面白いセッションでした。

まとめ

今回もジャンルに捕らわれない技術の話を聞けた面白い内容でした。

上越に住んでいる、もしくは上越出身者をスピーカーにお招きして上越の価値を知ってもらうというのがこの勉強会コンセプトのひとつでもあるけれど、上越・妙高出身のこんなに素晴らしい方々が参加してくれるとは夢にも思わなかったです。
ご協力頂いた植木さんには感謝してもしきれない。

ただそろそろお招きできる人材に限界を感じているので、次回からはしっかりと登壇テーマを設けようと思います。
もっと実践的な話も増やしたいです。

そして次回は7月20日に開催です!内容は決まり次第またアナウンスします。
今年は3ヶ月ごとの開催で、なんと秋には10月19日に越後謙信SAKEまつりの開催に合わせて予定しています。(日本酒を飲みながら勉強会をしましょうね)

ではまた次回お会いしましょう!

SSHで「WARNING: UNPROTECTED PRIVATE KEY FILE! 」が表示される場合

$
0
0

GithubにSSHで繋ごうと思ったら、次のようなエラーが発生した。

$ ssh -T git@github.com 
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/Users/*****/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/Users/*****/.ssh/id_rsa": bad permissions
git@github.com: Permission denied (publickey).

現在のid_rsaのファイル権限が 0644 でオープンすぎるということらしい。おそらく鍵ファイルをコピペしたりしてしまったためだと思われる。

なのでファイル権限を 0600 に変更すればOK。(所有者以外の操作を拒否する)

Mac

$ chmod 0600 ~/.ssh/id_rsa

AngularのテスティングフレームワークにJestを利用する

$
0
0

AngularのテスティングフレームワークはJasmine + Karmaがデフォルトで用意されている。便利な半面実行が遅く、効率よくフィードバックを得ながらテストを書くにはリズムが悪い。

解決策として、世界で今最も支持されているであろうJestを使用すれば、Angularのユニットテストの実行は格段に早くなる。

この記事ではAngularアプリケーションにJestを適用する方法を紹介したいと思う。

Jestとは?

JestはFacebookが開発しているJavaScriptのテスティングフレームワーク。

Jest · 🃏快適なJavaScriptのテスト

ユニットテストの実行速度が早いことに加えて、スナップショットテストや強力なモック機能など使い勝手が良く、最近では一番使われているテスティングフレームワークです。

Jasmineと同じく expect(a).toBe(true)といったBDDスタイルのアサーションなので、導入ハードルは比較的低い。

Jestへの変更

Angular CLIでテスティングフレームワークをJestに置き換えるnpmパッケージ(Angular Schematics)が公開されているので、これを使う。

https://github.com/briebug/jest-schematic

はじめにnpmパッケージをグローバルにインストールする。

$ npm install -g @briebug/jest-schematic

次にAngularプロジェクトにターミナルで移動して以下のコマンドを実行。

$ ng g @briebug/jest-schematic:add

実行すると不要なファイルは削除され、Jestなどの必要なパッケージのインストールと以下のファイルが生成される。

  • jest.config.js
  • src/setup-jest.ts
  • src/test-config.helper.ts

次に関連ファイルの修正を行う。

src/tsconfig.spec.json

types"jasmine"から "jest"に変更する

{"compilerOptions": {"outDir": "../out-tsc/spec","types": ["jest", // jasmine -> jest に変更
      "node"
    ],"module": "commonjs"
  },
}

src/setup-jest.ts

2行目以降を削除して以下に書き換える。

import 'jest-preset-angular';
import './jest-global-mocks';

src/jest-global-mocks.tsを新規作成して以下の内容を記述する。

/* global mocks for jsdom */
const mock = () => {
  let storage: { [key: string]: string } = {};
  return {
    getItem: (key: string) => (key in storage ? storage[key] : null),
    setItem: (key: string, value: string) => (storage[key] = value || ''),
    removeItem: (key: string) => delete storage[key],
    clear: () => (storage = {})
  };
};

Object.defineProperty(window, 'localStorage', { value: mock() });
Object.defineProperty(window, 'sessionStorage', { value: mock() });
Object.defineProperty(window, 'getComputedStyle', {
  value: () => ['-webkit-appearance'],
});

Object.defineProperty(window, 'CSS', {value: null});
Object.defineProperty(document, 'doctype', {
  value: '<!DOCTYPE html>'
});
Object.defineProperty(window, 'getComputedStyle', {
  value: () => {
    return {
      display: 'none',
      appearance: ['-webkit-appearance']
    };
  }
});
/**
 * ISSUE: https://github.com/angular/material2/issues/7101
 * Workaround for JSDOM missing transform property
 */
Object.defineProperty(document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});

/* output shorter and more meaningful Zone error stack traces */
// Error.stackTraceLimit = 2;

ここではMock用の設定をまとめている。

合わせて @types/jest もインストールしておく。

$ npm i -D @types/jest

基本はこれで完了。

テストの実行

テストの実行はコマンドが書き換えられているので、 npm test を実行すればいい。

$ npm test

また上記は単発の実行で、 npm run test:watchでwatchモードで実行できる。自分は package.json を書き換えて npm testでwatchするようにしている。

{"scripts": {
    ..."test": "jest --watch",
    ...
  },
}

テストを走らせる場合は大体watchしながら走らせるので、test:watchのほうは消してしまっていいと思う。

Jestの設定

jest-schematicの中では以下のJest presetを利用してJestの設定を行っているため、目を通しておくと良い。

https://github.com/thymikee/jest-preset-angular

Cloud Functions + TypeScriptでデプロイ時に親階層のnode_modulesが参照されてしまう

$
0
0

Cloud FuntionsをTypeScriptで書いているプロジェクトで firebase deploy --only functionsを実行した際に、親ディレクトリの node_modulesが参照されてしまいTypeScriptのコンパイルエラーに悩まされた。

この現象が起こった時のfirebase-toolsのバージョンは v6.9.1

$ firebase deploy --only functions

=== Deploying to ’some-project-name'...


i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint> functions@ lint /Users/****/path/to/functions> tslint --project tsconfig.json

Running command: npm --prefix "$RESOURCE_DIR" run build> functions@ build /Users/****/path/to/functions> tsc


../node_modules/@babel/parser/typings/babel-parser.d.ts(11,70): error TS1144: '{' or ';' expected.
../node_modules/@babel/parser/typings/babel-parser.d.ts(16,80): error TS1144: '{' or ';' expected.
../node_modules/@types/jest/index.d.ts(384,32): error TS1005: ';' expected.
../node_modules/@types/jest/index.d.ts(384,33): error TS1003: Identifier expected.
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! functions@ build: tsc
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the functions@ build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.


npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/yuhiisk/.npm/_logs/2019-05-09T12_47_39_299Z-debug.log


Error: functions predeploy error: Command terminated with non-zero exit code2

エラーコードには ../node_modules/@babel/parser/typings...と親ディレクトリのnode_modulesが参照されてしまっている。

解決方法

Cloud Functionsを格納しているディレクトリ(大体が functionsだと思う)のtsconfig.json内のcompilerOptionsに typeRootsプロパティを追加して@typesの場所を明示する。

{"compilerOptions": {"lib": ["es6"],"module": "commonjs","noImplicitReturns": true,"outDir": "lib","sourceMap": true,"target": "es6","typeRoots": ["./functions/node_modules/@types" // ← functions内のnode_modules/@typesを指定する
    ]
  },
  "compileOnSave": true,"files": ["node_modules/typescript/lib/lib.es6.d.ts"
  ],"include": ["src"
  ],"exclude": ["node_modules"
  ]
}

参考:Functions only deploy erroring, referencing parent application node_modules folder

上記のIssueではcompilerOptionsの skipLibCheckをtrueにする方法も提示されているけど、型定義ファイルのエラーに対しての処置としては本質的ではないので typeRoots を指定するほうが良い。


AngularのService(サービス)の役割

$
0
0

AngularにはService(サービス)という仕組みがあります。

その実体は単一のクラスです。

@Injectable({
  providedIn: 'root'
})
export class HeroService {
  private heroesUrl = 'api/heroes';

  constructor(
    private http: HttpClient,
    private messageService: MessageService
  ) {}

  getHeroes(): Observable<Hero[]> {
    ...
  }

  getHero(id: number): Observable<Hero> {
    ...
  }
}

Injectableデコレータを利用してクラス定義し、Angularアプリケーションに登録することでコンポーネントやサービスで利用可能になります。

なぜサービスが必要なのか?

どうしてAngularにはサービスが必要なのでしょうか?

本来コンポーネントはデータの受け渡しのみに集中するべきです。複雑なデータ整形処理、コンポーネントで重複したAPI通信、エラーハンドリングなど、何も考えずにコンポーネントに追加していっては次第に実装コードは肥大化し、それにつれて管理コストは上がり、コードの見通しも悪いため変更に時間がかかるようになってしまいます。

そこでサービスの出番です。コンポーネントにあるビジネスロジックをサービスに切り出すことで、関心事を分離でき、それに加えコンポーネント間の共通処理をまとめることができます。
サービスのおかげでアプリケーションの秩序が保たれるのです。

またサービスはクラスなので、自身にデータを保持することができます。HttpClientを使用した際のエラーメッセージを保持する際などに、コンポーネントではなくサービスにメッセージを持たせればどこからでも参照できて便利です。

サービスの使いかた

冒頭でも触れたとおり、サービスはAngularアプリに登録しなければ利用できませんが、あまり気にすることはありません。

Angular CLIで生成したサービスなら、Injectableデコレータ内で providedIn: 'root'が指定されているため自動的にAngularアプリ全体で利用することができます。

@Injectable({
  providedIn: 'root'
})
export class HeroService { }

providedIn: ‘root’ を指定しない方法

providedIn: 'root'の指定を無くして、モジュールやコンポーネントの中だけに利用範囲を限定させる方法もあります。

モジュールで利用する場合

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
  ],
  providers: [
    HeroService <-
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

コンポーネントで利用する場合

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css'],
  providers: [HeroService] <-
})
export class HeroDetailComponent implements OnInit { }

Providers配列にサービスを登録します。遅延読み込み(LazyLoading)を利用する際に便利です。

このときの注意点は、サービスはAngularアプリ内ではシングルトンであるべきだということを覚えておいてください。同じサービスを別々に有効にしてしまうと、コンポーネントからの参照先が一致せず意図しない動作になる可能性が高いため絶対に避けてください。

一般的に推奨されているのは providedIn: 'root'を指定する方法です。
難しいことが苦手な方は、基本的に providedIn: 'root'を指定しておきましょう。

AngularのLifecycle Hooksを理解する

$
0
0

Angularのコンポーネントやディレクティブには Lifecycle Hooks(ライフサイクルフック)という仕組みがあり、コンポーネントの変化にあわせてコールバックを設定できます。これによりコンポーネントのデータやビューの変更時の処理を追加できます。
コンポーネント志向のAngularアプリケーションの構築では、Lifecycle Hooks の理解が不可欠です。

今回は Lifecycle Hooks の基本的な仕組みを紹介します。

Lifecycle Hooksとは

記事の冒頭でも説明したように、Lifecycle Hooks はコンポーネントやディレクティブが変化(作成・変更・破棄)するタイミングで実行されるコールバックメソッドの総称です。

例えば、次のようなケースで利用します。

  • コンポーネントの初期化時にHttpクライアントでデータを取得する。
  • 親コンポーネントの初期化時に、子コンポーネントのDOM要素を取得する。
  • コンポーネントの @Input()で受け取るデータ内容を検証する。
  • 設定したイベントリスナーをコンポーネントが破棄される際に削除する。

Lifecycle Hooksを設定すると、コンポーネント自身が変更を検出したときに自動でコールバックが実行されるため、自前で実行タイミングを設定する手間が省けます。またコンポーネント実装の一貫性を保つことにも繋がります。

Lifecycle Hooksの使い方

ここではコンポーネントの初期化時に実行される ngOnInitメソッドを見てみましょう。
ngOnInitメソッドは、Angular CLIの ng generate componentコマンドでコンポーネントを作成するとはじめから適用されています。

import { Component, OnInit } from '@angular/core'; // ①
import { LoggerService } from './logger.service';

@Component({
  ...
})
export class PeekABooComponent implements OnInit { // ②

  constructor(private logger: LoggerService) { }

  ngOnInit() { // ③
    this.log(`OnInit`);
  }

  private log(msg:string){
    this.logger.log(`${nextId++} ${msg}`);
  }
}
  1. @angular/coreから OnInitインターフェイスをインポートしています。
  2. コンポーネントのクラスに、OnInitインターフェースを実装します。
  3. OnInitインターフェースを実装すると、ngOnInitメソッドの定義が必要になります。

Lifecycle Hooks は各メソッドを実装するためのインターフェイスが必要です。
インターフェイスの適用後はインターフェイス名の接頭辞に ng を付けた ngXXXX メソッドが定義できます。例えば OnInit インターフェイスだと ngOnInit メソッドとなります。

インターフェイスを適用しなくてもメソッドを定義できますが、Angularスタイルガイドのルールではインターフェイスを必ず指定することになっていますので、忘れずに適用してください。

Lifecycle Hooksの種類

メソッド名目的と実行タイミング
ngOnInit1回目の @Input()でデータバインドされた入力値を初期化後に一度だけ実行します。

Httpクライアントによるデータ取得などを含む、コンポーネントの初期化に関する処理を行います。
ngOnChanges@Input()でデータバインドされた入力値を設定・リセットする際に実行します。この処理は入力値が変更するたびに実行され、現在と過去のプロパティを保持した変更オブジェクト(SimpleChanges)を引数で受け取ります。
ngOnInitメソッドの前に1回実行され、その後は @Inputでデータを受け取る度に実行されます。

受け取ったデータ内容を検証する目的で利用します。
ngDoCheckデータの変更を検出するたびに実行します。
ngAfterContentInitコンポーネントまたはディレクティブのあるビューに、ng-contentでコンテンツが挿入された後に実行します。
ngAfterContentCheckedコンポーネントまたはディレクティブに、ng-contentで挿入されたコンテンツの変更を検知した際に実行します。
ngAfterViewInitコンポーネントのビューとその中の子ビュー、またはディレクティブを含むビューを生成した後に実行します。

ビューに存在するDOMを取得したり、子コンポーネントを参照する際に利用します。
ngAfterViewCheckedコンポーネントのビューとその中の子ビュー、またはディレクティブを含むビューの変更を検知した後に実行します。

ビューに存在するDOMや子コンポーネントの変更を監視する際に利用します。
ngOnDestroyコンポーネントまたはディレクティブを破棄する直前に実行します。

RxJSのSubscriptionをunsubscribeしたり、element.removeEventListenerを実行するために利用します。

Lifecycle Hooksの実行順序

Lifecycle Hooksは次の順番で実行されます。

  1. ngOnChanges
  2. ngOnInit
  3. ngDoCheck
  4. ngAfterContentInit
  5. ngAfterContentChecked
  6. ngAfterViewInit
  7. ngAfterViewChecked
  8. ngOnDestroy

※その他に ngOnChanges はデータを受け取る度、ngDoCheck は ngOnChages の後にも実行されます。

Lifecycle Hooksのライブデモ

以下のライブデモでLifecycle Hooksの動きを試せます。

https://stackblitz.com/angular/enapoxavrmn?embed=1&file=src/app/app.component.html

一番上のPeek-A-Booセクションで、「Create PeekABooComponent」「Update Hero」「Destroy PeekABooComponent」を順にクリックすると Lifecycle Hooksの実行ログを確認できます。
コードと実行結果を見比べてもらえば、わかりやすいです。

【公式日本語ドキュメント】 コンポーネントライフサイクルへのフック

Viewing all 357 articles
Browse latest View live