一覧と追加ができたっぽいので、難関な予感がする更新に手を付けてみます。
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の実装にフォーカスしたので、足りない部分がかなりありますね。
少なくともバリデーションとエラーハンドリングはつけるべき。
あ、テストコードが無いのも問題ですな。
その辺はおいおいやってくってことで。