Help us understand the problem. What is going on with this article?

Bootstrap 4でDatetimePickerを使いたい

More than 1 year has passed since last update.

はじめに

Delphi + ActiveXで作られた既存Webアプリケーションの仕様違い版を新規作成することになり、この際なので、ASP.NET + Bootstrap 4 で作成することにしました。
表入力形式や一覧表示にはスプレッドシートの「Handsontable」を使うとして、カレンダー入力は当初「Pikaday.js」を考えていましたが、画面設計に時間入力があり「DatetimePicker.js」ならカレンダー入力と時間入力の2つの機能を満たせるため、こちらに変更しました。

時間入力は、既存Webアプリケーションを踏襲した古い画面デザインで時間コンボボックスと分コンボボックスで入力する設計になっていたため、DatetimePickerの時間入力を使った今風なWebデザインに変更することにしました。

Bootstrap 4 DatetimePicker

Bootstrap DatetimePicker は、Bootstrap 3までとなっています。
https://eonasdan.github.io/bootstrap-datetimepicker/

Bootstrap 4でも使えると思ったのですが、Bootstrap 4 と Bootstrap 3 では変更点が多く動作させることができませんでした。
Bootstrap 3でDatetimePickerを使いたい

世界中の誰かが対応しているんじゃないかとネットで調べて見つけました。
https://github.com/pingcheng/bootstrap4-datetimepicker
Customizable Date/Time Picker Component For Bootstrap 4 (デモ)

【2018/07/14追記】
なんとISSUEまで書いたのに、本家の本当の後継を見つけてしまいました。当然、前月と次月のツールチップスの不具合もありません。

https://github.com/tempusdominus/bootstrap-4
Customizable Date & Time Picker For Bootstrap 4 (デモ)

同じ作者がBootstrapに依存しないDatetimePickerも作成しています。
https://github.com/tempusdominus/datetimepicker

以下の記事は、最初に見つけたpingcheng氏のbootstrap4-datetimepickerのままですが、日本語化などは同じように使えると思います。

日本語化

Customizable Date/Time Picker Component For Bootstrap 4からDownloadしたファイルを日本語向けに変更しました。

カレンダー表示を日本語向けにするには、moment.jsのローカル版が必要となるため、「moment.js/2.22.2/locale/ja.js」を追加し、ついでにバージョンも最新にしました。全ての言語対応版の「moment-with-locales.min.js」でも動作します。
https://cdnjs.com/libraries/moment.js/

カレンダー表示時に土日に色が付くようCSSを設定してあります。

DataTimePicker.png
DataPicker.png
TimePicker.png

プログラム(pingcheng氏版)

※ToolTipsの日本語名は最低限にしてありますので、必要なのは追加してください。

index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Date/Time Picker Component For Bootstrap 4 Demo</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.0.0/flatly/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="build/css/bootstrap-datetimepicker.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/ja.js" type="text/javascript"></script>

<script src="build/js/bootstrap-datetimepicker.min.js"></script>
<style type="text/css">
    .datepicker-days th.dow:first-child,
    .datepicker-days td:first-child {
        color: #f00;
    }
    .datepicker-days th.dow:last-child,
    .datepicker-days td:last-child {
        color: #00f;
    }
</style>
</head>

<body>
</br>
<div class="container">
    <div class="row">
        <div class="form-group">
            <div class="input-group date" id="datetimepicker1">
                <label for="datetimepicker1" class="pt-2 pr-2">日付:</label>
                <input type="text" class="form-control" />
                <span class="input-group-append">
                    <span class="input-group-text"><i class="fa fa-calendar"></i></span>
                </span>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <div class="input-group date" id="datetimepicker2">
                <label for="datetimepicker2" class="pt-2 pr-2">時間:</label>
                <input type="text" class="form-control" />
                <span class="input-group-append">
                    <span class="input-group-text"><i class="fa fa-clock-o"></i></span>
                </span>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript">
    $(function () {
        $('#datetimepicker1').datetimepicker({
            dayViewHeaderFormat: 'YYYY年 MMMM',
            tooltips: {
                close: '閉じる',
                selectMonth: '月を選択',
                prevMonth: '前月',
                nextMonth: '次月',
                selectYear: '年を選択',
                prevYear: '前年',
                nextYear: '次年',
                selectTime: '時間を選択',
                selectDate: '日付を選択',
                prevDecade: '前期間',
                nextDecade: '次期間',
                selectDecade: '期間を選択',
                prevCentury: '前世紀',
                nextCentury: '次世紀'
            },
            format: 'YYYY/MM/DD',
            locale: 'ja',
            showClose: true
        });
        $('#datetimepicker2').datetimepicker({
            tooltips: {
                close: '閉じる',
                pickHour: '時間を取得',
                incrementHour: '時間を増加',
                decrementHour: '時間を減少',
                pickMinute: '分を取得',
                incrementMinute: '分を増加',
                decrementMinute: '分を減少',
                pickSecond: '秒を取得',
                incrementSecond: '秒を増加',
                decrementSecond: '秒を減少',
                togglePeriod: '午前/午後切替',
                selectTime: '時間を選択'
            },
            format: 'HH:mm',
            locale: 'ja',
            showClose: true
        });
    });
