あいつの日誌β

働きながら旅しています。

Can't bind to 'ngModel' since it isn't a known property of 'input'. ("">

もう何度も同じメッセージを目撃し、時間を無駄にしているのでメモ

src/app/app.module.ts に FormsModule を追加する

 import { BrowserModule } from '@angular/platform-browser';
 import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
 
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
@@ -10,7 +11,8 @@ import { AppComponent } from './app.component';
   ],
   imports: [
     BrowserModule,
-    AppRoutingModule
+    AppRoutingModule,
+    FormsModule
   ],
   providers: [],
   bootstrap: [AppComponent]

chart.js v2.6.0 の tooltip を React.js でカスタマイズして使う

追記

そういえば tootlitp のデザインは下記URL から拝借しています。書き忘れていたので追記します。ちなみにこの CSS にドロップシャドウを追加したらシャレオツな感じになった気がします。

【5パターン】画像を使わず CSS3 のみで作れる吹き出しを作ってみた – Pure CSS3 Balloons | Stronghold Archive

あらすじ

Chart.js の tooltip をカスタマイズする必要があったので、その勉強した結果をメモしておきます。

どんなの?

こんなの

f:id:okamuuu:20170704222524p:plain

準備

react-storybook 上で演習します

create-react-app practice-react-chartjs && cd $_
getstorybook
npm run storybook

open http://localhost:6006 して以下の画面が表示されれば準備完了です

演習開始

chart.js をinstlall

npm install --save chart.js

シンプルな Bar Chart を作成します。

create src/components/bar-chart.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Chart } from 'chart.js'

class BarChart extends Component {

  componentDidMount() {
    const ctx = this.node.getContext('2d')
    const chart = new Chart(ctx, {
      type: "bar",
      data: {
        labels: ["好き", "嫌い", "どちらでもない"],
        datasets: [{
          label: "りんご",
          data: [ 5, 1, 4]
        },
        {
          label: "なし",
          data: [ 8, 1, 1]
        },
        {
          label: "みかん",
          data: [ 2, 1, 7]
        }],
      },
      options: {
        scales: {
          yAxes: [{
            display: true,
            ticks: {
              beginAtZero: true,
              max: 10,
            },
          }],
        }
      }
    })
  }

  render() {
    return (
      <div>
        <canvas ref={(node) => this.node = node}></canvas>
      </div>
    )
  }
}

export default BarChart

edit stories/index.js

import React from 'react';
import { storiesOf, action, linkTo } from '@kadira/storybook';
import BarChart from '../src/components/bar-chart'

storiesOf('Welcome', module)
  .add('BarChart', () => (
    <BarChart />
  )); 

tooltip component を作成する

css in js にします。content に空文字を設定する時は "' '" のようにしましょう。

npm install glamor --save

create src/components/tooltip.js

import React, { Component } from 'react'
import { css } from 'glamor'

let rule = css({
  position: "relative",
  display: "inline-block",
  padding: "0 15px",
  width: "auto",
  minWidth: "115px",
  height: "40px",
  lineHeight: "34px",
  color: "#19283C",
  textAlign: "center",
  background: "#F6F6F6",
  border: "3px solid #19283C",
  boxShadow: "2px 1px 8px 0 rgba(0,0,0,0.3)",
  zIndex: 0,
  ":before": {
    content: "' '",
    position: "absolute",
    bottom: "-8px",
    left: "50%",
    marginLeft: "-9px",
    width: "0px",
    height: "0px",
    borderStyle: "solid",
    borderWidth: "9px 9px 0 9px",
    borderColor: "#F6F6F6 transparent transparent transparent",
    zIndex: 0
  },
  ":after": {
    content: "' '",
    position: "absolute",
    bottom: "-12px",
    left: "50%",
    marginLeft: "-10px",
    width: "0px",
    height: "0px",
    borderStyle: "solid",
    borderWidth: "10px 10px 0 10px",
    borderColor: "#19283C transparent transparent transparent",
    zIndex: -1
  }
})

class Tooltip extends Component {

  componentDidMount() {
  }

  render() {
    return (
      <div className={`${rule}`}>{this.props.children}</div>
    )
  }
}

export default Tooltip

storybook に追加する edit: story/index.js

import React from 'react';
import { storiesOf, action, linkTo } from '@kadira/storybook';
import BarChart from '../src/components/bar-chart'
import Tooltip from '../src/components/tooltip'

storiesOf('Welcome', module)
  .add('BarChart', () => (
    <BarChart />
  ))  
  .add('Tooltip', () => (
    <Tooltip>test</Tooltip>
  ));

Chart.js の tooltip option で挙動を確認

試しに Chart.js に以下のオプションを追加してマウスをグラフに hover させると tooltip のイベントが発生します。ここで取得した情報を先ほどの Tooltip に渡します。

+++ b/src/components/bar-chart.js
@@ -32,7 +32,17 @@ class BarChart extends Component {
               max: 10,
             },
           }],
