Ruby on Rails Ajax でカートを空にする時に blind_up 効果を付ける

Rails によるアジャイルWebアプリケーション開発 第3版 の『第9章 タスク D : Ajax の追加』の自由課題『カートを空にする際に blind_up を使う方法』について。

フォームボタンを Ajax に対応させる

app/views/store/_cart.html.erb ファイルの button_to で作られるフォームボタンを form_remote_tag で作られる Ajax 対応のフォームに変更する。

<% form_remote_tag :url => { :action => "empty_cart" } do %>
  <%= submit_tag "Empty Cart" %>
<% end %>

コントローラの返すレスポンスを修正する

app/controllers/store_controller.rb 内の empty_cart メソッドが呼出された時に返すレスポンスを JavaScript を返すように修正する。

def empty_cart
  session[:cart] = nil
  respond_to do |format|
    format.js if request.xhr?
    format.html { redirect_to_index }
  end
end

レスポンスとして返す JavaScript を作る

app/views/store/empty_cart.js.rjs にカートを blind_up して消す処理を記述する。

page[:cart].visual_effect :blind_up
page.replace_html("cart", :partial => "cart", :object => @cart)

これで、カートを空にするためにボタンをクリックしてみると画面表示は変化しない。
ブラウザをリロードするとカートは空になるのでセッション・データからはアイテムが削除されている。

エラーの原因は store_controller.rb の empty_cart メソッドで @cart インスタンスを設定していなかったので _cart.html.erb の cartnil となっていたため。
そこで、 empty_cart メソッドに @cart = find_cart を追加して @cart インスタンスを設定するが、これだけだとリロードして @cart インスタンスを作り直さないと @cart の中身が空にならない。
カート部分の displaynone になるために、一見消えているように見えるが、HTML タグのソースを見るとしっかりカートの中身が残ってしまっている。

そこで、 app/models/cart.rb の Cart モデルにカートの中身を空にする(@item を空にする)メソッド empty を追加して、コントローラの empty_cart メソッドでカートの中身を空にする処理を加える。

app/models/cart.rb に empty メソッドを追加する。

def empty
  @items.clear
end

app/controllers/store_controller.rb の empty_cart メソッドでカートを空にする。

def empty_cart
  @cart = find_cart
  @cart.empty
  session[:cart] = nil
  respond_to do |format|
    format.js if request.xhr?
    format.html { redirect_to_index }
  end
end

これで、カートは空になった。

ただ、カートを空にする時に、カート部分の HTML を書き換える処理と blind_up の処理が同時に行なわれるために、一瞬でカートの中身が消えてしまい($0.00 になった合計のみが表示される)、その後 blind_up の処理が行われるような感じになってしまう。
この部分、上手い解決方法がわからないので処理時間を合せて強引に blind_up 処理が終了するとともにカート部分の HTML 書き換えが行なわれているように見せるようにした。
ちょっといまいちな解決法…

app/views/empty_cart.js.rjs で delay 使って HTML の書き換えを遅らせている。

page[:cart].visual_effect :blind_up, :duration => 1
page.delay(1) do
  page.replace_html("cart", :partial => "cart", :object => @cart)
end

これで、思ったような動作になった。
JavaScript を OFF にすると format.html でレスポンスが返されてちゃんと動作している。

RailsによるアジャイルWebアプリケーション開発
Sam Ruby David Heinemeier Hansson Dave Thomas
オーム社
売り上げランキング: 29946
«
»