</script>
</body>
</html>

補足

ToolTipsの前月 or 前年や次月 or 次年が表示されません。本家は表示されるので、Bootstrap 4対応の不具合と思われます。

Pull requestで指摘されていますが、カレンダー表示ボタンをクリックしてもカレンダーが表示されません。
Fix bootstrap 4 css class #6
これはBootstrap 4では「input-group-addon」ではなく「input-group-append」に変わったのですが、現段階でダウンロードしたファイルには反映されていませんでした。

今回はプログラムは変更しないで、classに両方書くことで対応しました。

<span class="input-group-append input-group-addon">
    <span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>

【2018/07/14追記】
最新版では指摘されたPull requestがマージされ「input-group-addon」ではなく「input-group-append」に変更になりました。
よって、「input-group-append」のみでいいです。ソースも反映しました。

<span class="input-group-append">
    <span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>

【2018/07/08追記】
Bootstrap 4対応版は本家と色を微妙に変えているようで、選択日は本家の方が自分は好みなので、CSSに追加してみました。

<style type="text/css">
    .datepicker-days th.dow:first-child,
    .datepicker-days td:first-child {
        color: #f00;
    }
    .datepicker-days th.dow:last-child,
    .datepicker-days td:last-child {
        color: #00f;
    }

    .bootstrap-datetimepicker-widget table td.active,
    .bootstrap-datetimepicker-widget table td.active:hover {
      background-color: #337ab7;
      color: #fff;
      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    }

    .bootstrap-datetimepicker-widget table td span.active,
    .bootstrap-datetimepicker-widget table td i.active {
      background-color: #337ab7;
      color: #fff;
      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    }
</style>

DataPicker2.png

改良して欲しい点

GitHubなので改良したい点があれば、Pull requestするなりforkすればいいんですけど、Pikaday.js を最初に検証したのでオプション不足が見えてしまう。

  • 日本は工場主導なので月曜日始まり対応(firstDay)のカレンダー表示が欲しい
  • 月選択に切り替えた時のタイトルには"年"が付いてないので、"年"表示(yearSuffix) が欲しい
// first day of week (0: Sunday, 1: Monday etc)
firstDay: 0,

// Additional text to append to the year in the calendar title
yearSuffix: '',

ISSUE

初のISSUEを書いてみました。
https://github.com/pingcheng/bootstrap4-datetimepicker/issues

前月と次月のツールチップの不具合の報告と、本家でも同じ不具合でISSUE「タイムピッカー のカレンダーアイコンのツールチップが正しくありません」は修正ソースコードを記載しました。

【2018/07/14追記】
前月と次月のツールチップが表示されない原因が分かりました。
find('span') を find('i') に置換する必要があります。

datetimepicker-tooltips.png

daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth);
daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth);

daysViewHeader.eq(0).find('i').attr('title', options.tooltips.prevMonth);
daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
daysViewHeader.eq(2).find('i').attr('title', options.tooltips.nextMonth);

※前月と次月以外にも前年と次年などもありますが、ISSUEに書いたので省略

Bootstrap 4 DatetimePicker(tempusdominus氏版)

後で見つけた、tempusdominus氏のbootstrap4-datetimepicker も試してみました。

本家の後継という位置づけなのでドキュメントも整っています。
Bootstrap4 v5 Docs

tempusdominus-bootstrap-4_2.png

Closeボタンを付けた。
tempusdominus-bootstrap-4_3.png

インストール

https://tempusdominus.github.io/bootstrap-4/

npm i tempusdominus-bootstrap-4

Package Installerの導入

NuGet版が用意されていないため、Visual Studioの場合は Package Installerをインストールします。
ツール->拡張機能と更新プログラムから拡張機能と更新プログラムの画面を開きます。オンラインを選択し、package installerを検索します。

