Rails / Ajaxを使って画面遷移しない一時保存機能をつける

- - - - - - - - - - - 
ブログを更新しました。<a href="http://workabroad.jp/posts/2091">元の記事はコチラ</a>
- - - - - - - - - - - 
先日つけてみたソーシャルボタンですが、やっぱ数字が出るってモチベーションになっていいね。意外とPocketも多いんだなぁ。
今回もまたRails。投稿サイトとかでよくある、一時保存機能をやってみた。文章書いていると、何かの拍子で「あ！」てなることが多いから、あるとユーザーに優しい。

<!-- extract -->

###フォーム

```erb

<div class="blog-form">
	<%= form_for(@post) do |f| %>

		<%= f.text_field :tilte %>
		<%= f.text_area :body %>
		<!-- for temporal saving -->
		<div class="create-temp"></div>
		<%= f.submit "送信する" %>
		
	<% end %>
</div>

```

フォームは例えばこんな感じ。
一時保存するかどうかのフラグと、行われた後のメッセージ表示用に  class="create-temp" がついた div を置いている。
　　
###コントローラー

```ruby

	def create
		@post = current_user.posts.build(strong_params)
		if @post.save
			redirect_to @post
		else
			render 'new'
		end
	end

	def create_temp
		@post = current_user.posts.build(strong_params)
		@post.published = false
		@post.save
	end

```

  
createアクションの他に、一時保存用に、```create_temp```というのを作った。```current_user```とか```strong_params```とかは、人によっていろいろでしょうが、適宜読み替えてください。
一時保存だから、一般には公開したくないわけで、postsテーブルにpublishedを追加して、create_tempアクションでは publish=false としています。
　　
config/routesはこんな感じ。

```ruby

resources :posts
post '/posts/temp',   to: 'posts#create_temp',  as: :temp_post

```
　　



###Javascriptでフォーム送信

```coffee

ready = ->
	# Temporal Saving
	if $('.create-temp')
		window.tempTimer = null
		$('.blog-form form').keydown ->
			# reset
			window.clearTimeout(tempTimer)
			window.tempTimer = window.setTimeout ->
				tempSubmit()
			, 5000

tempSubmit = ->
	# create
	if $('.create-temp span').data('result') != true
		$(".blog-form").ajaxSubmit(
			url: '/posts/temp',
			type: 'post'
		)
	# update
	else
		$(".blog-form form").ajaxSubmit()

# For turbolinks
$(document).ready(ready)
$(document).on('page:load', ready)

```

　　
色んなやり方があると思いますが、keydownされた後、5秒たったら一時保存のフォーム送信をするようにしました。
5秒以内にkeydownされると、キャンセルされます。

ユーザーのアクション関係なくデータを投げるのもありかもしれませんが、ユーザーが画面を開きっぱなしでどこかに行ってしまったら、ずーーっと送信されることになります…ので、なにか手は必要ですね。
ajax送信には[このプラグイン](http://malsup.com/jquery/form/)を使いました。
　　
Turbolinksなので、page:loadを使わないとダメです。
　　

###Viewファイル

jsに対応するビューファイルを作ります。

```javascript

var result = <%= @post.valid? %>;
var msg = "";
var notice = "";

if (result == true) {
	msg = "下書き保存されました";

	// This is for Temporal saving
	// forcing the form to look update method.

	// Add id
	$('.blog-form').prepend('<input name="post[id]" type="hidden" value="<%= @post.id %>">');

	// Chnage REST method
	$('.blog-form input[name=_method]').remove();
	$('.blog-form').prepend('<input name="_method" type="hidden" value="patch">');

	// Change URL
	$('.blog-form').attr('action', '/posts/<%= @post.id %>');

} else {
	msg = "下書き保存のための情報が足りません";
}


// Message
notice = "<span data-result=" + result + ">" + msg + "</span>";
$('.create-temp span').remove();
$('.create-temp').hide().append(notice).fadeIn();

```

　　
一回目の一時保存は普通にcreate_tempアクションに飛んでもらって良いんだけど、次のタイミング（例えば1分に1回保存するとかする場合）では、もうそのレコードはあるんだから、updateアクションに飛んでもらわないと困る。
　　
ということで、javascriptでフォームの内容を変更しています。
強引だけど、これ以外に思いつかなかった。

Railsのフォームは input name="_method" で、フォームの送信先を変える情報を持っています。
同じPOST送信でも、```value="patch"```ならupdateメソッド、```value="post"```ならcreateメソッドが呼び出されるので、updateしたいタイミングで変更してるのです。
　　

###updateメソッド

最後にupdateメソッド。
通常のupdateと、一時保存のupdateで、publishedの状態を変える必要があります。

通常update（送信ボタンを押した時）
```published = true```

一時保存update（下書きとして扱う）
```published = false```

あと、通常はHTML、一時保存はajaxなので、respond_toで分けています。
こんな感じになりました。

```ruby

	def update
		# 所有権のチェック
		@post.attributes = strong_params
		# publish if update botton was clicked
		@post.published = true if params[:commit]

		respond_to do |format|
			format.js do
				@post.save
			end
			format.html do
				if @post.save
					flash[:success] = "ブログが更新されました。"
					redirect_to @post
				else
					render 'edit'
				end
			end
		end
	end

```

　　
updateの前にこの記事が本当にユーザーの持ち物かどうかチェックする必要がありますが（所有権のチェック）、そのメソッドは省略します。
　　
以上です。
初めてやってみたので、つっこみあればお願いします :)




 → <a href="http://workabroad.jp/posts/2091">http://workabroad.jp/posts/2091</a>