君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

Angular2/4にてngSwitchを使ってtemplateの一部切り替えを行う

Angular2/4にてngSwitchを使ってtemplateの切り替えを行う

「routerを使う程ではないけど、templateを部分的に書き換えたいな〜」と思ったので、

ngSwitchを使用してtemplateの部分的な切り替えを行おうと思います。

0. 準備

新規プロジェクトを作成し、サーバをスタートさせます。

ng new ng-switch
ng serve

1. app.component.htmlの修正

<!-- app.component.html -->
<button (click)="onClick()">おしてね</button>
<div [ngSwitch]="templateFlg"> <!-- 重要 -->
  <div *ngSwitchCase="true"> <!-- 重要 -->
    <img style="display: inline;"
        src="https://pbs.twimg.com/profile_images/442329275769683968/gvLogHdH.jpeg"
        width="250px"
        height="250px"
        >
    <div style="display: inline;">(・(ェ)・)</div>
  </div>
  <div *ngSwitchCase="false">  <!-- 重要 -->
    <img style="display: inline;"
         src="https://pbs.twimg.com/profile_images/795060602309726208/WAKx1L5U.jpg"
         width="250px"
         height="250px"
         >
    <div style="display: inline;">(ΘェΘ)</div>
  </div>
</div>

templateの解説

<div [ngSwitch]="templateFlg">
  <div *ngSwitchCase="true">
    <!-- templateFlg == true の場合に表示されるtemplate -->
  </div>
  <div *ngSwitchCase="false">
    <!-- templateFlg == false の場合に表示されるtemplate -->
  </div>
</div>

ngSwitchには、出し分けするために用意した変数を代入します。

今回の場合はtemplateFlgという変数を用います。

それぞれのtemplateには*ngSwitchCaseを使って、

どの条件のときにtemplateを表示するかを記載します。

2. AppComponentの修正

import { Component } from '@angular/core';
import { NgSwitch } from '@angular/common'; // 追記

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  /** ここから */
  private templateFlg = true;
  onClick()
  {
    this.templateFlg = !this.templateFlg;
  }
  /** ここまで追記 */
}

結果

f:id:karoten512:20171004160035g:plain

Angular2/4のLifecycle Hooksを理解する(ngOnInit編)

Angular2/4のLifecycle Hooksを理解する

Angular2/4のLifecycle Hooksのうち、ngOnInitについて簡単な実験をして、

どのタイミングでngOnInitが発火するのか確かめてみることにします。

前提

以下のような親子関係があるとします。

親Componentから子Componentへ、parentValueという値が渡されているとしましょう。

0. プロジェクトを立ち上げて、必要なComponentを生成する

ng new lifecycle-oninit
ng g component child

1. templateファイルを以下のように修正

<!-- app.component.html ->
<app-child [parentValue]="parentValue"></app-child>

親ComponentであるAppComponentから、parentValueを受け取るようにします。

2. ChildComponentにInputを追記

/** child/child.component.ts */
import {
  Component,
  Input, // 追記
  OnInit // 追記
} from '@angular/core';
export class ChildComponent implements
OnInit // 追記
{
  @Input() parentValue; // 追記

  /** ここから */
  constructor() {
    console.log(this.parentValue);
    console.log("@@@constructor");
  }

  ngOnInit() {
    console.log(this.parentValue);
    console.info("@@@ngOnInit");
  }
  /** ここまで追記 */
}

親からparentValueを受け取れるように、@Input()を追加します。

3. AppComponentにparentValueを追記

/** app.component.ts */
export class AppComponent
{
  public parentValue = '親の値だよ';
}

ログをみてみる

f:id:karoten512:20171003222416p:plain

ログを見てみると、

constructor → ngOnInit

の順で呼ばれていることがわかります。

また、ChildComponentが生成された時点(constructorが呼ばれた時点)では、

親からのInputをまだ受け取っていないことがわかります。

ngOnInitが実行された時点では、親からのInputを受け取っています。

つまり、ngOnInitが実行されるタイミングは、

Inputを受け取ったあと

ということになります。

Angular公式ドキュメントでは

Use ngOnInit() for two main reasons:

1. To perform complex initializations shortly after construction.
2. To set up the component after Angular sets the input properties.

上にあるように、

constructionの後に実行される「複雑な初期化」を行うときや、

inputプロパティがセットされた後にcomponentをset upする際に使われるようです。

参考:https://angular.io/guide/lifecycle-hooks

rake aborted! Gem::LoadError: You have already activated rake *.*.*, but your Gemfile requires rake **.**.**. の解決方法

rakeコマンドを実行したときに時々起こる、rake aborted! の解決方法

結論。

rake ***

ではなく、

bundle exec rake ***

を使いましょう。

解説

まずはエラー文を確認

