のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

指定期間でDOMを出し分けてくれるangular-periodを公開しました

angular-periodとは

AngularJS のDirectiveで、期間を指定するとその 期間前期間中期間後でDOMの表示を切り替えてるようにするものです。

例えば、なんかの応募とかで、以下の様なHTMLをテンプレートエンジンやJavaScriptなどを使って出し分けることがある時に便利です。

angularJSを使った一番お手軽なケースとしてはこんなかんじでしょうか。

angular.module("campaign", [])
  .controller('CampaignCtrl", function () {
    this.now   = new Date().getTime();
    this.start = new Date('2015-08-01T00:00:00').getTime();
    this.end   = new Date('2015-08-31T23:59:59').getTime();
  });
<div class="campaign" ng-controller="Campaign as campaign">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-if="campaign.now < campaign.start">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-if="start <= campaign.now && campaign.now <= campaign.end">
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-if"campaign.end < campaign.now">
    <p>応募は締め切りました</p>
  </div>
</div>

angular-periodを使えば、以下のように書けます。

angular.module('campaign', ['angularPeriod']);
<div class="campaign" ng-period
  ng-period-start="'2015-08-01T00:00:00'"
  ng-period-end  ="'2015-08-31T23:59:59'">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-period-when="previous">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-period-when="during>
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-period-when="after">
    <p>応募は締め切りました</p>
  </div>
</div>

開始と終了を渡すと、その期間の前、中、後のHTMLを選択してDOM Treeに組み込みます。 最も単純なケースではコントローラーすら不要なので、ロジックやテストを大幅に削減できます。

また、angular-periodはリアルタイムな反映及び、setTimeoutの上限値である32bit intを超える値もサポートできるようにしているので、長期にわたる期間であってもある程度は許容されます。

インストール

npm, bowerともに公開しているので必要な方を利用してください。

npm install angular-period
bower install angular-period

日付の指定とパースについて

利用可能な値

上記には文字フォーマットを用いた例を記載しました。 ただ、実際にプログラムする上で文字列だけでは様々な不便なことがあるでしょう。

ngPeriodは Date オブジェクトに変換可能な値をサポートしています。

具体的には、 - Dateパース可能な文字列 - Date オブジェクト - momentjsなどのDateオブジェクトに渡せるオブジェクト

先ほどの例で、例えばmomentjsを使う場合は以下の様に書けます。

angular.module('campaign', ['angularPeriod'])
  .controller('CampaignCtrl", function () {
    this.start = moment('2015-08-01 00:00:00');
    this.end   = moment('2015-08-31 23:59:59');
  });
<body  ng-controller="Campaign as campaign">
<div class="campaign" ng-period
  ng-period-start="campaign.start'"
  ng-period-end  ="campaign.end">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-period-when="previous">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-period-when="during>
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-period-when="after">
    <p>応募は締め切りました</p>
  </div>
</div>
</body>

至ってシンプル。

パースできない値

Dateオブジェクトでパース可能な値はブラウザによって異なります。 パースできない値を渡すと、Dateは Invalid Date というオブジェクトを返すので、 それをそのままDateとして扱おうとすると、Nanだったり不適切な値になりえます。

困るケースでは、ChromeSafariで利用可能なDateのフォーマットが異なるという点です。

ChromeYYYY-MM-DD HH:mm:ssという値をサポートしていますが、SafariではInvalid Dateが返ってきます。 これは失念すると非常に気づきにくくリスキーで、常にChromeSafariでの動作確認を要求されるため大変不便です。

angular-periodでは、Invalid Date になるような値を渡された場合その時点でErrorオブジェクトをthrowするようにはしています。 JavaScriptのDateオブジェクトは非常に複雑かつブラウザ間で挙動が違うので、それを吸収するよりは、すでに実装され安定的に運用されている他のモジュール(momentjsのような)を利用することを推奨します。

まとめ

というわけで結構便利なモジュールを書いたつもりで居るのでどうぞお試しあれ。