-        }
+        },
+        tooltips: {
+          enabled: false,
+          mode: 'nearest',
+          position: 'nearest',
+          intersect: false,
+          custom: function(tooltip) {
+            console.log(this._chart, tooltip)
+          }
+        },
       }
     })

tooltip の表示、非表示については tooltip.opacity で判定できます。

tooltip の位置については tooltip.caretXthis.tooltip.caretY で相対的な位置がわかります。 これらと this._chart.canvas.getBoundingClientRect() を組み合わせることで計算することができます。

tooltip の表示位置を position: absolute にして以下の style を操作することで実現させます。

  • el.styleopacity
  • el.style.left
  • el.style.top

tooltip に表示する要素は tooltip.body[0].lines を参照すれば取得できます。

tooltip component をカスタマイズ

というわけで tooltip component に対して以下の props を渡せるようにします。

el.style.opacity el.style.left el.style.top text

動作確認をする際、 react-storybook には Knobs という便利な Addon があるのでこれを使います。が、使い方は割愛します。

edit src/components/tooltip.js

   render() {
+    const { opacity, top, left, children } = this.props;
     return (
-      <div className={`${rule}`}>{this.props.children}</div>
+      <div style={{position: "absolute", opacity, top, left}}>
+        <div className={`${tooltip}`}>{children}</div>
+      </div>
     )

位置情報を計算する

公式サイトに計算方法が載ってあるのでこの計算式を流用します。

http://www.chartjs.org/docs/latest/configuration/tooltip.html

例えば以下のようにします。chart.canvas.getBoundingClientRect() を実行すると canvas の位置がとれるのでそれを使用します。ただし、scroll するとずれるので注意。

正直ここの計算式はどれが正しいのかわかってないのですがたぶんこの計算式でまあまあ良い感じになると思います。

function customTooltip(canvas, tooltipModel) {
  if (tooltipModel.opacity === 0) {
    return {opacity: 0, left: 0, top: 0, text: ""} 
  }
  // const position = chart.canvas.getBoundingClientRect()
  // console.log(position.left, position.right)
  return {
    opacity: 1,
    // left: caret - tooltip の幅 / 2 + potion.left
    // top: caret - tooltip の高さ + potion.top
    left: tooltipModel.caretX - 151 / 2 + 8,
    top: tooltipModel.caretY - tooltipModel.height - 8,
    text: tooltipModel.body[0].lines.join("")
  }
}

あとは BarChart Compnent に onTooltipFire というイベントハンドラを仕込んで受け取った情報を Tooltip に渡せば良いです。

疲れてきたので後は github に上げておきます。興味がある方はご覧頂き、宜しければ⭐️でも付けてやってください

ソースコード

https://github.com/okamuuu/react-chart.js-example

storybook

https://okamuuu.github.io/react-chart.js-example/

ng test で Can't bind to 'routerLink' since it isn't a known property of 'a'. のような事を言われる場合

xxxx.component.spec.ts に以下のような追記をすると良いかもしれません。

+ import { RouterTestingModule } from '@angular/router/testing';


   beforeEach(async(() => {
     TestBed.configureTestingModule({
       ...
+      imports: [RouterTestingModule]
     })
     .compileComponents();

Port が is already in use. と言われた時にどのプロセスの事を言っているのか MacOSX で調べる

もし Port が 4200 だった場合は以下のようにすればOK

% lsof -n -i4TCP:4200 | grep LISTEN
node      36069 okamuuu   17u  IPv4 0xce7dba5cd8807a8b      0t0  TCP 127.0.0.1:4200 (LISTEN)

おしまい

インディー系テックカンファレンス、Y8(通称ヤパチー) で登壇しました

というわけで渋谷のマークシティに行ってまいりました。会場の雰囲気はこちらを参考にしてください。

togetter.com

インフラエンジニアのWEBアプリ入門のススメ by おざしゅー

書くと動くので楽しい。楽しいのでもっと動かしたい。もっと知りたい。そんないい話が聞けました。一度書くのをやめるともう一度書き始めるの大変なので楽しいっていう感覚大事だなとおもいました。

発表資料: https://speakerdeck.com/ozashu/inhuraenziniafalsewebapuriru-men

OWASP Top 10 2017 RC について by mahoyaya

2013年と2017年で変更されたのは以下

  • アクセスコントロールの不備
  • 不十分な防御機能
  • 保護されていない API

そして本物はすごい

PMを目指して1年が経った by expa(えくすぱ、と読みます)

技術者としての自分を蔑ろにしいてしまい、望まないルートを歩むところだったという反省を是非生かして頂きたいと思いました。

WordPress のサイトを Rails にしたはなし by motchang

uzulla さんの前でこんな話をしますが...という前置きでしたが非常に面白い話でした。 質疑応答では「データがどうにもならないのでエンジニアとしては早くここから抜け出したい」という発言が印象に残りました。

チームで取り組む Singe Page Application by オカムラ

私の発表内容です。今後WEBがリッチ化してUI/UXが重要になってくるので早い段階でフロントエンドがデザイナーの力を借りてプロダクトを開発する体制をととのえましょう。

という話をしたかったのですが...なんだか拙い発表になってしまいました。次の成功の栄養になりました。

speakerdeck.com

お昼休み

1時間ほど休憩

hyperapp – 1kbのビューライブラリ by Jorge Bucaran (ジョージ)

hyperapp の作者が登場。個人的にはミニマムなツールが好きなので好みです。 質疑応答で非同期処理はどうするのか?という質問があったのですが asyncAction が setAction を呼ぶ。みたいな回答だった気がします。たぶん。 あとAPIはどれぐらいの頻度で壊れるのか?という質問に対してもうAPIは固まっているとのことです。

MySQLサーバーのパフォーマンスチューニング by まみー

WEBアプリ開発者からインフラエンジニア

新しい分野の仕事に挑戦するとアウトプットが増えるから楽しい。本当そうですよね。アウトプットする材料が一杯手に入っているので私も頑張らないと...

.NET Core がLinuxでどのように動いているか、またわれわれはどのようにデバッグするのか by たなか

そもそも LLDB の使い方が分かってなくてすいません。

条件付きのブレイクポイントはつけられますか? => できます 変数の書き換えは? => どうなんでしょう?

ScalaでウェブAPIを書いている人が設計や実装やその他について話そうか by たっくん

有用なドメインモデルの獲得はまだまだ難しいとの事。理論を重ねながらも実装がなかなか良い状態へ持ち込めないジレンマを感じました。週1の定例でユビキタス言語を振り返るというのは良いことだなと思いました。

OSSの敵なっちゃうのもいいじゃない by lestrrat

飛び込みで参加 buildscone への応募を待っているとの事。slack の中の人がくるらしい。WEB+DB で GO の連載をしているので読んでくださいとのこと。

トーク内容は4時間ぐらいで書いたそうです。PRを送らずにforkする理由は API が気に入らないから。fork したことによって知識も増えオリジナルとコードについて議論できる。若者よもっとコードを書け。私はGoは5万行書いた時点で理解した。5という数字にきっと深い意味があるんでしょうね。

V8 for フロントエンドデベロッパー by brn(ブルーノ)

V8 エンジンについて解説。趣味で追いかけているそうです。

Q: コードを読むのにコツがあるのか?

A: コードだけを読むのは難しいので有識者などから知識を授かるのも大事。初期のコードとは随分様変わりしているとのこと。

逆にいうと興味がある人は初期のコードを探して読むと理解がはやのかもしれません。

なぜ、PHPのmbstring.func_overloadをdeprecatedにするのに5年かかったのか? - 慢心、環境の違い by sasezaki

トークの内容はmbstring.func_overload やめたほうがいいじゃないのか?という issue を立てて賛同者もいたが、いや使いたいんだという PR が出たり、いや deprecated にするんだというやりとりが長く続いた経緯を紹介してくれました。

ある無名エンジニアがAbemaTV iOSチームに加入するまでの道のり by satoshi0212

時間を作り、人に会って、動き回った。今に見ていろ精神。そして振り返れば完遂できた目標はすべて締め切りがあった。時間はお金より大事。そう思うことは簡単ですがその通りに実践するのはとても難しいことです。お見事です。

閉会式とベストスピーカーの発表

というわけでベストスピーカーは「WordPress のサイトを Rails にしたはなし by motchang」さんでした。

1: WordPress のサイトを Rails にしたはなし by motchang

2: V8 for フロントエンドデベロッパー by brn(ブルーノ)

3: hyperapp – 1kbのビューライブラリ by Jorge Bucaran (ジョージ)

個人的なベストスピーカー

私は「PMを目指して1年が経った」と「なぜ、PHPのmbstring.func_overloadをdeprecatedにするのに5年かかったのか? - 慢心、環境の違い」が良かったと思います。

PMを目指して1年が経った

聞いていて学ぶ事がたくさんあるお話でした。この1年は無駄な時間じゃないと思います。

deprecatedにするのに5年かかった

私たちは色々な道具を使ってお金を頂いていますが影でその道具が正しく使えるようにメンテナーの知らざる努力があるのを思い出させてくれました。

Second Season

懇親会しつつさらに発表が続く Second Season が行われましたが私は不参加です。

しんぺいさんとの View Library について雑談

お昼休みにしんぺいさんと雑談。Vue.js も便利そう。HTMLコーダーやWEBデザイナーが直感的にコンポーネントを書きやすいかも。react-storybook みたいなの他の View ライブラリでも欲しいんですよね。という話をしました。

そんな事言いながら私は今 Angular4 の仕事をしていますが...

ちなみに

登壇者が撮影された写真は大体発表前に撮影されます。 登壇者が発表している最中は前を向いてくれないから写真撮影するのは難しいそうです。

こちらからは以上です。