インストール完了後(Visual Studio再起動)に、プロジェクトの右クリックメニューで「Quick Install Package」を選択します。
画面表示後に「npm」を選択、「tempusdominus-bootstrap-4」を入力してバージョンを最新にしたら「Install」ボタンをクリックします。

DataTimePickerInstall.png

適切なフォルダに配置します。

プログラム(tempusdominus氏版)

index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Tempus Dominus Bootstrap 4 Date & Time Picker Examples</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.1.1/flatly/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="build/css/tempusdominus-bootstrap-4.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/ja.js" type="text/javascript"></script>
<script src="build/js/tempusdominus-bootstrap-4.js"></script>
<style type="text/css">
    .datepicker-days th.dow:first-child,
    .datepicker-days td:first-child {
        color: #f00;
    }
    .datepicker-days th.dow:last-child,
    .datepicker-days td:last-child {
        color: #00f;
    }
</style>
</head>

<body>
</br>
<div class="container">
    <div class="row">
        <div class="form-group">
            <div class="input-group date" id="datetimepicker1" data-target-input="nearest">
                <label for="datetimepicker1" class="pt-2 pr-2">日付:</label>
                <input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" />
                <span class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
                    <span class="input-group-text"><i class="fa fa-calendar"></i></span>
                </span>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="form-group">
            <div class="input-group date" id="datetimepicker2" data-target-input="nearest">
                <label for="datetimepicker2" class="pt-2 pr-2">時間:</label>
                <input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker2" />
                <span class="input-group-append" data-target="#datetimepicker2" data-toggle="datetimepicker">
                    <span class="input-group-text"><i class="fa fa-clock-o"></i></span>
                </span>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript">
    $(function () {
        $('#datetimepicker1').datetimepicker({
            dayViewHeaderFormat: 'YYYY年 MMMM',
            tooltips: {
                close: '閉じる',
                selectMonth: '月を選択',
                prevMonth: '前月',
                nextMonth: '次月',
                selectYear: '年を選択',
                prevYear: '前年',
                nextYear: '次年',
                selectTime: '時間を選択',
                selectDate: '日付を選択',
                prevDecade: '前期間',
                nextDecade: '次期間',
                selectDecade: '期間を選択',
                prevCentury: '前世紀',
                nextCentury: '次世紀'
            },
            format: 'YYYY/MM/DD',
            locale: 'ja',
            buttons: {
                showClose: true
            }
        });
        $('#datetimepicker2').datetimepicker({
            tooltips: {
                close: '閉じる',
                pickHour: '時間を取得',
                incrementHour: '時間を増加',
                decrementHour: '時間を減少',
                pickMinute: '分を取得',
                incrementMinute: '分を増加',
                decrementMinute: '分を減少',
                pickSecond: '秒を取得',
                incrementSecond: '秒を増加',
                decrementSecond: '秒を減少',
                togglePeriod: '午前/午後切替',
                selectTime: '時間を選択'
            },
            format: 'HH:mm',
            locale: 'ja',
            buttons: {
                showClose: true
            }
        });
    });
</script>
</body>
</html>

補足(tempusdominus氏版)

今のところ「showClose: true」にしていますが、クローズボタンが表示されません。
ここはISSUEを書いて報告すれば修正されると思います。

https://github.com/tempusdominus/bootstrap-4/issues/160

Other changes
moved showTodayButton, showClear and showClose into options.buttons

buttons配下に移動になっていました。

改良して欲しい点を直した

【2018/07/28追記】
上記で改良して欲しい点を書きましたが、オープンソースなので修正を待つより直してしまえってことでやってみました。

firstDay Option

日本は工場主導なので月曜日始まり対応(firstDay)のカレンダー表示にする。
firstDay のDayって日のことかと思って違和感があったけど、調べたら曜日のことでした。

//first day of week (0: Sunday, 1: Monday etc)
firstDay: 1,

ソースコードの変更点は、ISSUES に書きました。
https://github.com/tempusdominus/bootstrap-4/issues/173

【2018/08/19追記】
作者によりCloseされました。理由はmoment.js側でfirstDayが制御できるとのこと。
調べてみたら、下記の方法で出来ることが分かりました。
Bootstrap 3 Datetimepicker 3.0.0 - week starts at Monday

locale: 'ja' としているところを書き換えます。

locale: moment.locale('ja', {
    week: { dow: 1 }
}),

firstDayOption.png

土日の色付けは下記のようにしました。

<style type="text/css">
    .datepicker-days th.dow:nth-last-child(2),
    .datepicker-days td:nth-last-child(2) {
        color: #00f;
    }
    .datepicker-days th.dow:last-child,
    .datepicker-days td:last-child {
        color: #f00;
    }
