ITの隊長のブログ

ITの隊長のブログです。Rubyを使って仕事しています。最近も色々やっているお(^ω^ = ^ω^)

WordPressでスケジュール表毎に記事を表示するページ作ったときの流れメモ

2時間で終わると思っていたけど、思った以上に時間かかった。

レベル低くてはずいけど、振り返り。

1. 仕様メモ

  • 数ヶ月毎のスケジュールをタブ化して、それぞれの日に紐づく投稿記事をリスト化してほしい
    • 土曜日と日曜日はclassを付与してcssで色を変更したい
  • 記事にはtaxnomyを紐付けて、taxonomyで絞り込みができるようにしたい

2. 仕様をプログラムに落とし込み

前はボトムアップで考えていました。考えてもよくわからないので、手を先に動かしてハマったら覚えようの姿勢。

が、この方法はぐぐることが前提のため、知識は増えますが、ものすごく時間がかかります。

1日(8時間)かけて実装したけど、結果が全然違うので、夜間やり直し。みたいな。

なので、最近というか去年ぐらいから、要件定義とは別に、やりたいことのtodoをソースコードに書きなぐってから、実装しています。

今回こんな感じ。

  1. アクセスした日付から当月を取得する
  2. 当月から、来月、再来月...と数ヶ月のDatetimeオブジェクトを作成
  3. 揃ったらArray化
    1. それぞれの月で、日スケジュールArrayを作成(ex: 1日 ~ 30日)
    2. 指定月の最初の日、最後の日を取得する
    3. 無限ループの中で、最初の日を1日ずつインクリメントする
    4. Datetimeオブジェクトを作成するArrayにpushしていく
    5. インクリメントした日が最後の日を超えたらbreakする
    6. 指定月の日スケジュールArrayの準備完了
  4. 数ヶ月毎でループ
    1. 数ヶ月毎の中には、日スケジュールArrayがあるのでそれでループ
      1. それぞれ日にマッチする記事を検索して出力する

こんな感じに書きなぐった。

さぁ実装です!

次、結果

結果

実装に5時間かかりました(°ω°;

次、振り返り。

振り返り

主に遅くなった原因を上げていきます。

Datetimeクラスの使い方が曖昧だった

まぁーググりまくった。

色々ありますが、modifyの使い方やformatをググった。あんまり覚えていなかったのね。反省。

フォーマットの符号をよく覚えていない問題

例えば

YYYY/MM/DD HH:mm:ss

よく見るこのフォーマットにするには

<?php
echo (new Datetime())->format('Y/m/d H:i:s');

このH:i:siが中々覚えきれないね。iって何の頭文字よ。さらに、よくやりがちなのはYYYY/MM/DDとほしい形通りのフォーマットに置換してくれると勝手に解釈してしまい、その文字列を渡すってのもよくやりました。moment.jsはそう使えるんだよ!!orz

また、曜日はwを使えますが、0 ~ 6 の数値が返ってきます。

英語ならStringを返すformatもありますが、日本人の場合は、日本語のArrayを用意してあげないといけません。これは次回から用意しておきます。

<?php
$jaWeekArray = ['日', '月', '火', '水', '木', '金', '土'];
echo $jaWeekArray[(new Datetime())->format('w')];

modifyに渡す文字列をよく覚えていない問題

<?php
echo (new Datetime())->modify('+1 days')->format('Y/m/d');

こうすると、実行時の日から+1された日付が表示されます。便利。

便利ですが、色々と使い方があります。これは覚えておいた方がもっとコーディングが早くなると思いました。

<?php
$nextDay = (new Datetime())->modify('+1 days'); // 次の日
$firstDay =  (new Datetime())->modify('first day of this month'); // 今月の最初の日
$lastDay =  (new Datetime())->modify('last day of this month');  // 今月の最後の日
$nextMonth = (new Datetime())->modify('+1 month'); // 来月
$afterMonth = (new Datetim())->modify('+2 month'); // 再来月

'+'と数字を使ったStringはまだわかりやすいですが、文にされると単純に暗記しないと覚えられないですね。。。

WordPressの検索の仕方が曖昧でした

4ヶ月前はガッツリWordPressを触っていたので、やりたいことはだいたいすぐ浮かぶようになりました。

が、「どのメソッドだっけ?」「ここのパラメータってなんだっけ?」ってのが多く、ググってしまいました。

これも今回やったことはちゃんと覚えなければ。。。

検索パラメータの構造

<?php
$parameter = array(
    'post_type' => 'post',
    'posts_per_page' => 4,
    'meta_query' => array(
        array(
            'key' => 'target_date',
            'value' => (new Datetime())->format('Y/m/d'),
            'compare' => '>=', // これはvalue以上の日付を検索する用に指定
            'type' => 'DATE'
        )
    ),
    'orderby' => array('target_date', 'event_start_time'), // orderは複数指定できる、また、mata_queryのkeyも指定可能
    'order' => 'acs'
);
return get_posts($parameter);

orderbyのところが曖昧でした。また、今回初めてしりましたがmetaの情報でもソートができることは覚えておきます。

テンプレートパーツに変数を渡す時

set_query_varを使いましょう。

<?php
set_query_var('tagName', 'タグの名前');
get_template_part('template-parts/part', 'test_tag_name'); // ~/template-parts/part-test_tag_name.phpってphpファイルを読み込む

todoの見直しをした

これが一番の原因じゃないかな。

わかった人は多いと思いますが、上に書いたtodoの中で、

  1. 数ヶ月毎でループ
    1. 数ヶ月毎の中には、日スケジュールArrayがあるのでそれでループ
      1. それぞれ日にマッチする記事を検索して出力する

この部分の、「1. それぞれ日にマッチする記事を検索して出力する」この箇所。これヤバイです。

というのも、数ヶ月毎ループ => 日スケジュール毎ループ => 記事検索(sql発行 -> 問い合わせ)というフローになっているので、DBへの軽いdos攻撃になるんじゃないかなと思ったわけですよ。

ということで、これはまずいと最後の最後で考えを変えないといけなくて、ちと混乱しました。

こういうときはコンビニやトイレ休憩がおすすめです。目の前にいるとこんがらがっているので、一旦別のことすると、少し気持ちが楽になります。んで、歩いているとさっき出てこなかった考えがひらめいたりするのでおすすめです。

さて、じゃあどうしようかなと思ったら、そもそも、数ヶ月毎のループの前で検索する月は決まっています。ので、betweenなどを使って、先に数ヶ月毎の記事を取得しましょう。

ただ、取得したデータは array(indexNumber => post)となっていて、postにアクセスしないと、日付の情報などがとれないです。そのため、日スケジュールのループ時に、2016/01/01というデータが渡された時、その値が記事リストの中に入っているのか確認するため、ループする必要があります。

日スケジュールのループの中で、マッチする日を探すためにループする。月の日数 x 記事数のループをしなければいけません。これはよくない。

「うーん」と悩んでいる中、コンビニ行ってきましたが、単純な話なことに気づきました。

記事リストの配列の構造を変えてしまえばいいのです。

keyを日付、valueを記事情報にしてあげれば、issetで、日付がマッチしていれば、それは表示したい記事だ。ということがわかります。

長くなりました。とまあこんな感じで、todoまで戻って、やり直して、構造を変えて、スマートな感じの処理になり、サービスを実装することができました。

振り返り終わり。

次回どうする?

使いそうな形にして、githubとかでコードを追加しておく

次回はコピペで終了するようにします。

「コピペは禁止」って人も多いと思いますが、数行のコード、またそのコードを理解しているのなら、自分おkだと思っています。

https://github.com/Aipakazuma/php-technical-collection/blob/master/jaWeekArray.php

実装前の // todo:の書きなぐりは引き続き続ける

これは頭がスッキリするので、続けてもいいかなと思う。

また、仕様書とエディタをいったりきたりせずに、エディタ上でソース書きながら確認できるので、いいかなと。

感想

自分で振り返るのってやっていたと思ったけど、今回はじめてかも。

【読書メモ】ITエンジニアが覚えておきたい英語動詞30

この本読みました。

ITエンジニアが覚えておきたい英語動詞30

ITエンジニアが覚えておきたい英語動詞30

うむ。英語苦手、というか英語をよくわかっていない自分でも読みやすい本じゃないかなと思います。

洋書読んでいるとき思うのですが、英語を頭の中で読みながら頑張ってそれの訳を探すのね。In many case, ... えとえと「多くのcase・・・多くの場合」か。みたいな。それをたった2行程の文だとしても結構大変なのです。

母国語のように、母国で意味がわかれば一番手っ取り早いのでは? と、いつも考えています。

この本はそういう英語学習のための基礎?みたいなことを教えてくれました。ITの技術を教えてもらったときにイメージ大事よイメージ! と、言われて学んできましたが、英語もイメージなのね。イメージ。

つーことで、忘れないうちにメモする。

日本人が英語が苦手な理由

本の最初で、外国で仕事するなかで日本人は会話の中で主語の後で詰まる傾向にあると書いています。

  • 日本人は主語の後で詰まる
  • 中国人は動詞の後で詰まる

S(主語) + V(動詞)がでると、「SでVしたい」ことはすぐわかるから、その後で詰まってもなんとなく意図が伝わるらしいです。

が、S(主語)で詰まると、何がしたいのかよくわからないため、会話がスムーズじゃないと述べています。

逆に、S+Vがすぐに話せるなら、会話はスムーズさを感じられるらしいです。その基礎をこの本で理解して鍛える準備をしましょう。ってことですね。

そのためには

  • 英語 => 日本語、日本語 => 英語と、翻訳を繰り返すのでなく、英単語をイメージとして理解すること
  • 動詞は4000語あるが、会話の6割はよく使う30語(基本動詞10 + 実践的に使いこなしたい20)を覚えればおk

なるほど。頑張ります。

基本動詞 10のイメージ

part1では、スピード会話力を身につけるための10の動詞が説明されています。

  1. イメージを理解する
  2. 頻出する使い方を理解する
  3. 練習

このstepで進めてくれるので、1回で覚えるのは難しいですが、なんどか試していくとこの本ででてくる使い方は覚えていけそうです。

詳細は今後読み返して理解してもらうとして、イメージだけ軽くメモしておきます。

Do

具体的な動詞が思いつかないなら、代わりにdoを使って話せばおk.動作を表す名詞にdoをつけるだけ。

  • do the work
  • do the exchange
  • do the copy

いまいち構文は覚えていませんが、とりあえず困ったらdoを使えばだいたいおkらしいです。I do programing.でもいいのかな?

Have

「持つ」って意味がすぐ浮かびそうです。が、主語が「モノ」だとしてもhaveを使うことができます。

f:id:aipacommander:20170102112601p:plain

※PPAPではありません。

例えば、「このプロジェクトには3つの段階がある」を英語にするとなると、

「このプロジェクト」が「3つの段階」を持っているに分解すれば、haveを使って表現することができます。

The project has three ...

って感じ。

  • This machine has no memory.
  • That team has a lot of talents.

擬人化するのが大事とのこと。

Get

getは3つのpartがあります。色んな意味で使えるらしいです。

「手にする」イメージ

f:id:aipacommander:20170102112104p:plain

「詳細をgetする」という使い方をイメージできます。

  • get access
  • get permission
  • get data

2つ目は、「こいつを何とかする」イメージ

f:id:aipacommander:20170102113127p:plain

なんとかするぜぇー。なんとかしてやるぜぇー。

このイメージで大事なのは、「自分がする」ではなく「誰にやってもらう」意味合いが強く、どんな立ち位置にいても使える表現とのこと。

  • get this product shipped
  • get this product improved
  • get this product ordered

最後は、「〜になる」イメージ

f:id:aipacommander:20170102113533p:plain

「大きくなる」は「get + 形容詞」。「壊れた状態になる」は「get + 過去分詞」という使い方ができる。

becomeでもいいらしいですが、getのほうが日常会話で多用されているから是非覚えたいところ。

  • get started
  • get completed
  • get confused

Give

「考えを伝える」はgiveを使う

f:id:aipacommander:20170102113750p:plain

「あげる」で覚えましたが、「考えを伝える」「発表する」「紹介する」は全部giveが使えます。それぞれ具体的にする動詞を探さなくてもgiveを使えばいいってことですね。

  • give a talk
  • give an introduction to ...
  • give a warning(警告する)

Make

「こいつを〜にする」イメージ

f:id:aipacommander:20170102114053p:plain

頻繁に使われるのは

  • make sure(〜を確かめる)
  • make sense(もっともだ、意味を成す)
  • make use of(使う、活用する)

の3つですが、「こいつを〜にする」の使い方が4番目らしいです。

  • make it clear
  • make it available
  • make it small

Take

「自ら手にする」イメージ

f:id:aipacommander:20170102114452p:plain

「取る」ではなくて、はっきりした意図を持って「取る」という意味で使います。積極的なのね。

  • I'll take it
  • take an action
  • take a break

でも「取る」という意味でも使われるとのこと(どっちよ)

f:id:aipacommander:20170102114816p:plain

  • take an example
  • take questions
  • take a seat

日本語にすると動作がバラバラですが、すべてtakeでおkです。

Put

「そいつがある状態にする」をイメージ

f:id:aipacommander:20170102115103p:plain

日本語「置く」って意味は忘れたほうが良い。(こんがらがるので)

え? お前の図は置こうとしている? 確かに。

が、「何か」に「とある状態」にする。って言いかえれば、「机」に「モノを置いた」は同じ意味あいになりますし、また、「名前」を「この欄に書く」でもputが使えます。(欄に名前が書いた状態にする)

  • put your name in this box

putすごい。

  • put the cursor here
  • put a video card in the machine
  • put music in my computer

ちなみに、よく使われるのは「put together」という表現らしく、「まとめる」という意味あいでよく使われるとのこと。

Go

「物事・状況が進む」はgo

f:id:aipacommander:20170102115555p:plain

「何か物事が方向性を持って進む」というのはgoが使えます。

  • go to
  • go ahead
  • go through

「行く」とかで覚えていましたが、色々な表現できるんですね。しゅごい。

  • go back(戻る)
  • sure. go ahead.(どうぞどうぞどうぞ)
  • go through the slide(スライドを検討する) => 1つずつチェックする。検討するという表現ができる

Come

Goは「行く」。Comeは「来る」

f:id:aipacommander:20170102120138p:plain

「私が・・・来た!」って感じかな。「現れる」というイメージを持ってください。

逆にgoはどっか行っちゃう意味合いで捉えられてしまうっぽいです。

なので、遅れて言い訳したあとに「すぐそっちへ向かいます」ならcomeを使えってことですね。

  • come up with(考えが現れた => 思いついた)
  • where did this data come from?
  • the feature will come in the next release.

Look

Lookの話かな? と、思いきやSeeとWatchもでますね。全体的に英語の「見る」イメージを説明しています。

f:id:aipacommander:20170102120537p:plain

何がいいたいのかというと

  • lookは見ている
  • seeは目に入っている

ということ、要は「意図的」なのかそうじゃないのか。よく使う説明ですね。

しかし、本では、どちらも「意図的」と説明しています。

例えば、「信号が見える」であればsee、「信号を見ている」であればlookが使えますが、意識的にはどっちも「見ている」ことの意識はあるわけですよ。

「じゃあ、どないせーちゅねん」となるわけですが、seeするためにlook atする。ということで理解してくださいと書いてあります。どういうこっちゃ。

要するに、seeは”見える状態”で使えば良い。lookは”見える。見る行為”で使ってください。具体的にはseeは"状態"なので命令することができない。命令文で使うことが出来ません。

  • I saw him at the station.
  • I saw your email.(君のメールが届いているのを見たよ)
  • I looked at your email. (内容までしっかり見たよ)

ただ、どっちでもおkな場合もあるので、使い分けは慣れないと厳しいとのこと。結構曖昧なのね。

ちなみに、watchなんですが、「動く画面を見る」っていうイメージなので、こっちは具体的なためあんまり困らないかも。

実践的に使いこなしたい20の動詞

こっからはダイジェスト

  • is that + string ・・・〜は〜だということ
  • need・・・必要だ
  • work・・・作業する -> やる
  • find・・・分かる
  • run・・・〜について意見をもらう
  • follow up・・・引き続き議論する
  • update・・・最新の情報を教える
  • help・・・助かるよ
  • happen・・・たまたまそうなる
  • seem・・・〜みたいだ
  • discuss・・・〜について話をする
  • learn・・・新しいことを知る
  • call out・・・しっかり言っておく
  • block・・・作業ができない
  • capture・・・捕まえる -> 洗い出す
  • execute・・・実行する -> やる
  • conduct・・・実行する -> やる
  • store・・・保存する
  • talk・・・相談する
  • align・・・すり合わせる

storeって、保存するって意味合いだったのね。。。はじめて知った。

まとめ

この本を購入した一番の理由はは、ITエンジニアの方が書いたという点でした。内容には実際に現場で利用した文章が例として載っています。なので、しっかり使いこなせるようになれば、すぐにでもstackoverflowとかで使えるようになるんじゃないか?と思ったので購入しました。

エンジニアの人だったらモチベーションを保ちながら読めるんじゃないかなと思います。

また、最後の章に外国のエンジニアとmeetingするシュミレーションがあります。こっちは流し見でしかみていませんが、上のイメージを理解してやるとたのしいですよ。

本の例や最後の章のシミュレーションは英語の音声などのオプションもあるので、あとでiphoneにいれて通勤中にでも聞き流してみたいなと思います。

早く使えるようになるといいな。

ってことでメモ終わり。

ITエンジニアが覚えておきたい英語動詞30

ITエンジニアが覚えておきたい英語動詞30

2016年振り返り~2017年の目標

明日から来年ですね。

www.aipacommander.com

今年の1月4日に上の記事書いたので結果はどうなったでしょうか。という公開処刑をこれから行います。

IT技術について

びっくりするほど何もできていない(°ω°;

あらー。これは落ち込みますねーorz

全くできていないとまではいいませんが、機械学習とかチュートリアルぐらいしか終わっってないし。せいぜいやったといえばJavaScriptかな。目標をもう少し具体的にしないと比較することもできないねぇ。

ちなみに今年身に付けたと思った内容としては

  • CakePHP2

だけ。。。orz

後半触って苦労しているのは

  • CakePHP3
  • Angular2

かな・・・。

来年なんですが、ちょっと基礎を見直したいと思います。今年よく使った言語順として

  1. PHP
  2. JavaScript
  3. SQL
  4. Python
  5. Java
  6. Scala
  7. VB

こんな感じなので、1,2と3を抜かして4を中心に基礎勉強しようかなと。基本が大事と思った年なので。

プライベートで稼ごうにもどうしても本業に時間を多く時間を割いてしまい、特に今年の後半は中々勉強の時間や試す場が少なかったと思います。基本って結構疎かになっているような気がするので、来年前半はしっかり基礎を固めたいと思う。

具体的に何をする?

本を読みながら手を動かす

個人的に知見を広めるにはこれが一番手っ取り早いです。

ただ、手を動かしながら読むのは結構大変で時間もかかる(経験から)ので、半年を目標にして頑張ります。え? 結構スケジュールきびしいんじゃない? こうでもしないと私はぐーたらしてやらないのです(・ω・;

できるだけさっさと読み漁って手を動かして後半楽しみたい。

転職する

んー。これがいまいち考えがパッとしていません。

今Web会社にいるんですけど、"Web開発"ではないのです。んで、私はプログラマーになりたいのです。

コミュニティに遊びに行って、飲み会とかで他の方々とお話すると転職したほうがいいとよく言われているので、じゃあ是非!って気持ち自体はなっています。

「じゃあ転職せいや」ってなるとは普通思いますが、今このレベル、この習慣で別会社に席を移しても何も変わらないんじゃ・・・?という不安があります。

今技術がない理由を環境のせいにするのはなんかちょっと違うような気がしています。

環境はあくまで+@であって、やっぱり自身のレベル上げには自分次第なのでは?と思っているのです。

2016年はどう?って言われたときに、結局仕事をこなすだけの知識を取り入れたのみで、プログラマーとしては全然成長していないと思います。なので、2017年はコミュニティでLTするとか、自分の作品をGithubに3つアップするなど、そういったことを短い時間でできるようにしていきたいと考えています。これができたら転職しよう。そしたら、明るい転職活動ができるかも。今は不安しかないのでw

  • コミュニティはなんでも良いので、LTを最低2つ発表
  • Githubに自分の作品を3つアップ(サービスでも良い)

これで行きましょう。がんばろ。

ちなみに、今の不満も書いておくね

  • "開発"会社じゃないこと
  • 通勤と退勤で合計2時間かかること
  • リリースの環境が選べない。インフラ技術力がほぼ0な点が大きい。ので、リリース作業やテスト環境などの自動化ができない

給料はプログラマーの割には・・・とよく言われますが、そこに不満はないです。生活できているので。(まぁもっと貰えるならほしいけど)

英語

去年の目標

githubにて英語でコミット 字幕無しで映画を鑑賞

できませんでしたw残念。

ただ、個人的にはこれ結構いい感じじゃないかな? 身についているかさて置き、少しずつ習慣になってきている感じです。

アプリで毎日5つの英文を覚える

Real英会話

Real英会話

  • LT Box Co., Ltd.
  • 教育
  • ¥980

これこれ。すごくいいと思います。

スピーキングとか中々クリアできなくて、すげーイライラしますよw 英語の"R"って上顎の喉側に舌をつくかつかないかのところに置いて(要するに巻き舌)、”アール”って言わないと通じないとか結構リアル。

また、アプリなので、スマートフォンを持っていたらちょっとした待ち時間で勉強できるのでオススメです。

StackOverflowに投稿するようになった

日本の質問サイトで中々解決できなかったので、試しに1時間かけてw英文を書いて、プログラムのソースコードと一緒に投稿したらすぐに返事が返ってきた!

これはすごくうれしかった。英語ヘッタクソでも通じるもんだね。プログラムすごい。

stackoverflow.com

今は英語を使いたくて、なんか仕事でハマったら、逆にうれしい。「よっしゃ!これで投稿できる!」みたいなw

ちなみに、曖昧なことを書いたり、プログラムのソースコードを載せないと回答くれません。やっぱりソースコードを書かないと意図を組みにくいんじゃないかな?

洋書を購入してみた

上にも書いたのですが、Angular2を勉強するためにng-book2という電子書籍を購入しました。英語です。

英語を勉強したいってわけじゃなくて、Angular2を勉強したかった。けど、日本に書籍がないので(リリースが2016年9月ぐらいだから当たり前っちゃ当たり前か)、ヤケクソで購入しました。

いざ、読もうとするとやっぱり読めませんw

そこでGoogle翻訳ですよ。電子書籍なので、英文のコピペや単語を右クリックして意味を翻訳してもらいます。それでちまちま読んでいる感じ。

それを繰り返していると、"Notice,"とかよく出てくるので「ああ、”注目する”ところがあるんだな」とか、よく出てくる単語は勝手に覚えていきます。

最初、ストレスは溜まりやすいですが、目的があるならそれを理由にチャレンジするのもいいかもしれません。

読書

2016年の目標は

技術書 - 月に1冊 年間12冊 実用書 - 週に1冊 目標は53冊です。

さぁどうでしょう!

f:id:aipacommander:20161231152242j:plain

13冊!(内訳: 技術書: 4冊、実用書: 9冊)

orz

むりぽー。全くとどいてへんやん・・・

個人的にはGoogleの本に一番時間がかかりました。翻訳本って結構読みづらいですね。

また、好きになった本はこちらです。

f:id:aipacommander:20161231152311j:plain

好きになりすぎて、続きの本と布教用の本も買った。(布教しているけど、中々伝わらない(´・ω・`))

今副業でデモを作ったりしますが、短い時間でお客さんにデモを開発して見せるってめっちゃ大変です。ソニックガーデンさんはさらに、2、3つの案件は余裕でこなせるらしいです。すごい。

また、実際自分の本業でも実感することがありました。

本業の顧客からよく伺うのは

納品までに、申し訳ないがどうにか要件と違うことをやってほしい(要は仕様変更) 追加の発注は稟議を通すのが大変、または時間がかかるのでストレス 実際動かしてみるとやっぱり違う感じがする

というお話をよく伺いました。これってやっぱ納品型のビジネスってシステム開発に向いていないじゃないかなと考えています。仕様変更なんて100%発生します。自分はそのつもりですが、周りの人や経営陣は不満を感じています。

ただ、納品のない開発をまだ経験したことがないため、メリット・デメリットがよくわかっていないので(文章ではわかった気になっていますが、個人の成功事例がないので)、副業で試していこうと思います。

転職活動に是非この本の著者さんの会社にも挑戦したいなと考えています。

来年の目標は?

技術書は上で書いた通り、前半は全部で9冊です。

で、実用書なんですが、できればゆるーく読んでいこうと考えています。個人的に目標とすることは書評です。

実際読んだ本は、月日が立つと内容が薄れていきます。で、覚えている本って実は書評書いた本だったり手を動かした本なんですよね。

やっぱり実際試した本は記憶に残りやすいのか、良いと思いました。せっかく時間使って読んでいるので、読む本は書評が前提として読むようにしていきます。

  • 本数は20冊
  • 書評前提
  • あと必須じゃない限りは本を購入しない(買い過ぎ。積み本になっている本が多いorz)
    • 可能な限りすべて読んでから購入すること

生活

貯金ができるようになりました。ヤッター!!!

今貯金十万単位で貯金できてるよ!!!ヤッター!!!

元パチンカスで、浪費型な私が! 貯金できるようになりました!

これってなんかブログの記事になりそうなので、箇条書きで理由を書いておきます。

  • 使ったお金は必ずログに残す(Dr.walletを使っています)
  • 収入 > 消費の式が成り立つように月々生活する
    • 貯金額を1万以上毎月振り込む
    • コンビニは悪
  • ATMのカードを作らずに通帳を複数発行して、給与・支払いなどを毎月自分の足で歩いて引き落とし・振込などをする

大まかにこの3点です。これで借金や支払い滞納を返済しながら(今年は50万ぐらい返済できた!)貯金ができた!

まぁちょっと色々あって今年最後に大きな負債がでてきましたが、これは普通に返して2年を予定しています。

が、そんなに不安ではないですね。今ならもっと短くで返済できる見通しが立てれそう。がんばろ。

達成できないかったやつ

  • 引っ越し&結婚
    • 来年できるかな・・・?ってかやらないと
  • ダイエット
    • 見事に同じ体重のまま。まずい
    • 一応ジョギングは続いています。何故かやせないのよねー
  • ギター
    • 全く習慣がなくなりました。もったいない

まとめ

今年は色々な出会いがありましたー。みんなに負けないようにがんばりますー。

これから走ってきます。来年もよろしく。

Angular2を触り始めたがこいつがフレームワークってことを忘れていました

意味深なタイトルっぽいけど、今日の反省です。技術的な話はしないのでそのつもりで来た人はブラウザバック。

CakePHP3をサーバサイドで、フロントをAngular2で開発しようとしました。

CakePHP3側はJson APIとして使っていましたが、部分的にCakePHP側でもviewを用意していました。

例えばPostsControllerがあるとして、addとeditのページ(アクション)を用意しました。

<?php

class PostsController extends AppController {

    public function add() {
    }

    public function edit() {
    }
}

ただ、addとeditのformの中には動的なfieldがあり、そこをAngular2でやろうとしたのです。

これが間違いだった。

さぁ、いざ組んでみますと、Angular2のコードがモリモリ増えていきます。(やってみたらわかります)

ついには、CakePHP側のViewがすべてAngular2になりました。

用意したaddやeditはただ、jsを読む込ませるためのtemplate表示になりました。そのあとはフロント側のAngular2がデータをリクエストしてtemplateを元にviewを組み立てていく。ま、いいんじゃね?と思い開発を進めました。

が、困ったのはそのあと。

addページで保存したあと、次のアクションをどうしようか迷ったときです。

普通は、その場で編集しますか?と促したり、indexページへリダイレクトしたりするはずです。

私もそこで、そういうwindow.confirmを用意して、ユーザーに判断させるコードを実装しました。

        // ...
        if (window.confirm('データを保存しました。編集を続けますか?')) {
          this.postCrudService.saveData(response);
          this.router.navigate(['/posts/edit/' + response.data.id])
        } else {
          this.router.navigate(['/posts/'])
        }
        // ...

this.routerはAngular2のモジュールである、Routerモジュールです。こいつを使うことで、ページの移動などを実装することができます。this.postCrudServiceはDIしている自作のサービスクラスです。こいつで保存したデータを使いまわすようにしています。

私は、addで保存したら、編集ページに移動しようとしました。が、Angular2は思ったとおりに動作しませんでした。

私は、リロードすると思いましたが、しませんでした。そういえばsingle page applicationってことを忘れていました。

これは大変です。私は部分的にAngular2を使おうとしていましたが、CakePHP3でやろうとしていたことがAngular2でもできるようになっているのです。マジのマジでCakePHP3はJsonAPIになってしまった。

あらー、1日中連携しようともくもく頑張っていたのに、これじゃPostController->index()のtemplateもAngular2にしないとダメってことだな。あららのら。

確かに読む込むだけのaddやeditがあるってなんかキモいもんな。今回の場合だと

  • index - ページ読み込み用
  • add - jsonで新規保存
  • edit - jsonで編集保存
  • delete - jsonで削除

っていうAPIを用意して、全部Angular2に任せればよかったのか。

この休日ずっとAnguarl2(ng-book2)を勉強すればよかった。知らずにそのまま開発してしまった。

あー失敗した。やばい。やばい。やばいぃーー!!!

jQueryしか使ったことがない人がAngularのFormで試したことを書く

この記事はAngular Advent Calendar 2016 17日目の記事です。(遅刻しました)

この記事を書いている人

  • jQueryを使って、基本的な使い方でホームページのナビゲーション開閉やタブのアニメーションを書ける人
  • jQueryを使って、動的なFormを作ったり、サーバへリクエスト飛ばしたりする人
  • jQueryを使って、5000行ぐらいのシミュレーターみたいなアプリケーションを書いたことがある人

仕事で使っているというだけで、jQueryJavaScriptを深くは理解していません。大好き!ってわけでもありません。「javascript やりたいこと」でググって解決するレベルです。Googleがないと仕事できない。

そんな人がAngular2を試したら

  • 「AngularJS1.x触ったことあるしへーきへーき」 -> (°ω° オレノシッテイルアンギュラハドコ?
  • 「Typescript?ES6?なにそれ?」
  • 「webpack?gulpとかnpmじゃだめなの?」

↑の状態で挑んだら簡単に死亡いたしました。本当にありg(ry

で、そんな人がググり続けたとしても、古い情報なのか新しい情報なのかわからるはずもなく、地雷を踏みづづけるだけで、一向に闇から抜け出せません。マジで挫折一歩手前。

近くに簡単に質問できる人もコミュニティもないため、洋書ですが本を購入することにしました。

  • ng-book2

www.ng-book.com

記事の内容のほとんどが↑の本から学習したことなので、この記事読まなくて↑の本読んだ方が早かったりするかもです(^^ゞ ※英語の本です。 ※私は英語はほとんど理解してませんが、Google翻訳を使って読むとびっくりするほど内容が理解できます。ありがとうGoogle

この本購入してからAngular楽しいお(^ω^ = ^ω^)

本記事は ng-book2 を読んで、Angular Formで学習したこと、Angular Formで試したことのログをまとめた記事です。

Form in Angular2

この記事で試したソースコードgithubに上がっています。実際にコードを動かしたほうが理解すると思いますー。

github.com

はじめの環境構築

必要なアプリケーションのインストール

この記事は、Typescriptとangular-cliを使います。また、OSはOSXです。windowsの方はすみません(´・ω・`)

自分の環境は以下。

$ sw_vers 
ProductName:  Mac OS X
ProductVersion: 10.11.6
BuildVersion: 15G1108

$ node -v
v6.6.0

$ npm -v
3.10.9

それでは環境構築します。

$ npm install -g typescript
$ npm install -g angular-cli@1.0.0-beta.18
# angular-cliの監視オプションに使うツールらしい
# linuxの人はどうすればいいの? 
# -> https://ember-cli.com/user-guide/#watchman
# windowsの人はどうすればいいの?
# -> なんか必要ないんだって。nodea.jsに実装されているwatcherを使うんだって
$ brew install watchman

無事インストールできたら、確認。

$ ng version
angular-cli: 1.0.0-beta.18
node: 6.6.0
os: darwin x64

おk

プロジェクトを作りましょう

$ ng new angular-hello-form-application

これだけ。That's it. 感動。

動作確認してみましょう

angular-cliApacheとかwebアプリケーション的なものを用意しなくても、すぐに動作を確認できるオプションが用意されています。

$ cd angular-hello-form-application/
$ ng serve
** NG Live Development Server is running on http://localhost:4200. **
...

ng serve実行後、すぐにURLが表示されるので、それをコピってブラウザで確認してみてください。

f:id:aipacommander:20161226192107p:plain

この画面がでてきたら、環境構築完了です!

それでは本題に進みます。

FormControlとFormGroup

Angular2でFormを使いたいなら、この2つのモジュールを利用します。

FormControlは、1つのinputフィールドを意味します。

let firstName = new FormControl("Aipa");
console.log(firstName.value);  // "Aipa"
console.log(firstName.errors); // 型はStringMap<string, any> of errors
console.log(firstName.dirty);  // 値は変わったか検知。boolean
console.log(firstName.valid);  // validateの結果が登録されます。boolean

templateで使いたい場合はこう書きます。

<!-- formの中で書く -->
<input type="text" [formControl]="name">

んで、これらをまとめてくれるのが、FormGroupです。

let yourName = new FormGroup({
  firstName: new FormControl('Aipa'),
  lastName: new FormControl('Commander')
});

console.log(yourName.value); // -> {
//  firstName: "Aipa",
//  lastName: "Commander"
// }

console.log(yourName.errors); // StringMap<string, any> of errors
console.log(yourName.dirty);  // false
console.log(yourName.valid);  // true

というふうに、まとめることができます。

FormControlは1つのフィールド、FormGroupはフィールド全体を見てくれます。

例えば、firstNameのFormControlのバリデーションがfalseだった場合、yourNameのバリデーションもfalseになります。部分的にチェックしてエラーを表示したり、全体のバリデーションがうまくいかないとsubmitさせないなどができると思います。

yourName.formControl['firstName'].valid; // false
yourName.formControl['lasttName'].valid; // true
yourName.valid; // false

簡易フォームの実装

それでは、実際に実装してみましょー。

と、その前に、ちゃんと説明しようと書いていましたが、説明の仕方に悩んだときにこんな記事を見つけました。

;°ω°)モウコレデイイジャン

もっと早く会いたかったです。

まぁ自分は自分なりに説明書いてログにします。

本題に戻ります。先ほど作ったプロジェクトのディレクトリで、componentをgenerateします。

$ ng generate component first_form

まずは、generateしたcomponentに実装していきます。

おっと、その前にFormControlやFormGroupを使えるようにするために、~/angular-hello-form-application/src/app/app.module.tsで、FormsModuleとReactiveFormsModuleをimportします。

~/angular-hello-form-application/src/app/app.module.ts

import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // <- add

@NgModule({
  declarations: [
    AppComponent,
    FirstFormComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
    FormsModule,        // <- add
    ReactiveFormsModule // <- add

これでおkです。

ちなみに、FormsModuleのみimportすると

  • ngModel
  • NgForm

を組み合わせて、Formを扱うことができます。ReactiveFormsModuleも一緒にimportすると

  • formControl
  • ngFormGroup

を含め、色々使えるようになります。

ふーん。Reactive リアクティブってなに?

ぐぐったらこの記事がでてきた。ふむ。

Webフロントエンドでリアクティブプログラミング

いまいちピンときていません。分かる人いたら教えて下さい。

まぁいまはなんだっていいでしょう。実装に話を戻します。

~/angular-hello-form-application/src/app/app.component.htmlにタグを追加します。先ほどgenerateしたcomponentをrenderできるように追記します。

~/angular-hello-form-application/src/app/app.component.html

<app-first-form></app-first-form>

formの実装に入ります。

~/angular-hello-form/application/src/app/first-form/first-form.componet.htmlに、formを追加します。

<form #f="ngForm" (ngSubmit)="onSubmit(f)">
  <div class="form-group">
    <label>お名前は?</label>
    <input type="text" placeholder="名前を入力してください" name="yourName" ngModel>
  </div>
  <div class="form-group">
    <button type="submit">送信</button>
  </div>
</form>

↑のように追加すればおkです。

突然あらわれる#f=ngFormngFormにびっくりしましたが、これはFormsModuleをimportするだけで、NgForm(<form #f="ngForm" ...>)がviewで使えるようになり、また自動的に<form>にアタッチされます。

それでは、component.tsへメソッドを追加します。

~/angular-hello-form/application/src/app/first-form/first-form.componet.ts

export class FirstFormComponent implements OnInit {
  // ...
  onSubmit(form: any): void {
    console.log(form.value);
  }
}

追加したフォームに"アイパー隊長"と入力し、送信ボタンをクリックすると、consoleから、Object {yourName: "アイパー隊長"} がでてきました。これでよし。

#f=ngForm#fはテンプレートで使用できるようするローカル変数です。つまりこの箇所はローカル変数を宣言している&値をセットしているのです。

#fがフォームの値となります。「テンプレートで使える変数」という理解でおkだと思います。

また、NgFormの型はFormGroupです。なので、変数fは、FormGroupとして扱うことができます。

最初のFormGroupとFormControlの関係を説明した通り、FormControlはFormGroupに追加されて扱えるようになります。

このフォームではngModelがセットされているinputタグの値は、自動的にFormControlを作成して、属性であるname=***で関連付けられます。そして、FormGroupである変数fにセットされています。

このフォームでsubmitボタンをクリックすると、onSubmitのイベントが発生します。その時実行されるのが、<form>の属性にある(ngSubmit)="onSubmit(f.value)"です。

Angularで()は、イベントをセットするときに使う属性と理解しています。んで、実行されるのが右側のコードです。onSubmit(f.value)は、Componentに実装してあげます。

上ですでに動きは確認しました。もちろんconsole.log()だけではなく、フォームにセットされた値を確認したり、バリデーションしたり、サーバへ送信したりなど、やりたいことを実装してあげれば良いです。

よっしゃ!理解したぞ!(多分)

ここまでが、単純にフォームを使うだけのチュートリアルです。

FormBuilderでFormのカスタマイズ

ngForm、ngModelを使って、暗黙的にFormControl、FormGroupを使ってみました。

ただ、このままだとカスタマイズができません。

実践では、もっと複雑に、詳細にカスタマイズするはずですので、今度はそれができるFormBuilderを使ってみましょう。

今、自分もこのFormBuilderを実践で使っています。

ng-book2をFormBuilderはどういうふうに使えばいいの? 語るよりはまずは実際に実装してみました。

コンポーネントをジェネレートします。

$ ng generate component use-form-builder

~/angular-hello-form/application/src/app/app.component.html

<h1>
  {{title}}
</h1>
<app-first-form></app-first-form>

<app-use-form-builder></app-use-form-builder> <!-- これを追加 -->

んじゃ、使用するComponentで、FormBuilderをimportします。

~/angular-hello-form/application/src/app/use-form-builder/use-form-builder.component.ts

import { FormBuilder, FormGroup } from '@angular/forms';

importしただけじゃ使えないので、DIしましょう。 ※"DI (dependency injection)"って何?の人。自分もよくわかっていないので、近くのエロい人たちに聞いてください。

export class UseFormBuilderComponent {
  myForm: FormGroup;

  constructor(formBuilder: FormBuilder) { // <- これがDI(らしい)
    this.myForm = formBuilder.group({
      'myName': ['Aipa']
    });
  }

  onSubmit(value: string): void {
    console.log('わたしの名前は' + value);
  }
}

これで、FormBuilderが使えるようになりました。constructor()の引数にセットすることで、このComponentで利用できるようになりました。

FormBuilderでよく使うメソッドは2つ

  • control - FormControlを新規で作成します
  • group - FormGroupを新規で作成します

この2つです。

formBuilder.group({})は、FormGroupを返します。

また、formBuilder.group({})の中に、KeyValueにセットされているmyForm: ['Aipa']はFormConotrolです。また、['Aipa']は初期値です。

ちなみに、arrayってなんてのもありますが、この記事の最後あたりで試したログがあります。

説明がだるくなってきたので、まずは動かしてみてみましょう。templateを用意して、ビルドしてみてください。

~/angular-hello-form/application/src/app/use-form-builder/use-form-builder.component.html

<h2>ふぉーむびるだーずふぁいたーずです。</h2>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group">
    <label>お名前は?</label>
    <input type="text" placeholder="名前を入力してください" [formControl]="myForm.controls['myName']">
  </div>
  <div class="form-group">
    <button type="submit">送信</button>
  </div>
</form>

実行するとこんな画面が出力されるはず。初期値が入っていますね。

f:id:aipacommander:20161226192217p:plain

また、submitしてみると、consoleに"わたしの名前はAipa"とでるはず。これでおkです。

少し説明します。

<form>タグの属性に、[formGroup]="myForm"とあります。この[formGroup]は、ディレクティブとして動作しており、myFormをこのフォームのFormGroupとして利用することを宣言しています。

次に、<input>タグの属性に[formControl]="myForm.controls['myName']"とあります。これは既存のFormControlにFormBuilderで作成したformControlをバインドしています。これでComponent側で細かく設定する情報を付与することができます。

Validation

次はValidationを試します。

~/angular-hello-form/application/src/app/use-form/builder/use-form-builder.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-use-form-builder',
  templateUrl: './use-form-builder.component.html',
  styleUrls: ['./use-form-builder.component.css']
})

export class UseFormBuilderComponent implements OnInit {
  myForm: FormGroup;

  constructor(formBuilder: FormBuilder) { // <- これがDI(らしい)
    this.myForm = formBuilder.group({
      'myName': ['Aipa', Validators.required]
    });
  }

  ngOnInit() {
  }

  onSubmit(form: any): void {
    console.log(form);
    console.log('わたしの名前は' + form.controls['myName'].value);
  }
}

~/angular-hello-form/application/src/app/use-form-builder/use-form-builder.component.html

<h2>ふぉーむびるだーずふぁいたーずです。</h2>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group" [class.error]="!myForm.controls['myName'].valid && myForm.controls['myName'].touched">
    <label>お名前は?</label>
    <input type="text" placeholder="名前を入力してください" [formControl]="myForm.controls['myName']">
    <div *ngIf="!myForm.controls['myName'].valid">myName is invalid</div>
    <div *ngIf="myForm.controls['myName'].hasError('required')">myName is required</div>
  </div>
  <div class="form-group" *ngIf="!myForm.valid">Form is invalid</div>
  <div class="form-group">
    <button type="submit">送信</button>
  </div>
</form>

ValidationはValidatorsをimportして利用します。

先ほど、key valueでformControlの設定をFormBuilder.groupへ渡しました、valueの値は配列であり、要素の0番目が初期値でした、Validationは要素の1番目にセットします。今回はValidators.required(空文字NG)をセットしました。

templateには、エラーを表示するようにngIfを用意しています。最初のほうで説明したformControlやformGroupにある、validを使っています。また、セットした特定のValidationのエラーを表示したい場合は、 myForm.controls['myName'].hasError('required')を使って出力します。

次は複数のValidationをセットします。4文字以上入力しないとエラーを出力するようにしてみます。Validators.minLenghtを使います。

「複数あるから・・・」って考えたら、要素の2番めに配列でValidatorsを渡したくなると思います。それでも動きました。が、本ではそんな説明がありませんでした。(ネットにはちらほらありますが)

配列で渡すのは正規な方法ではないのかな?

この記事ではValidators.compose([])を使います。

~/angular-hello-form/application/src/app/use-form/builder/use-form-builder.component.ts

  constructor(formBuilder: FormBuilder) {
    this.myForm = formBuilder.group({
      'myName': ['Aipa', Validators.compose([ // <- これを追加
          Validators.required,
          Validators.minLength(4)
        ])
      ]
    });
  }

~/angular-hello-form/application/src/app/use-form-builder/use-form-builder.component.html

<h2>ふぉーむびるだーずふぁいたーずです。</h2>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group" [class.error]="!myForm.controls['myName'].alid && myForm.controls['myName'].touched">
    <label>お名前は?</label>
    <input type="text" placeholder="名前を入力してください" [formControl]="myForm.controls['myName']">
    <div *ngIf="!myForm.controls['myName'].valid">myName is invalid</div>
    <div *ngIf="myForm.controls['myName'].hasError('required')">myName is required</div>
    <div *ngIf="myForm.controls['myName'].hasError('minlength')">myName must be at least 4 characters long.</div> <!-- ここを追加 -->
  </div>
  <div class="form-group" *ngIf="!myForm.valid">Form is invalid</div>
  <div class="form-group">
    <button type="submit">送信</button>
  </div>
  <pre>{{myForm.controls['myName'].errors | json}}</pre>
</form>

全然関係ないですが、このValidators.minLength(4)、templateでエラーを出力する属性を用意したら何故か出力されませんでした。

最初書いていたコードはこちら。

<div *ngIf="myForm.controls['myName'].hasError('minLength')">myName must be at least 4 characters long.</div> <!-- ここを追加 -->
hasError('minLength') // "minLength" => ダメ、"minlenght" => おk

(°ω° キャメルケースじゃダメなのね。。。

<form>の閉じタグ1行前で、<pre>{{myForm.controls['myName'].errors | json}}</pre>で確認したら発覚しました。わかりづらい。

ちょっとハマったが、Validationを複数扱うときは、Validators.compose()を使えばおk.

Custom Validation

Validationの続きですが、自前でValidationも用意することができます。

返り値はStringMap<string, boolean>で返せば良いです。クラスがあるとわかりにくいのでようは

{ [s: string]: boolean }

これを返せばよい。

~/angular-hello-form/application/src/app/use-form/builder/use-form-builder.component.ts

function fullMatchStringAipa(control: FormControl): { [s: string]: boolean } {
  // 初期値がない場合はエラーになったので
  // nullチェックもする
  if (control.value) {
    if (!control.value.match(/^Aipa/)) {
      return {invalidAipa: true};
    }
  }
}

// ... 省略

  constructor(formBuilder: FormBuilder) {
    this.myForm = formBuilder.group({
      'myName': ['Aipa', Validators.compose([
          Validators.required,
          Validators.minLength(4),
          fullMatchStringAipa // <- これを追加
        ])
      ]
    });
  }

~/angular-hello-form/application/src/app/use-form-builder/use-form-builder.component.html

<div *ngIf="myForm.controls['myName'].hasError('invalidAipa')">myName is string "Aipa"</div>

hasError()で渡す値は、自前で用意したValidationの返り値{ [s: string]: boolean }s: stringの値を渡してください。

Watching for changes

formControlの値の変更を検知することができます。AngularJS1.xではすごく苦労した記憶があります(物覚え悪いからだと思いますが)。今回はどうでしょう。

~/angular-hello-form/application/src/app/use-form/builder/use-form-builder.component.ts

  constructor(formBuilder: FormBuilder) {
    this.myForm = formBuilder.group({
      'myName': ['Aipa', Validators.compose([
          Validators.required,
          Validators.minLength(4),
          fullMatchStringAipa
        ])
      ]
    });

    // 追加した記述
    this.myForm.controls['myName'].valueChanges.subscribe(
      (value: string) => {
        console.log('aipa value change: ', value);
      }
    );
  }

個人的にはすごくわかりやすかったです! こんなすぐ使えるのねー。

次は試したかったこと。です。

Form in Angular2 で、試したこと

Enter keyの無効化

フォームで入力中にEnter keyを2度押してしまって、ページが遷移するってことありません? わたしはよくあります。

あれが嫌で、作成するフォームはだいたい<form onsubmit="return false;">をしています。

これが良いか悪いかの判断はできませんが(できる方、教えてくださいm(_ _ )m )、とりあえずAngular2でもやりたかったので、試してみました。

<form [formGroup]="myForm"
      (ngSubmit)="onSubmit(myForm.value)"
      (keydown.enter)="keyDown($event)" <!-- 追加 -->
      class="ui form">
</form>

keydown.enterで、エンターキーが押されたときのイベントが発火したときに、メソッドをコールすることができる。Component側に下記を実装。

keyDown(event: any): void {
  console.log('You just clicked entry.');
  return event.preventDefault(); // enterを無効化
}

これでできました。

Input form bind date picker

日付入力のフォームには、date pickerをよく使いますよね。jQueryだったらすぐ使えます。

が、Angular2ではどう扱ったらいいのはわからず、ググり方もわからなかったので、時間を多く浪費しました。

pluginも色々試しましたが、何故かうまくいかなかったんですよねー。

色々学んできて、やっとできたので、これもメモしておきます。

3rd Party Library Installation

まず、必要なライブラリをインストールします。

$ npm install pikaday moment --save-dev 

今回はpikadayを使います。また、moment.jsも依存しているため、これもインストール。

んで、ビルドツールである、angular clijsonファイルに追記します。

~/angular-hello-form-application/angular-cli.json

"app": [{ 
      "styles": [
        "styles.css",
        "../node_modules/pikaday/scss/pikaday.scss"
      ],
      "scripts": [
        "../node_modules/pikaday/pikaday.js",
        "../node_modules/moment/moment.js"
      ]
}]

次に使いやすいようにdirectiveをを用意します。

$ ng generate directive date-picker
installing directive
  create src/app/date-picker.directive.spec.ts
  create src/app/date-picker.directive.ts
import { Directive, ElementRef, Input } from '@angular/core';

// 宣言?
declare let Pikaday: any;

@Directive({
  selector: '[appDatePicker]'
})
export class DatePickerDirective {

  // この変数は呼び元コンポーネントから値をもらうので
  // @Input()の値をselectorと同じにする
  @Input('appDatePicker') datePickerField: any;

  constructor(
    // pikadayのクラスのbindにはフィールドの情報が必要なため
    // ElementRefを使って自分のフィールドを渡す
    private elementRef: ElementRef
  ) {}

  ngOnInit() {
    var picker = new Pikaday({
      field: this.elementRef.nativeElement,
      format: 'YYYY/MM/DD',
      i18n: {
        previousMonth : '先月',
        nextMonth     : '来月',
        months        : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
        weekdays      : ['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'],
        weekdaysShort : ['日','月','火','水','木','金','土']
      },
      onSelect: (date) => {
        // 何故かpikadayをbindしただけじゃ、formControlの値を変更することができない
        // onSelect時に、直接formControlへ選択した文字列を渡すようにする
        this.datePickerField.setValue(picker.toString());
      }
    });
  }

}

ちなみに、悩みとしては、angular cliでビルドしていますが、pikadayのcssなどが、htmlへinlineで追加されてしまい、他のcssに影響がでています。

上書きしてもいいですが、あとあと絶対面倒になるので、どうにか必要なページだけpikaday、pikadayのcssをロードできないか方法を探し中です。知っている人いましたら教えてください(><

追記 2016/12/26

必要なページだけ読み出す方法がわかりました。

まず、angular-cliに追加した情報を削除します。 ~/angular-hello-form-application/angular-cli.json

"app": [{ 
      "styles": [
        "styles.css",
        "../node_modules/pikaday/scss/pikaday.scss" // 削除
      ],
      "scripts": [
        "../node_modules/pikaday/pikaday.js", // 削除
        "../node_modules/moment/moment.js" // 削除
      ]
}]

んで、directiveをcomponentへ変更して、デコレータのパラメータを変更します。というか、Componentとして作り直したほうがいいかもしれません

~/src/app/date-picker/date-picker.component.ts

import { Component, ElementRef, Input, ViewEncapsulation } from '@angular/core';

// 宣言?
const Pikaday = require('../../../node_modules/pikaday/pikaday');
const PikadayStyle = require('../../../node_modules/pikaday/scss/pikaday.scss');

@Component({
  selector: '[appDatePicker]',
  template: '',
  styleUrls: [PikadayStyle],
  encapsulation: ViewEncapsulation.None
})
export class DatePickerComponent {

個別のページだけモジュールを使うって方法がわからなかったので、汚い英語ですが、stackoverflowに投稿して回答を待ちましたところ、「requireを使えばいけるよ」的な回答を頂いたので、試してみたらまじでうまくいきました(°ω°

stackoverflow.com

自分の実装はDirectiveで試していましたが、styleをあてたかったのと、Directiveのデコレータでstyleを指定する方法がわからなかったので、Componentへ変更しました。

また、Componentが吐き出すstyleは、cssセレクターに動的の属性が当てられます。しかし、pikadayのdomはComponentの属性が付与されないため、単純にComponentを用意するだけではstyleはあたりません。

なので、encapsulationの値に、ViewEncapsulation.Noneを渡してあげると、(よろしくないとは思いますが)属性を付与しなくなるので、pikadayのdomにstyleがあたるようになります。これでおkです。

これで必要なComponentだけstyleとmoduleをロードすることができるようになりました。githubの方も修正しています。

動的なフォーム

これが一番やりたかった。

ng-book2 にはのっていなかったので、できるか心配でしたが、色々ググって試してみたらできたのでメモしておきます。

$ ng generate component dynamic-form
installing component
  create src/app/dynamic-form/dynamic-form.component.css
  create src/app/dynamic-form/dynamic-form.component.html
  create src/app/dynamic-form/dynamic-form.component.spec.ts
  create src/app/dynamic-form/dynamic-form.component.ts

~/angular/hello-form/application/src/app/dynamic-form/dynamic-form.component.html

<h2>動的フォーム</h2>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group">
    <label>お名前は?</label>
    <input type="text" [formControl]="myForm.controls['yourName']">
  </div>
  <!-- この下のタグを渡せないと動作しなかった。group -> arrayにも名前を用意しないといけないらしい -->
  <div formArrayName="whatAnimationDoYouLike">
    <div *ngFor="let testDatetime of myForm.controls.whatAnimationDoYouLike.controls; let i=index">
      <div [formGroupName]="i">
        <div class="delete-button">
          <span>Address {{i + 1}}</span>
          <button type="button" *ngIf="myForm.controls.whatAnimationDoYouLike.controls.length > 1" (click)="removeGroup(i)">フォームから削除</button>
        </div>
        <div class="form-group">
          <label>アニメの名前は?(必須)</label>
          <input type="text"
            [formControl]="myForm.controls['whatAnimationDoYouLike'].controls[i].controls['answer']">
        </div>
        <div class="form-group">
          <label>好きな理由は?(任意)</label>
          <input type="text"
            [formControl]="myForm.controls['whatAnimationDoYouLike'].controls[i].controls['reason']">
        </div>
      </div>
    </div>
  </div>

  <div class="margin-20">
    <button type="button" (click)="addGroup()">フォームを追加</button>
  </div>
  <pre>{{myForm.value | json}}</pre>
  <div class="form-group">
    <button type="submit">送信</button>
  </div>
</form>

属性formArrayName="whatAnimationDoYouLike"の箇所。これも必須です。Cannot find control with unspecified name attributeのエラーが発生します。group -> arrayにも名前を用意しないといけないらしい。

下はComponentです。動的なフォームを組みたい場合、FormArrayが必要になりますので、importしてください。

~/angular/hello-form/application/src/app/dynamic-form/dynamic-form.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';

@Component({
  selector: 'app-dynamic-form',
  templateUrl: './dynamic-form.component.html',
  styleUrls: ['./dynamic-form.component.css']
})
export class DynamicFormComponent implements OnInit {
  myForm: FormGroup;

  constructor(
    private formBuilder: FormBuilder
  ) { }

  ngOnInit() {
    this.myForm = this.formBuilder.group({
      'yourName': ['', Validators.required],
      'whatAnimationDoYouLike': this.formBuilder.array([
        this.initGroup()
      ])
    });
  }

  initGroup() {
    return this.formBuilder.group({
      'answer': ['', Validators.required],
      'reason': ['']
    });
  }

  addGroup() {
    const arrayControls = this.getFormArrayControls();
    arrayControls.push(this.initGroup());
  }

  removeGroup(i: number) {
    const arrayControls = this.getFormArrayControls();
    arrayControls.removeAt(i);
  }

  getFormArrayControls() {
    return <FormArray>this.myForm.controls['whatAnimationDoYouLike'];
  }

  onSubmit(form: any) {
    console.log(form);
  }
}

initGroup()で、複数のformControlをgroupingして値を返します。

addGroup()は、今現在のFormArrayを取得して、initGroup()から追加データをpush()して追加しています。

removeGroupはその逆です。現在のFormArrayを取得して、指定された要素番号でFormArrayを、removeAt(i)しています。i: numberはテンプレートからもらう情報です。

これでおkです。もしFormArrayの値をwatchしたいとかあれば、this.myForm.controls['whatAnimationDoYouLike'].controls[i].controls['answer']とかでvalueChangesを実行すればおkです。ながいねー。

まとめ(?)

1ヶ月前は「なんでこのフレームワークを選んだんだ(´;ω;`)ブワッ」と泣きそうになりましたが、理解してくると楽しくなってきますねー。

近くに教えてくれる人がいると手っ取り早いですが、そうじゃない場合はやっぱり本がいいのではと思います。

引き続き、Angular2を学んで、使えるようになったらまた色々と記事を上げていきたいです。