一覧と追加ができたっぽいので、難関な予感がする更新に手を付けてみます。

CRUDのUです。

一覧部で「編集」ボタンを押したら1行編集状態になって、

「確定」でサーバに書き込み、

「キャンセル」で編集中の内容を破棄して元に戻るって感じにしたいと思います。

更新は$resourceの機能をある程度知らないと無理っぽいので、

原典とか、日本語訳とかを参照しながら進めました。

編集フォーム追加

まずは編集用のUI表示を追加します。

 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
<!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/myapp.js")"></script>
  </head>
  <body>
    <div ng-controller="TasksController">
        <ul >
            <li ng-repeat="task in Tasks">
                <div ng-show='task!=EditingTask'>
                    {{ task.Id }} {{ task.Status }} {{ task.Description }}
                    <button ng-click="startEdit(task)">編集</button>
                </div>
                <form novalidate name='taskEditForm' ng-show='task==EditingTask'>
                    {{ task.Id }} {{ task.Status }}
                    <input type="text" ng-model=" task.Description"/>
                    <button ng-click="cancelEdit(task)">キャンセル</button>
                    <button ng-click="updateTask(task)">確定</button>
                </form>
            </li>
        </ul>
        <hr>
        <input type="text" ng-model="newDescription" placeholder="Input Description"/>
        <button ng-click="add()">追加</button>
    </div>
    <hr>
    <div>
      <label>Name:</label><input type="text" ng-model="yourName" placeholder="Enter a name here">
      <h1>Hello {{yourName}}!</h1>
    </div>
  </body>
</html>

6行目:コントローラー側でjQueryの機能を使いたくなったので、追加。

13行目: ng-show ディレクティブで表示条件を切り分け。EditingTaskが編集中のオブジェクトを指すようにコントローラ側でセットさせる予定。

17~22行目: 編集フォーム。今回編集対象はDescriptionだけにします。ng-clickでハンドラを指定。この時 task とか使えるみたい。

コントローラに編集機能追加

コントローラとモデルはこんな感じに変更しました。だいぶ長くなってます。

 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
42
43
44
45
46
47
48
(function () {  //一応即時関数で安全性に考慮

    var my_app = angular.module('myApp', ['ngResource']);   //RESTfulなサービス呼出のためngResourceを使います

    //モデルは factoryで定義するらしい
    my_app.factory('Tasks', ['$resource', function ($resource) {
        return $resource('/api/tasks/:Id'       //WebAPI側でIdを要求しているので:Idプレースホルダを設定
            , { Id: '@Id'}                      //:Idに対応するparamDefaults設定
            , { update: { method: 'PUT'}}       //PUTを呼び出せるようにする。
        );
    } ]);

    my_app.controller('TasksController', ['$scope', 'Tasks', function ($scope, aTasks) {
        $scope.DebugData = null;
        $scope.Tasks = aTasks.query();
        $scope.add = function () {  //追加処理
            aTasks.save({ Description: $scope.newDescription }, function (saved_object) {
                $scope.Tasks.push(saved_object);    //成功ハンドラの第一引数にWebAPIの返り値が入るっぽい。
            });
        };
        //編集対象
        $scope.OriginTask = null;  //編集前の値
        $scope.EditingTask = null;  //編集中の値
        //編集開始
        $scope.startEdit = function (task) {
            $scope.OriginTask = angular.copy(task); //Cancel機能用にオリジナル値を保存
            $scope.EditingTask = task;              //編集中の値をセット
        };
        //タスク入れ替えヘルパー関数
        var replaceTask = function (oldTask, newTask) {
            var idx = $.inArray(oldTask, $scope.Tasks);    //IE8ではindexOf使えないのでjQuery使用
            if (idx >= 0) $scope.Tasks[idx] = newTask;
        };
        //キャンセル
        $scope.cancelEdit = function (task) {
            replaceTask(task, $scope.OriginTask)    //編集前の値を復帰させる
            $scope.EditingTask = null;  //編集中の値は無いという事に。
        };
        //更新実行
        $scope.updateTask = function (task) {
            task.$update(function (saved_object) {  //$resourceで取得したオブジェクトは .$actionでアクションを呼び出せるらしい。
                replaceTask(task, saved_object)    //保存された値をセット
            });
            $scope.EditingTask = null;
        };

    } ]);
})();

詳しくはコメントを見てもらうとして、$scope.EditingTaskにセットされたtaskが編集対象になります。27行目の $scope.EditingTask = task; だけでページの表示もちゃんと変わります。すごい~。

WebAPI変更

WebAPI側も少々変更。追加処理の時と同じく追加された値を返すようにします。

さらにダミーのビジネスロジックとして、Descriptionを”[]”で囲む処理を追加しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// PUT api/tasks/5
        public Models.Task Put(int id, [FromBody]Models.Task value)
        {
            using (var db = new Models.TaskContext()) {
                value.Description = "[" + value.Description + "]";  //サーバサイドのビジネスロジック的な処理
                db.Entry<Models.Task>(value).State = System.Data.Entity.EntityState.Modified;
                db.SaveChanges();
                return value;   //保存された値を返す
            }
        }

実行

実行するとこんな感じ。

編集中:

確定後:

サーバ側の処理により”[]”で囲われた値が表示されてます。

削除

CRUDのCRUまでできたので最後のDも作ってみましょう。

ここまでできてしまえば削除は簡単ですね。

まずは削除ボタンを追加。

一覧部分に追加してみました。

1
2
3
4
5
<div ng-show='task!=EditingTask'>
                    {{ task.Id }} {{ task.Status }} {{ task.Description }}
                    <button ng-click="startEdit(task)">編集</button>
                    <button ng-click="deleteTask(task)">削除</button>
                </div>

4行目が追加された部分です。ボタン配置してハンドラを ng-click で設定。

コントローラにdelete処理を追加します。

1
2
3
4
5
6
7
//削除
        $scope.deleteTask = function (task) {
            task.$delete(function () {
                var idx = $.inArray(task, $scope.Tasks);    //IE8ではindexOf使えないのでjQuery使用
                $scope.Tasks.splice(idx, 1);    //配列から削除
            });
        };

$scope.updateTaskの次あたりにdeleteTaskメソッドを追加します。

以上 CRUDできました。

少ないコードでModelとViewがしっかり同期するのが気持ちいですね。

もっとできる人がやればさらに少ないコードで済みそうですし、

業務系Webアプリではかなり重宝しそうです。

不足部分

CRUDの実装にフォーカスしたので、足りない部分がかなりありますね。

少なくともバリデーションとエラーハンドリングはつけるべき。

あ、テストコードが無いのも問題ですな。

その辺はおいおいやってくってことで。