Bootstrapはやってますね。使ったことありませんが、お手軽にきれいなサイトができるらしいっす。

てことで、AngularJSの練習で作ったプロジェクトにBootstrapを組み込んでみたいと思います。

AngularJS と Bootstrapを合わせ技で使う場合、UI Bootstrapがよさそうです。

こーゆーのやるときって上級者の間ではyeomanとかでさくっとscaffolding的な事をするんでしょうけど、お勉強目的なので地道にダウンロード→プロジェクトに組み込みでやっていきます。

ダウンロード

UI Bootstrap

Angularな人たちが作っているnative AngularJS directivesなライブラリ(?)です。

UI Bootstrapの”Download”から Build=Minified ,Include Templates=Yesでダウンロードします。

バージョンは0.10.0でした。

ダウンロードされたファイル”ui-bootstrap-tpls-0.10.0.min.js”をプロジェクトのScriptsにコピー。

Bootstrap

twitterな人たちが作ってる、さくっとかっこいいサイトができるらしいフレームワークです。

BootstrapのサイトからDownloadします。

バージョンは3.1.1でした。

“bootstrap-3.1.1-dist.zip”を展開したフォルダをプロジェクトのContentにコピー。

フォルダ名を”bootstrap3.1.1″にしました。

こんな構成になっております。

追加フォームをbootstrap風にしてみる

手始めに新規追加フォームをbootstrapで装飾してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!doctype html>
<html ng-app="myApp">
  <head>
    <script src="@Url.Content("~/Scripts/angular.min.js")"></script>
    <script src="@Url.Content("~/Scripts/angular-resource.min.js")"></script>
    <script src="@Url.Content("~/Scripts/jquery-1.11.0.min.js")"></script>
    <script src="@Url.Content("~/Scripts/ui-bootstrap-tpls-0.10.0.min.js")"></script>
    <script src="@Url.Content("~/Scripts/myapp.js")"></script>

     <link href="@Url.Content("~/Content/bootstrap3.1.1/css/bootstrap.min.css")" rel="stylesheet"/>
  </head>
  <body>
    <div ng-controller="TasksController">
        ・・・中略・・・
        <hr>
        <form class="form-horizontal" role="form">
            <div class="form-group">
                <label for="newDescription" class="col-sm-2 control-label">詳細</label>
                <div class="col-sm-10">
                    <input type="text" ng-model="newDescription" placeholder="Input Description" id="newDescription" class="form-control"/>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button ng-click="add()" class="btn btn-default">追加</button>
                </div>
            </div>
        </form>
    </div>
    <hr>
        ・・・中略・・・
  </body>
</html>

bootstrapって結構冗長な書き方になるのですね。

実行してみると… ChromeだとちゃんとするけどIE8だとダメですね。

horizontalとかoffsetとか効いてないっす。

IE8対応

Supported browsersによるとRespond.js とやらを組み込めとのこと。さらに、Basic templateを良く見るとIE用の記述がありますね。”if lt IE 9″ってやつ。

html5shivRespond.jsをダウンロードして、Scriptsにコピーして参照します。修正後は

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<!doctype html>
<html ng-app="myApp">
  <head>
    <script src="@Url.Content("~/Scripts/angular.min.js")"></script>
    <script src="@Url.Content("~/Scripts/angular-resource.min.js")"></script>
    <script src="@Url.Content("~/Scripts/jquery-1.11.0.min.js")"></script>
    <script src="@Url.Content("~/Scripts/ui-bootstrap-tpls-0.10.0.min.js")"></script>
    <script src="@Url.Content("~/Scripts/myapp.js")"></script>

     <link href="@Url.Content("~/Content/bootstrap3.1.1/css/bootstrap.min.css")" rel="stylesheet"/>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="@Url.Content("~/Scripts/html5shiv.min.js")"></script>
      <script src="@Url.Content("~/Scripts/respond.min.js")"></script>
    <![endif]-->
  </head>
  <body>
        ・・・中略・・・
  </body>
</html>

IE8、Widnows7標準のブラウザなんで対応しないわけにはいきませんよね~。IE滅びないかな♪

UI Bootstrapなdirectiveを使ってみる

動作確認もかねて見た目が派手そうなDatepickerを使ってみます。

Entityの変更

日付っぽいプロパティを持たせたいので、TaskにDueDateというプロパティを持たせてみました。

1
2
3
4
5
6
7
8
[Table("Tasks")]
    public class Task
    {
        public int Id { get; set; }
        public TaskStatus Status{ get; set;}
        public string Description { get; set; }
        public DateTime? DueDate { get; set; }
    }

テスト用データにも日付を入れるようにします。

1
2
3
4
5
6
7
8
using (var db = new Models.TaskContext()) {
                var task = new Models.Task();
                task.Status = Models.TaskStatus.New;
                task.Description = "てすとっす";
                task.DueDate = DateTime.Parse("2014/4/1");
                db.Tasks.Add(task);
                db.SaveChanges();
            }

AngularJSコントローラ変更

AngularJS用のスクリプトにui.bootstrapを組み込み、カレンダー用のコントローラを追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(function () {  //一応即時関数で安全性に考慮

    var my_app = angular.module('myApp', ['ngResource'    //RESTfulなサービス呼出のためngResourceを使います
                                            , 'ui.bootstrap'   //モダンなページデザインのためUI Bootstrapを使用します
                                            ]);

・・・中略・・・

    my_app.controller('TasksController', ['$scope', 'Tasks', function ($scope, aTasks) {
        $scope.Tasks = aTasks.query();
        $scope.newTask = {};    //入れ子のコントローラから参照されるので、空のオブジェクトをセットしておく
        $scope.add = function (task) {  //追加処理
            aTasks.save({ Description: task.Description, DueDate: task.DueDate }, function (saved_object) {
                $scope.Tasks.push(saved_object);    //成功ハンドラの第一引数にWebAPIの返り値が入るっぽい。
            });
        };

・・・中略・・・
    my_app.controller('DatepickerCtrl', ['$scope', function ($scope) {
        // Disable weekend selection
        $scope.disabled = function (date, mode) {
            //return (mode === 'day' && (date.getDay() === 0 || date.getDay() === 6));  //disable weekend
            return false;   //enable all days
        };

        $scope.open = function ($event) {
            $event.preventDefault();
            $event.stopPropagation();
            $scope.opened = true;
        };

        $scope.dateOptions = {'year-format': "'yy'",'starting-day': 1 };    //コントローラ側からオプション設定

    } ]);
})();

4行目でUI Bootstrapへの参照を追加しています。

11行目付近で新規追加用のモデルを明示的に作成しています。

コントローラを入れ子にした際の親のスコープオブジェクトを明確にするためです。

addハンドラも追加対象を明示的にセットするようにしました。あ、DueDateにも対応してます。

19行目からがカレンダー用のコントローラですサンプルを参考に要らない部分を削ってみました。

Datepickerのコントローラ名に”DatepickerController”を使ったらカレンダーが表示されなくなりました。ほかの名前なら大丈夫。なんか内部的に使ってたりするみたいです。半日はまりました。

新規フォームView変更

新規追加用フォームを変更していきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
・・・中略・・・
    <div ng-controller="TasksController">
・・・中略・・・
        <hr>
        <form class="form-horizontal" role="form" ng-model='newTask'>
            <div class="form-group">
                <div ng-controller="DatepickerCtrl">
                    <label for="newDueDate" class="col-sm-2 control-label">締切日</label>
                    <div class="col-sm-6">
                        <p class="input-group">
                         <input type="text" class="form-control"
                            ng-model="newTask.DueDate"
                            datepicker-popup="yyyy-MM-dd"
                            is-open="opened"
                            datepicker-options="dateOptions"
                            date-disabled="disabled(date, mode)"
                            ng-required="true"  />
                          <span class="input-group-btn">
                            <button class="btn btn-default" ng-click="open($event)">
                            <i class="glyphicon glyphicon-calendar"></i></button>
                          </span>
                        </p>
                    </div>
                </div>
            </div>

            <div class="form-group">
                <label for="newDescription" class="col-sm-2 control-label">詳細</label>
                <div class="col-sm-10">
                    <input type="text" ng-model="newTask.Description" placeholder="Input Description" id="newDescription" class="form-control"/>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button ng-click="add(newTask)" class="btn btn-default">追加</button>
                </div>
            </div>
            {{newTask}}
        </form>
    </div>
・・・中略・・・

5行目:フォームにモデルを明示的にセットしています。

7行目:DatepickerCtrlを入れ子にして、

12行目:で親コントローラのモデルを参照しています。

13行目:からのdatepicker-??とかのdirectiveがUI Bootstrap用の記述になります。これだけでPopupなカレンダーが出るようになります。

30,35行目:モデルを明示的にセットしています。

実行

IE8でも動きます。

おそろしいです。

続くかも。