</style>

yearSuffix Option

月選択に切り替えた時のタイトルには"年"が付いてないので、"年"表示(yearSuffix) を付けました。

yearSuffix: '',
dayViewHeaderFormat: 'YYYY{0} MMMM',

ソースコードの変更点は、ISSUES に書きました。
https://github.com/tempusdominus/bootstrap-4/issues/174

2018/08/19に修正したコードをgithubに上げました。
https://github.com/yaju/Bootstrap-4-Date-Time-Picker

yearSuffixOption.png

beforeShowDay Option

【2018/09/18追記】
土日以外に祝日に色をつけたい場合、下記サイトのbootstrap datepickerのような日ごとに呼ばれるbeforeShowDay関数はありませんでした。
bootstrap datepickerで土日の色を変えるよ!

プログラムを修正してbeforeShowDayオプションを追記するとかもしてみたが、最終的にシンプルに土日の色付けと同様にCSSに追加するだけで出来ることが分かりました。
data-day=(日付)の日付は書式(format: 'YYYY/MM/DD')に則ります。

<style type="text/css">
    .datepicker-days th.dow:first-child,
    .datepicker-days td:first-child {
        color: #f00;
    }
    .datepicker-days th.dow:last-child,
    .datepicker-days td:last-child {
        color: #00f;
    }

    .datepicker-days td[data-day="2018/09/17"] {
        color: #f00;
    }
</style>

改変方法

プログラムを修正してbeforeShowDayオプションを追加する方法となります。
CSSのみでは対応し難い場合もありますので、日ごとに呼ばれるコールバック関数があると便利です。

// 170行あたりにオプションを追加
beforeShowDay: $.noop,

// 下記の関数内にbeforeShowDayオプション機能を追加
TempusDominusBootstrap4.prototype._fillDate = function _fillDate() {

    for (i = 0; i < 42; i++) {
        // ・・・
        if (currentDate.day() === ((0 + this._options.firstDay) % 7) || currentDate.day() === ((6 + this._options.firstDay) % 7)) {
            clsName += ' weekend';
        }
        // ここから追加
        if (this._options.beforeShowDay !== $.noop) {
            var before = this._options.beforeShowDay(currentDate);
            if (before !== '') clsName += ' ' + before;
        }
        // ここまで
        row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="day' + clsName + '">' + currentDate.date() + '</td>');
        currentDate.add(1, 'd');
    }
}

【使い方】
beforeShowDayオプション用の関数を追加します、引数のdateは、Moment.jsのdate型となります。
Moment.jsはJavaScriptのDateオブジェクトをラップするオブジェクトとなっていますので、toDate()を呼べばDate型に変換できます。

CSSのクラス名を追加するのが目的です。

例.祝日を赤色にする
$('#datetimepicker1').datetimepicker({
    format: 'YYYY/MM/DD',
    beforeShowDay: function(date) {
        if(date.format('L') === "2018/09/17") {
            console.log(date.format('L'));
            return "public-holiday";
        }
        else
            return "";
    }
});

イベント

変更イベントをネット調べると「dp.change」とあったが動作せず、これはBootstrap3 のDatetimePickerで、tempusdominus版のDatetimePickerは「change.datetimepicker」であった。ドキュメントのEventsには記載してある。

$('#datetimepicker1').on('change.datetimepicker', function(e) {
    console.log(e.date);
});

インラインの値変更

HandsontableというWEBでExcelのようなスプレッドシートライクな入力を可能にしてくれるJavaScriptライブラリがあります。
日付入力は標準でありますが時間入力がありません。そこでボタンをクリックしたらダイアログ画面内にインラインの時間入力を表示するようにしました。

StartTime.png
inputTime.png

// ボタンの時間をインラインのデフォルト時間にセット
var time = table.getDataAtCell(selectedRow, selectedCol);
if (time === null) time = "00:00";
$('#datetimepicker1').data("datetimepicker").defaultDate(moment(moment().format('L') + ' ' + time));

// 変更イベントでボタンの時間表示に即時反映
$('#datetimepicker1').on('change.datetimepicker', function(e) {
    if(selectedCol === 0) return;
    table.setDataAtCell(selectedRow, selectedCol, e.date.format("HH:mm"));
});

Set a default date #111 - tempusdominus/bootstrap-4

最後に

調べると思ってたより記事が無いとかあっても古くて使えなかったり、結局苦労して切り開いて率先して書いていくしかなんだよね。
まだまだ、グーグル先生って頼りない。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした