Symfony4 + WebpackEncoreBundle + Bootstrap4な環境でdatetimepickerを使う手順
こんにちは、たつきちです。
タイトルのとおりですが、Symfony4 + WebpackEncoreBundle + Bootstrap4 な環境でdatetimepicker的なものを使いたくてちょっと調べたので必要な手順をまとめておきます。
ちなみに、Symfony4 + WebpackEncoreBundle + Bootstrap4 の環境を簡単に作成できる便利なスケルトンがあるので、次回何か作るときはぜひ使ってみてください。(ステマ)
1. どのライブラリを使うかを決める
さて、とりあえずBootstrapベースのdatepicker/datetimepickerを適当にググってみました。
その結果、
あたりがスター数的に有名どころっぽかったんですが、いずれもBootstrap3ベースで、Bootstrap4には対応していないようでした。
さらにググったところ、 こちらの記事 を見つけ、どうやら
を使うのがよさそうということが分かりました。
今回はこれを使うことにします。
2. インストール
普通にnpmでインストールするんですが、jQuery に加えて Moment.js にも 依存している ので、こちらもあわせてインストールする必要があります。(jQueryはBootstrapが依存しているのですでに入っている前提で)
$ npm i -D tempusdominus-bootstrap-4 moment
3. リソースの読み込み
インストールできたら、必要なリソース(js/css)をEncoreに読み込ませます。
ここでは例として、 ttskch/symfony-micro-skeleton を使っている場合の /webpack.config.js
の内容をベースにdiffを載せてみます。
const Encore = require('@symfony/webpack-encore');
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.cleanupOutputBeforeBuild()
.autoProvidejQuery()
.autoProvideVariables({
Popper: ['popper.js', 'default'],
+ moment: ['moment'],
})
.enableSassLoader()
.enablePostCssLoader()
.enableVersioning(Encore.isProduction())
.enableSourceMaps(!Encore.isProduction())
.addEntry('app', [
'./assets/js/app.js',
'./assets/scss/app.scss',
])
.addEntry('vendors', [
// js
'jquery',
'bootstrap',
'popper.js',
+ 'moment',
+ 'tempusdominus-bootstrap-4',
// styles
'./assets/scss/vendors.scss',
+ './node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.css',
])
.splitEntryChunks()
.enableSingleRuntimeChunk()
.copyFiles({
from: './assets/images',
to: 'images/[path][name].[ext]',
})
;
module.exports = Encore.getWebpackConfig();
やっていることは、
-
addEntry
でmoment
とtempusdominus-bootstrap-4
のjsファイルを読み込ませる - 同じく
addEntry
でtempusdominus-bootstrap-4
のcssファイルも読み込ませる(※) -
autoProvideVariables
で、グローバルにmoment
を追加する
です。
2については、ここで読み込む方法の他に、既存のscssファイル内で
@import "~tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.css";
とする方法でもOKです。
ttskch/symfony-micro-skeleton を使っている場合なら、 ここ に追記すればいいですね。
また、3についてですが、これは tempusdominus-bootstrap-4
がグローバル空間に moment
が存在していることを前提にしているために必要になる対処です。これをやらないと
Uncaught Error: Tempus Dominus Bootstrap4's requires moment.js. Moment.js must be included before Tempus Dominus Bootstrap4's JavaScript.
といったエラーになります。( 参考 )
4. アセットをリビルド
忘れずにアセットをビルドしなおしましょう。
encore dev --watch
しながら作業していた人もいるかもしれませんが、 webpack.config.js
を書き換えたのでwatchプロセスも再起動する必要があります。
5. 使う
ここまで来たらあとは使うだけです。
使い方も基本的には 公式ドキュメント を見ればだいたい分かるようになっています。
今回は例として、
- ボタンは無しで、テキストフィールドにフォーカスするとカレンダーが出現する
- 「年月日」をカレンダーから選択すると
2020-01-31
といった形式の文字列が入力される
という仕様の実装をしてみましょう。
Symfony Formと合わせて使う
せっかくなのでここでは Symfony Form を使っている想定で説明しましょう。
FormType
$builder
->add('date', TextType::class, [
'label' => '年月日',
'attr' => [
'pattern' => '\d+-\d+',
],
])
View
{{ form_start(form) }}
{{ form_row(form.date, {attr: {'data-toggle': 'datetimepicker', 'data-target': '#'~form.date.vars.id}}) }}
{{ form_rest(form) }}
js
$('input要素のセレクター').datetimepicker({
locale: 'ja',
format: 'YYYY-MM-DD',
viewMode: 'days',
});
こんな感じの実装で、以下のようなUIが手に入ります!
ポイントとしては、
- 念のため
input
要素のpattern
属性を使って簡易的にバリデーション -
input
要素自身にdata-toggle
とかdata-target
とかを設定する必要がある(ので、form
系のtwigタグの記述がやや煩雑になる…) - jsで使えるオプションは ここ にすべて書いてある
-
format
にはPHPではなくmoment互換の書式を指定する( 参考 ) -
viewMode
で「年だけ選ぶ」とか「年月だけ選ぶ」とか色々設定できる( 参考 ) - フロント側ではあくまで文字列として扱われるので、バックエンドに送る際には Data Transformer を使って文字列を
\DateTime
オブジェクトに自力で変換するなどの対応が必要
といったところでしょうか。
余談
ちなみに、FormTypeの設定を
$builder
->add('date', DateType::class, [
'label' => '年月日',
'widget' => 'single_text',
])
とすることで <input type="date">
が出力され、Data Transformerを使うまでもなく普通に送信すれば \DateTime
型で受け取ってくれるようにもできます。
しかし、これだと
- ブラウザ標準のdatepickerも表示されてしまうのでユーザーの混乱を招く
- 「年月日」の場合はいいけど、例えば「年月」だけを扱いたいような場合にも、入力欄には
2020/01/01
のように日付まで表示せざるを得ずイマイチ
といった難点があります。状況に応じて使えそうなら使ってみてください。
なお、この場合、フォーマットは YYYY-MM-DD
一択になります。この形式の文字列でないと、 <input type="date">
に反映されません。
年月のみを扱うような場合には、 YYYY-MM-01
といったフォーマット指定をする必要があるでしょう。
まとめ
- 使うライブラリは tempusdominus/bootstrap-4 これがよさそう
- Moment.jsに依存しているのでインストール時に多少注意が必要
- ドキュメントが充実してるので使い方を学ぶのは比較的簡単
- 見た目は正直そこまで美しくないので、気に入らなければcss書きましょう
- ttskch/symfony-micro-skeleton 使ってね!
Discussion