backboneの基礎の基礎 第4回[データの保存・削除]

今回は、簡単な編集と削除機能を作ります。
こんな感じ

![4_screen.png](https://qiita-image-store.s3.amazonaws.com/0/79214/78d9922c-8095-799c-f9da-9a8eaac54106.png)

編集ボタンを押すと下に編集する画面が表示されます。
![4_screen2.png](https://qiita-image-store.s3.amazonaws.com/0/79214/afc317ae-450a-86a7-bb6d-286b2fae875b.png)

前回からの追加内容をざっくりと書くとー
レイアウトに編集画面用のレイアウト追加
モデルに保存、削除時の送信先となるurlを設定
あとは、保存、削除ボタン押下時の処理を記述といった感じです。

では、さっそく
## layout.html

```html
<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title>backbone.jsの超簡単サンプル 2回目</title>
	</head>
	<body>
		<!-- view表示用タグを埋め込む -->
		<div id="about"></div>
		<br>
		<div id="memberDetail"></div>
		
		<!-- ここは外部ライブラリの読み込み -->
		<script src="js/lib/jquery-2.1.4.min.js"></script>
		<script src="js/lib/underscore-min.js"></script>
		<script src="js/lib/backbone-min.js"></script>
		
		<!-- template読み込み -->
		<!-- チーム紹介 template -->
		<script type="text/template" id="about-template">
			<div>
				<div>名前: <span><%= name %></span></div>
				<div>生年月日: <span><%= birthday %></span></div>
				<div>ポジション: <span><%= position %></span></div>
				<div><input type="button" class="detail" value="詳細" id="<%= index %>"></div>
				<div><input type="button" class="edit" value="編集" id="<%= index %>"></div>
			</div>
		</script>
		
		<!-- メンバー詳細 template -->
		<script type="text/template" id="member-detail-template">
			<div>
				<div>名前: <span><%= name %></span></div>
				<div>生年月日: <span><%= birthday %></span></div>
				<div>ポジション: <span><%= position %></span></div>
				<div>背番号: <span><%= number %></span></div>
				<div>趣味: <span><%= hobby %></span></div>
				<div>今季の意気込み: <span><%= zeal %></span></div>
			</div>
		</script>
		
		<!-- メンバー詳細(編集用) template -->
		<script type="text/template" id="edit-member-detail-template">
			<div>
				<div>名前: <input type="name" value="<%= name %>" id="name" ></div>
				<div>生年月日: <input type="birthday" value="<%= birthday %>" id="birthday" ></div>
				<div>ポジション: <input type="position" value="<%= position %>" id="position" ></div>
				<div>背番号: <input type="number" value="<%= number %>" id="number" ></div>
				<div>趣味: <input type="hobby" value="<%= hobby %>" id="hobby" ></div>
				<div>今季の意気込み: <input type="zeal" value="<%= zeal %>" id="zeal" ></div>
			</div>
			<input type="button" value="保存" id="editBtn">
			<input type="button" value="削除" id="deleteBtn">
		</script>
		
		<!-- モデル -->
		<script src="js/MemberModel.js"></script>
		
		<!-- コレクション -->
		<script src="js/TeamCollection.js"></script>
		
		<!-- チーム紹介ビュー -->
		<script src="js/TeamAboutView.js"></script>

		<!-- メンバー詳細ビュー -->
		<script src="js/MemberDetailView.js"></script>
		
		<!-- メンバー詳細 編集ビュー -->
		<script src="js/MemberDetailEditView.js"></script>
		
		<script>
			<!-- DOM読み込み完了後に実行 -->
			$(function() {
				// ビューを生成し、処理を開始する。
				new app.TeamAboutView();
			});
		</script>

	</body>
</html>
```

メンバー詳細(編集用)のテンプレートとそのjavascriptファイルの読み込み追加。
ここは特に問題ないですね。

## MemberModel.js
モデル。保存や削除時のリクエストの送信先のURLプロパティを追加。
データ取得時はコレクションのfetchメソッドを使ったけど(複数レコードの取得)、保存や削除はまとめてできない(コレクションにメソッドがない)のでモデルのメソッドを使います。

```js
var app = app || {};

app.MemberModel = Backbone.Model.extend ({

	url : '/xxxxx/xxxx',
	
	defaults : {
		position : '', // ポジション
		name : '', // 名前
		birthday : '', // 誕生日
	},
});
```
続いて、チーム紹介用のjavascript。
## TeamAboutView.js

```js
var app = app || {};

app.TeamAboutView = Backbone.View.extend ({
	el : '#about',
	
	aboutTemplate : _.template($('#about-template').html()),
	
	collection : '',
	
	events : {
		'click .detail' : 'dispDetail',
		'click .edit' : 'editDetail',
	},
	
	initialize : function() {
		this.collection = new app.TeamCollection();
		
		// データを取得する。
		this.collection.fetch({reset: true});
		
		// データ取得後に、画面を描画する。
		this.listenTo(this.collection, 'reset' , this.render);
	},
	
	render : function() {
		var that = this;
		
		var i = 0;
		
		// コレクションに入っているデータ件数分、メンバーを表示する。
		this.collection.each(function(model){
			model.set('index', i);
			that.$el.append(that.aboutTemplate(model.toJSON()));
			
			i++;
		});
	},
	
	dispDetail : function (event) {
		// indexを取得する。
		var index = $(event.target).attr('id');
	
		// modelを取得する。
		var memberModel = this.collection.models[index];
		
		// メンバー詳細を表示する。
		var options = {
			model : memberModel
		};
		new app.MemberDetailView(options);
	},
	
	editDetail : function (event) {
		// indexを取得する。
		var index = $(event.target).attr('id');
	
		// modelを取得する。
		var memberModel = this.collection.models[index];
		
		// メンバー詳細(編集)を表示する。
		var options = {
			model : memberModel
		};
		new app.MemberDetailEditView(options);
	},
});
```

編集ボタン押下した際のイベントをeventsプロパティに設定し、
`'click .edit' : 'editDetail',`

編集レイアウトを描画するメソッド**editDetail**を追加。
これも前回と同じなので、問題ないですね。

次が新しい要素たくさんの編集レイアウトのjavascript。
## MemberDetailEditView.js

```js
var app = app || {};

app.MemberDetailEditView = Backbone.View.extend ({
	el : '#memberDetail',
	
	memberTemplate : _.template($('#edit-member-detail-template').html()),

	events : {
		'click #editBtn' : 'edit',
		'click #deleteBtn' : 'delete',
	},
	
	model : '',
	
	initialize : function() {
		// 前に描画したメンバー詳細を削除する。
		this.$el.empty();
		
		// 画面を描画する。
		this.render();
	},
	
	render : function() {
		// パラメータで渡されたmodelで画面を描画する。
		this.$el.append(this.memberTemplate(this.model.toJSON()));
	},
	
	edit : function() {
		var name = this.$el.find('#name').val();
		var birthday = this.$el.find('#birthday').val();
		var position = this.$el.find('#position').val();
		var number = this.$el.find('#number').val();
		var hobby = this.$el.find('#hobby').val();
		var zeal = this.$el.find('#zeal').val();
	
		// 入力内容でモデルを変更する。
		this.model.set('name', name);
		this.model.set('birthday', birthday);
		this.model.set('position', position);
		this.model.set('number', number);
		this.model.set('hobby', hobby);
		this.model.set('zeal', zeal);
		
		this.model.save(null, {
			success : function(){console.log("success!!")},
		});
	},
	
	delete : function() {
		this.model.destroy();
	},
});
```

前回の復習もかねて、
編集用のレイアウトの表示位置となる
`el : '#memberDetail',`
描画の際に使用するテンプレート
`memberTemplate : _.template($('#edit-member-detail-template').html()),`
保存、削除ボタン押下時のイベント
`events : {
		'click #editBtn' : 'edit',
		'click #deleteBtn' : 'delete',
	},`
初期化の**initializeメソッド**と描画の**renderメソッド**。

ここからがいよいよ新しいものです！

まずeditメソッド。
保存ボタン押下時の処理をします。

やってることは簡単で、入力テキストボックスの値を取得して、それをモデルに詰めなおして、サーバにリクエストを送信してます。

this.$el.findでこのレイアウト内の要素を探します。
`var name = this.$el.find('#name').val();`だと
id属性が「name」の要素を探します。

モデルのsetメソッドで値をモデルに設定。
`this.model.set('name', name);`

そして最後に詰めなおしたモデルのデータでリクエストをサーバに送ってます。

```js
this.model.save(null, {
	success : function(){console.log("success!!")},
});
```
第一引数が送信する値。nullだとモデルのプロパティがリクエストデータとして送られます。
ここに値があれば、モデルのプロパティと第一引数の値をマージしたものが送られます。
例えば、下記のようにするとnameはマージ太朗(第一引数が優先)、そしてnick_nameも追加されたデータが送られる。

```js
var data = {
	name : 'マージ　太朗',
	nick_name : 'まーじん',
};
	
this.model.save(data, {
	success : function(){console.log("success!!")},
});
```

リクエストがちゃんと送られてるかをクロームのF12押して確認すると、
![4_request.png](https://qiita-image-store.s3.amazonaws.com/0/79214/1e113d0e-e372-7f17-606a-8b224228eab5.png)
ちゃんと送れとります！

今回は更新データなので、リクエストのメソッドは「PUT」になりやす。
新規の場合は、「post」です。
新規かどうかの判断はidプロパティがあるかどーかです。

モデルの全データを送ってますが、変更があったものだけとかサーバに送るデータを選択したい事もありますよねー
その場合はこう。

```js
this.model.save(data, {
	success : function(){console.log("success!!")},
	patch : true
});
```
プロパティ**patch:true**と書きます。
この通りです。
![4_patch.png](https://qiita-image-store.s3.amazonaws.com/0/79214/29b4bd42-e4c9-a6d9-de3b-efcbea5cf35f.png)
第一引数の値のみが送信されて、リクエストのメソッドは「patch」となります。

最後に削除。
モデルのdestroyメソッドを使います。

```js
delete : function() {
	this.model.destroy();
},
```

これでdeleteメソッドのリクエストを発生させます！

サーバサイドは書かないけど、それぞれのリクエストに応じてputだと更新、deleteだと削除するようなロジックを書けばOK！

少し長くなったけど、第4回終了。
あっ、忘れてたけど「TeamAboutView.js」でfetchのところと、監視するイベント変えとります。

前は

```js
// データを取得する。
this.collection.fetch();
		
// データ取得後に、画面を描画する。
this.listenTo(this.collection, 'sync' , this.render);
```
だったけど、

```js
// データを取得する。
this.collection.fetch({reset: true});
		
// データ取得後に、画面を描画する。
this.listenTo(this.collection, 'reset' , this.render);
```

前までのだと、syncイベントを監視してるので、modelのsaveやdestroyでもthis.renderメソッドが動いてしまう。
syncなので、サーバと正常に処理ができると動く。
今回は特にそのタイミングで描画する必要ないので、fetchの場合のみ動くように変更しました。
fetchのパラメータに{reset:true}を渡すと、fetchが完了するとresetイベントを発動してくれる。

ではでは。
[プログラムはこちら](https://github.com/naka4A6/backbone_4)