編集機能と削除機能の実装
残る処理は、編集機能と削除機能です。一覧ページには[Show this word]リンクがあるのみなので、編集、削除するにはページ遷移が必要になってしまいます。SPAらしく、一覧ページで編集と削除もできるようにするために、[Show this word]リンクの替わりに[Edit this word]リンクと[Destroy this word]ボタンを置きます。また、一覧の各項目を操作可能にするために、一覧ページの各項目をフレームで囲みます。
<div id="words"> <% @words.each do |word| %> <%# フレームで囲む %> <%= turbo_frame_tag word do %> <%= render word %> <p> <%# 編集リンクと削除ボタンを置く %> <%= link_to "Edit this word", edit_word_path(word) %> | <%= button_to "Destroy this word", word, method: :delete %> </p> <% end %> <% end %> </div>
項目をフレームで囲ったので、編集フォームでの置き換えができるようになります。そこで、編集フォームであるedit.html.erbをリストのようにフレームで囲みます。また、[Show this word]リンクは意味がないので削除してしまいます。
<%# フレームで囲む %> <%= turbo_frame_tag @word do %> <%= render "form", word: @word %> <br> <div> <%# 元に戻るリンクだけを置く %> <%= link_to "Back to words", words_path %> </div> <% end %>
turbo_frame_tagメソッドの引数にモデルオブジェクトを与えていますので、id属性が一覧側と一致するようになります(図3)。
この時点ではフォームに置き換わるだけなので、更新と削除でStreamsアクションを返すように、コントローラのupdateアクションメソッドとdeleteアクションメソッドをそれぞれリストのように変更します。
def update if @word.update(word_params) # showビューをレンダリングして返す render :show else …略… end def destroy @word.destroy # 削除メソッドの呼び出しをレンダリングして返す render turbo_stream: turbo_stream.remove(@word) end
updateメソッドでは、更新に成功したらshowビュー(show.html.erb)をレンダリングして返すようになっています(本来は詳細ページであるshowビューである必要はないのですが、簡略化のためこのようにしています)。turbo_frame_tagメソッドの引数の@wordによって項目固有のid属性となるので、編集された項目のフレームがこの内容によって更新されます。[Back to words]リンクは意味がなく、一覧ページのスタイルに合わせるために削除しています。
<%# フレームで囲む %> <%= turbo_frame_tag @word do %> <%= render @word %> <div> <%= link_to "Edit this word", edit_word_path(@word) %> | <%= button_to "Destroy this word", @word, method: :delete %> </div> <% end %>
destroyメソッドでは、削除後に「turbo_stream: turbo_stream.remove(@word)」をレンダリングして返すようになっています。これは、コンテンツタイプをTurbo Streamとして返すためのショートカットで、内容は「turbo_stream.remove(@word)」となります。これにより、削除した項目が一覧ページからも削除されます(図5)。