今回のエラー文を見てみましょう。

Gem::LoadError: You have already activated rake 12.0.0,
but your Gemfile requires rake 11.2.2

この場合、

今使っているrakeコマンドのバージョンは12.0.0ですが、

Gemfileでは11.2.2のバージョンが指定されてますよ。

と伝えてくれています。

実は今回の場合、

rake ***

で実行された場合は12.0.0のバージョンが使われます。

bundle exec rake ***

で実行した場合は11.2.2のバージョンが使われます。

そしてこの場合はエラーが起きません。

なぜそういうことが起こるのか

rake ***

で実行した場合と、

bundle exec rake ***

で実行した場合とでは、 参照しているrakeが異なるからです。

それぞれどのrakeを参照しているのか

まずは

rake ***

がどのrakeを参照しているのか調べてみましょう。

gem environment

とすると、 色々出てくるのですが、

  - GEM PATHS:
     - /home/vagrant/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0
     - /home/vagrant/.gem/ruby/2.3.0

こちらのGEM PATHSにアクセスし、フォルダ内に入っているものをみてみましょう。

f:id:karoten512:20171002233821p:plain

大量のgemが見つかりました。

これらは、今まで

bundle install

してinstallしてきたgemたちです。バージョン違いのgemがいっぱいあって恐ろしいですね。

rakeはいくつあるのか

ls | grep rake

をすると、

f:id:karoten512:20171002233904p:plain

以上の3つのrakeが見つかりました。

rake *** を実行すると

この中の最新バージョンである12.0.0が実行されます。

なお、

rake -V

をすると、installされている最新バージョンが確認できます。

bundle exec rake *** を実行すると

「プロジェクトディレクトリ下のGemfile.lockに記載されているrakeのバージョン」が使用されます。

今回の場合は11.2.2ですね。

なお、

bundle exec rake --version

をすると、使用されるrakeのバージョンが確認できます。

解決策

プロジェクトで使っているrakeのバージョンと、異なるバージョンのrakeを使おうとするととエラーが起こるので、

基本的に

bundle exec rake ***

を使用するようにしましょう。

その他の解決策

gem install時にbundle install をするのではなく、

bundle install --path vendor/bundle

として、

gemはプロジェクト毎に管理しましょう(推奨)

こうすると、vendor/bundle内でgemが管理できるので、

複数のバージョンのgemが混在することがなくなります。

参考: http://dev.classmethod.jp/server-side/language/bundler-gem-management/

あなたのダイエットはなぜ続かないのか。行動心理学から考えてみる

ダイエットは本当につづかないのか?

ダイエットって、続かないですよね。

会社帰りにラーメン食べちゃいますよね。

ジム契約してもなんだかんだで行かなくなっちゃいますよね。

そんな続かないダイエットについて、

どうやったら続くのか

を行動心理学から紐解いてみました。

ちなみに行動心理学のお陰で、ダイエットが続くようになりました。

今のところ2ヶ月で4キロほどおちてきています。目指せ学生時代の体重。

強化と弱化

行動心理学で、強化・弱化という言葉があります。

強化

強化とは、「行動が繰り返されるようになる」ことを指します。

弱化

弱化とは、「行動がされなくなっていく」ことを指します。

今回の目的

つまり今回の目的は、

  • ジムへ行くという行動を強化

  • ラーメン食べるという行動を弱化

ということになります。

ジムへ行く行動を強化するには

まず強化には以下の2種類があることを知っておきましょう。

  • 好子出現の強化

f:id:karoten512:20170930233146p:plain

このように、

行動の結果、自分にとって好ましい状況(好子)が出現すると、

その行動を繰り返すようになります。これを「好子出現の強化」と言います。

f:id:karoten512:20170930233717p:plain

逆に行動の結果、自分にとって好ましくない状況(嫌子)が消失すると、

その行動を繰り返すようになります。これを「嫌子消失の強化」と言います。

つまり、この法則を満たすようにすれば、

ジムへ行くという行動が強化されることになります。

f:id:karoten512:20170930234003p:plain

みんなも考えてみよう(棒読み)

ラーメン食べるという行動を弱化するには

まず、弱化には以下の2つがあることを知っておきましょう。

  • 好子消滅の弱化

f:id:karoten512:20170930234800p:plain

まあこの例はあまり良くないですね。

寂しい→浮気する→寂しくない

で「好子出現による強化」のパターンもよくあります。今回は気にしないことにします。

f:id:karoten512:20170930234817p:plain

これは説明いらないかな。

つまり、この法則を満たすようにすれば、

ラーメン食べるという行動が弱化されることになります。

f:id:karoten512:20170930234214p:plain

後はもう簡単ですね。

僕はどうしたか

  • 美人インストラクターのいるジムに通った

