#302 In-Place Editing
- Download:
- source codeProject Files in Zip (84.4 KB)
- mp4Full Size H.264 Video (18.5 MB)
- m4vSmaller H.264 Video (9.87 MB)
- webmFull Size VP8 Video (11.7 MB)
- ogvFull Size Theora Video (24.5 MB)
上の画面は、あるRailsアプリケーションのユーザプロファイルのページです。ページの一番下に「Edit Profile(プロファイルを編集する)」のリンクがあり、このリンクをクリックすると詳細情報を編集できるページに切り替わります。
このような独立した編集ページを使う代わりに、ユーザがプロファイルページ上でフィールドをクリックしたら直接その場所で編集できるようにしたいと思います。クリックによってフィールドが編集可能に変わり、Enterキーを押すかTabでフィールドを移動したら変更をデータベースに保存します。
Best In Place
アプリケーションにインライン編集機能を追加するためのRailsプラグインはいくつかあり、The Ruby Toolboxにそれらをまとめたリストがあります。いずれも一見の価値がありますが、今回はBest In Placeを使用します。Best In Placeは、Rest in Placeという別のプロジェクトのフォークですが、これを使う意味はbest_in_place
ヘルパーメソッドにあります。これによってRailsアプリケーションに、その場で編集できるフィールドを簡単に追加できるようになります。
Best In Placeにはデモページがあり、試してみるとクリックでフィールドを編集できる様子がわかります。クリックによって静的テキストがデータ型に応じたフォームフィールドに置き換わります。Enterキーを押してフィールドから抜けると変更がサーバに送信されデータベースが更新されます。Best in placeはバリデーションをサポートしているので、例えば無効なメールアドレスを入力するとエラーメッセージが表示されて、入力した値は直前の有効な値に戻ります。また複数のデータ型をサポートしているので、関連を編集するためのドロップダウンメニューを表示したり、ブール値のフィールドでは2つの値を切り替えたりできます。ではこの機能をプロファイルページに追加する作業を見ていきましょう。
アプリケーションにBest In Placeを追加する
アプリケーションにBest in Placeを追加するためには、まずアプリケーションのGemfileにgemを追加して、bundle
コマンドを実行します。
source 'http://rubygems.org' gem 'rails', '3.1.3' gem 'sqlite3' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.1.5' gem 'coffee-rails', '~> 3.1.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'best_in_place'
gemにはアプリケーションにインクルードする2つのJavaScriptファイルが含まれています。jQueryプラグインのjquery.purr
と、best_in_place
です。このアプリケーションはRails 3.1なので、それらをJavaScriptのapplication.jsファイルのmanifestに追加します。
//= require jquery //= require jquery_ujs //= require jquery.purr //= require best_in_place //= require_tree .
編集可能にするフィールドを定義する必要がありますが、それをCoffeeScriptファイルのusers.js.coffee
でおこないます。必要な作業は、編集可能にしたい要素でbest_in_place()
を呼び出すだけです。それをbest_in_place
クラスで要素に適用します。これをBest In Placeが内部で使用します。
jQuery -> $('.best_in_place').best_in_place()
Best In Placeが設定できたので、これからユーザプロファイルのページに適用していきます。ページのテンプレートは現状は以下の通りです。
<h1>User Profile</h1> <p> <b>Name:</b> <%= @user.name %> </p> <p> <b>Email:</b> <%= @user.email %> </p> <p> <b>Gender:</b> <%= @user.gender %> </p> <p> <b>Public profile:</b> <%= @user.public_profile %> </p> <p> <%= @user.bio %> </p> <%= link_to 'Edit Profile', edit_user_path(@user) %>
テンプレートは単純に各フィールドの値を出力するだけです。Best In Placeのヘルパーメソッドを、name
とemail
のフィールドに追加します。このメソッドは引数を2つとります。今表示されているオブジェクトと、編集したいフィールドのシンボルです。
<p> <b>Name:</b> <%= best_in_place @user, :name %> </p> <p> <b>Email:</b> <%= best_in_place @user, :email %> </p>
ユーザプロファイルのページを読み込んで、name
かemail
のいずれかのフィールドをクリックすると、そのフィールドが編集可能に変わります。
変更を保存する
フィールドの値を変更して他の場所をクリックするとフィールドは前の値に戻ってしまいますが、これは期待する動きではありません。しかしページをリロードすると、ユーザ情報が更新されたというメッセージが表示され、ページには変更後の値が表示されます。
これは、Best In PlaceがJSONを用いて更新情報をサーバに送り返しているのにも関わらず、UsersController
がJSONリクエストに正しくレスポンスするよう設定されていないからです。このコントローラは、Best In Placeが期待する通り、通常の7つのRESTfulアクションを使用し、nameフィールドの値を変更するとupdate
アクションが起動されます。正しいパラメータがアクションに送られるのですが、JSONリクエストに対して本来返すべきレスポンスを返していません。
これを修正するためには、update
にrespond_to
ブロックを追加するか、あるいはRails 3アプリケーションなのでrespond_with
を使えば自動的に処理されます。これにはコントローラの最初にrespond_to
の呼び出しが必要なので、それも追加します。
class UsersController < ApplicationController respond_to :html, :json # Other actions omitted. def update @user = User.find(params[:id]) @user.update_attributes(params[:user]) respond_with @user end end
この振る舞いをさらにカスタマイズする場合は、updateアクションでフルのrespond_to
ブロックを使います。この方法についてはBest in PlaceのREADMEにサンプルがあります。
これでコントローラがJSONにレスポンスを返すようになったので、インライン編集の機能は正しく動作するようになりました。変更をおこなうとそれがデータベースに追加され、画面も期待する通りに更新されます。
バリデーションも機能しています。無効なメールアドレスを入力するとエラーメッセージが表示されます。しかしデフォルトではあまり見やすくありません。
このエラーメッセージをCSSで整えます。
.purr { position: fixed; top: 30px; right: 100px; width: 250px; padding: 20px; background-color: #FCC; border: solid 2px #666; &:first-letter { text-transform: uppercase }; }
スタイルはすべて、Best in Placeが使用するpurr
クラスの中にあります。ページをリロードして再度無効なメールアドレスを入力すると、より見やすいエラーメッセージが表示されます。
その他のデータ型を扱う
ここまででname
とemail
の2つのフィールドを編集可能に変更しました。続いてユーザページのその他のフィールドを見ていきます。bio
フィールドには長いテキストが含まれていますが、デフォルトではbest_in_place
は1行のみのテキストボックスを表示します。これはtype
属性を指定することで変更できます。
<%= best_in_place @user, :bio, type: :textarea %>
public_profile
フィールドはブール値なのでcheckbox
タイプを使用して、:collection
オプションに表示する値を渡します。
<%= best_in_place @user, :public_profile, type: :checkbox, collection: %w[No Yes] %>
現在のpublic profileオプションをクリックすると、2つの選択肢が切り替わるようになりました。性別オプションも同じように処理できますが、ここでは:checkbox
の代わりに:select
を使用して、オプションを2次元配列で渡します。
<%= best_in_place @user, :gender, type: :select, :collection [["Male", "Male"], ["Female", "Female"], ["", "Unspecified"]] %>
現在の性別をクリックすると、ドロップダウンメニューが表示されます。
今回のエピソードは以上です。これでユーザプロファイルのページの全フィールドでインライン編集ができるようになりました。もう少し複雑なことをしたいという場合もあるでしょう。例えば、関連テーブルからオプションを表示したり、価格フィールドで表示される値を編集フィールドとは違う形でフォーマットして表示したいなどです。残念ながらこれは現行バージョンのBest In Placeでは少し難しいです。このような場合には、従来通りの編集用フォームに戻すか、自分でゼロから書くことになります。