Angularでカレンダーを表示してみる、jQueryライブラリとお別れ

  • 投稿日:
  • by

以前の記事で書いたように、しばらくAngularUIのui-calendarを使っていたのですが、依存ライブラリが多い割りにあまり機能を使ってないので不満でした。

また、昨日の記事で書いたように、jQuery依存はやめたいと思ってました。

他にもいいライブラリはないのかと思って調べたのですが、見つかりません。というのも、どれもUI(CSS)とロジック(JS)がセットになっていてリッチなのです。もっと手軽にできないものでしょうか。

そこで、まずはカレンダーを表示するだけでいいと思って、以前紹介したinkを使って自力でやってみることにしました。

ロジックは、moment.jsを使います。このライブラリは単機能でAPIがきれいでいいです。

最初に完成系のスクリーンショットがこちら。

calscn.png

続いて、コードスニペットを貼り付けておきます。 まず、jadeファイルは次のような感じです。

.ink-grid(ng-controller="CalendarCtrl")
  .column-group.top-space.horizontal-space
    .large-20.content-left
      a(ng-click="updateCalendar(calTable.prevMonth)")
        i.icon-caret-left.icon-large
    .large-60.content-center
      .calendar-title {{calTable.title}}
    .large-20.content-right
      a(ng-click="updateCalendar(calTable.nextMonth)")
        i.icon-caret-right.icon-large
  .column-group.content-center.horizontal-space
    .large-100
      table.ink-table.bordered
        tr
          th 日
          th 月
          th 火
          th 水
          th 木
          th 金
          th 土
        tr(ng-repeat="row in calTable.data")
          td(ng-repeat="cell in row", ng-class="{selected: cell.selected}", ng-click="selectCalendar(cell)") {{cell.display}}

ちょっと長いですね。HTMLに変換してなくてすみません。

次にコントローラです、下記。

angular.module('MyApp', []).controller('CalendarCtrl',function($scope, CalendarUtil) {
  $scope.updateCalendar = function(date) {
    $scope.calTable = CalendarUtil.getTableData(date);
  };
  $scope.updateCalendar(new Date());
  $scope.selectCalendar = function(cell) {
    if (cell.date) {
      $scope.calTable.selectedCell.selected = false;
      $scope.calTable.selectedCell = cell;
      $scope.calTable.selectedCell.selected = true;
    }
  };
});

最後に、CalendarUtilです。これも長い。もっと機能が充実したらモジュールとして切り出してもいいかもしれません。

angular.module('MyApp', []).factory('CalendarUtil', function() {
  var dowList = [0, 1, 2, 3, 4, 5, 6];
  function getTableData(selectedDate) {
    var selected = moment(selectedDate);
    var start = selected.clone().startOf('month');
    var end = selected.clone().endOf('month');
    var curr = null;  
    var selectedCell; 
    var data = [];
    for (var j = 0; j < 5; j++) {
      var row = [];   
      for (var i = 0; i < dowList.length; i++) {
        var dow = dowList[i];
        if (end) {
          if (!curr && dow === start.day()) {
            curr = start;
          }
          if (curr) { 
            var cell = {
              display: curr.date(),
              date: curr.clone().toDate(),
              selected: curr.isSame(selected, 'day')
            };
            row.push(cell);
            if (cell.selected) {
              selectedCell = cell;
            }
            if (curr.isSame(end, 'day')) {
              end = null;
            } else {  
              curr.add(1, 'days');
            }
          } else {
            row.push({});
          }
        } else {
          row.push({});
        }
      }
      data.push(row); 
    }
    return {
      title: selected.format('YYYY年MM月'),
      data: data,
      selectedCell: selectedCell,
      nextMonth: selected.clone().startOf('month').add(1, 'months').toDate(),
      prevMonth: selected.clone().startOf('month').subtract(1, 'months').toDate()
    };
  }
  return {
    getTableData: getTableData
  };
});

なんか読みにくいコードですね。もっとシンプルにしたいです。条件分岐が多すぎるような気がします。

こんな感じでできました。同様の機能をjQueryでやったらどうなるのかはちょっと気になります。あまりコード量は変わらないような気もしますね。でも、全体をangularで作るならこの方がいろいろ拡張しやすいと思います。