f:id:karoten512:20170930235039p:plain

  • 近所にある、食べると絶対に腹をこわすラーメン屋に通い続けた(弱化されていった)

f:id:karoten512:20170930235156p:plain

以上です。

向き不向きがあるので自分にあった方法を考えてみましょう。

試行錯誤です。

補足

ラーメンで腹を壊し続けたせいで体重が減ってる疑惑。

Angular4にて、サービスを介してコンポーネントからコンポーネントへ値を送る

コンポーネントから別のコンポーネントに値を送る

親子関係があるときは、@Input()を使用することにより親から子へ値を送りました。

今回、あるコンポーネントから親子関係にないコンポーネントへ、

サービスを介して値を送る実装をしてみたいと思います。

解説

具体的な手順に入る前に、大体の流れを確かめておきましょう。

今回やりたいことを図にすると、こんな感じになります。

f:id:karoten512:20170929160242p:plain

Serviceを介して、ComponentAからComponentBへMessageを送る感じです。

これを実現するために、まずServiceクラス内に「privateの口」「publicの耳」を用意します。

f:id:karoten512:20170929160427p:plain

次に、ComponentBにpublicの耳を配置します。

この時、耳を澄ませておくことが重要です(subscribe)

f:id:karoten512:20170929160902p:plain

これで準備が整いました。

まず、ComponentAがServiceに以下のように依頼します。

f:id:karoten512:20170929160907p:plain

次に、Serviceがこんにちは!と叫びます。

f:id:karoten512:20170929161014p:plain

そうすると、耳を澄ませていたComponentBに聞こえます。

f:id:karoten512:20170929161101p:plain

なにいってんだこいつ、という感じだと思いますので、

実際にコードを見て確認してみましょう。

0. 準備

新規プロジェクトを作成し、サーバをスタートさせます。

ng new send-value
ng serve

1. componentA, componentB, SendValueServiceを作成

ng g component comp-a
ng g component comp-b
ng g service send-value

2. SendValueServiceにてSubjectを読み込む

// send-value.service.ts
import { Subject } from 'rxjs/Subject';

3. SendValueServiceにprivateの口を追加

// send-value.service.ts
private toCompBDataSource = new Subject<string>();

4. SendValueServiceにpubilcの耳を追加

// send-value.service.ts
public toCompBData$ = this.toCompBDataSource.asObservable();

5. app.moduleににSendValueServiceをDIする

// app.module.ts
import { SendValueService } from './send-value.service'; // 追加

@NgModule({
  providers: [SendValueService], // 追加
}) 

SendValueServiceをDIするのはapp.moduleのみです。

compA, compBにもDIしてしまうと、

3つの別々のSendValueServiceインスタンスが出来てしまうので、うまく動きません。

サービスを使ってコンポーネント間で値のやり取りをするときは、

「同一のServiceインスタンス」を介して値のやり取りができるように注意しましょう。

今回はmoduleにDIしていますが、2つのコンポーネントが同一の親を保つ場合は、

その親にDIしてもOKです。

6. compBのコンストラクタにSendValueServiceを記述

// comp-b.component.ts
import { SendValueService } from '../send-value.service'; // 追加

export class CompBComponent implements OnInit {
  constructor(
    private sendValueService :SendValueService // 追加
  ) { } 

  ngOnInit() {
  }
}

7. pubilcの耳を澄まさせる(subscribe)

// comp-b.component.ts
import { Subscription } from 'rxjs/Subscription'; // 追加

export class CompBComponent implements OnInit {
  private subscription: Subscription; // 追加

  constructor(
    private sendValueService :SendValueService
  ) {

  }
 
  ngOnInit() {
    /** ここから */
    this.subscription = this.dataService.toCompBData$.subscribe(
      msg => {
        console.log('compBは' + msg + 'をうけとりました');
      });
    }
    /** ここまで追加 */
}

8. SendValueServiceに喋りを実行するメソッドを追加

// send-value.service.ts
  public sendMessageToCompB(msg)
  {
    this.toCompBDataSource.next(msg); // 喋る
  }

9. compAのコンストラクタにSendValueServiceを記述し、喋らせるメソッドを追加

// comp-a.component.ts
import { SendValueService } from '../send-value.service';

export class CompAComponent implements OnInit {

  constructor(
    private sendValueService :SendValueService // 追加
  ) { } 

  /** ここから */
  onClick() :void
  {
    this.sendValueService.sendMessageToCompB('こんにちは!');
  }
  /** ここまで追加 */
}

10. componentAのtemplateにボタンを追加

// comp-a.component.html
<button (click)="onClick()">クリックしてね</button>

結果

f:id:karoten512:20170929164624g:plain

パティシエが教えるgithub flowによるチーム開発

パティシエが教えるgithub flowによるチーム開発

※当方バックエンドエンジニアです(not パティシエ)

業務系の会社で働く傍ら、プログラミングスクールにて主に未経験の方にプログラミングを教えております。

この間、github flowによるチーム開発について説明したのですが、

リポジトリを「ケーキ」にたとえて説明したところなかなか評判が良かったので、

記事にしてみようと思います。

1. パティシエ長が管理するmasterケーキをcloneします

f:id:karoten512:20170929095342p:plain

パティシエ長が、最終的にお客さんに売りに出すケーキである「masterケーキ」を管理してます。

f:id:karoten512:20170929095406p:plain

ますはそのmasterケーキを自分の手元に持ってきます(git clone)

2. 自分がいじるケーキをつくります

f:id:karoten512:20170929095529p:plain

今、手元には複製されたmasterケーキがあります。

シェフ長から、「Aさんはケーキにいちごを乗せてくれ」と頼まれたとします(issue)。

masterケーキをそのまま残したまま、

git checkout -b issue#ichigo

として、

いちごを乗せる作業を行うための、自分だけがいじるケーキを作ります。

f:id:karoten512:20170929095547p:plain

他の3人についても同様です。

f:id:karoten512:20170929095606p:plain

3. ケーキをいじります

それぞれのケーキをいじります。

Aさんはケーキにいちごを乗せ、

Bさんはりんごを乗せ、

Cさんはパイナップルを乗せます。

(どんなケーキだ。。。)

4. できあがったケーキをパティシエ長に提出

f:id:karoten512:20170929095628p:plain

git push origin issue#ichigo:issue#ichigo

をして、パティシエ長にいちごが乗ったケーキを提出します。

この時、githubの機能である「pullリクエスト」を一緒に行います。

f:id:karoten512:20170929095648p:plain

Bさん、Cさんについても同じです。

5. パティシエ長がそれぞれのケーキをチェック

f:id:karoten512:20170929095711p:plain

パティシエ長が、提出されたケーキをチェックします。

変な位置に果物が載ってないか、果物が腐ってないかなど、

要件を満たしているか・バグがないコードなのかどうかをチェックします。

6. パティシエ長がマージ

f:id:karoten512:20170929095727p:plain

提出されたケーキが問題ないようであれば、

それぞれのケーキのmasterとの差分を取り込みます。

今回で言えば、

いちご、りんご、パイナップルがmasterケーキの上に乗ることになります。

これが「マージ」と呼ばれる作業に当たります。

マージする際にいちごとりんごが同じ位置に載ってしまった場合は、

コンフリクト」と言って、

パティシエ長が頑張って修正したり、AさんやBさんに修正依頼を出したりしてうまく調整します。

これを繰り返して、ちょっとずつmasterケーキを充実させていくことがシステム開発になります。

うまくissueを割り振らないと、マージするときに大変なので注意しなくてはなりません。

Angular4からgoogle mapを操作する「AGM (angular-google-maps)」を使ってmapを表示。

Angular4を使ってgoogle mapを表示する

google map上でクリックしたときにマーカをプロットし、その位置情報をAjaxで投げてDB上に保存する」ということがしたくなりました。

google map上での操作については「Maps JavaScript API」を使用すればできるのですが、 生jsで書くとマーカや情報ウィンドウの管理が煩雑になりそうです。

そこで、「AGM (angular-google-maps)」というmoduleを使うことにしました。

今回はmapを表示させるところまでやってみようと思います。

0. 準備

新規プロジェクトを作成し、サーバをスタートさせます。

ng new angular-google-maps
ng serve

1. angular-google-mapsのパッケージをinstall

npm install --save-dev @agm/core

moduleのgithubはこちら。 https://github.com/SebastianM/angular-google-maps

2. 必要なmoduleを読み込む

// app.module.ts
import {
  AgmCoreModule,
  GoogleMapsAPIWrapper
} from '@agm/core';

3. @NgModuleのimportに、読み込んだModuleを追加

// app.module.ts
@NgModule({
  // ...
  imports: [
    AgmCoreModule.forRoot({
      apiKey: 'googleから取得したapi key'
    }),
  ]
// ...
});

4. app.component.htmlに、mapを読み込むためのdirectiveを記述

<agm-map
  [latitude]="lat"
  [longitude]="lng"
  [zoom]="zoom"
  >
</agm-map>

5. app.component.tsに、mapの初期設定(緯度/経度等)を記述

// app.component.ts
export class AppComponent {
  lat: number = 35.681167;
  lng: number = 139.767052;
  zoom: number = 16;
}

6. app.component.cssに、mapの高さを記述(忘れがち)

// app.component.css
agm-map {
  height: 600px;
}

これを書かないとmapが表示されません(どハマリした)。

結果

f:id:karoten512:20170925185904p:plain