プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

「RailsによるアジャイルWebアプリケーション開発」23章読了

Web2.0と題して、Prototype、Script.aculo.us、RJSテンプレートが取り上げられています。

Ajaxでdiv要素の更新

ページ中のpタグで囲ったところだけ、Ajaxで更新するというサンプルです。
ここでは、四つの方法で実現してみました。

  • ハイパーリンクをクリックすると更新される
  • フォームボタンをクリックすると更新される
  • divで括られたテキストをクリックすると更新される(イベントハンドラ)
  • 5秒置きに自動的に更新される(疑似プッシュ技術的な)

さっそく解説をば。


まずは、prototype.jsが必要になるので、JavaScriptライブラリの取り込みを行うため、レイアウトファイルを修正します。

app/views/layouts/products.html.erb

<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Products: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold' %>

  <!-- add -->
  <%= javascript_include_tag :defaults %>
</head>

headタグ内に追加しました。
:defaultsにすると、Prototype、Script.aculo.us、application.jsが取り込まれますが、個別に取り込むことも可能です。


次にコントローラ。
app/controllers/products_controller.rb

  def ajaxtest
  end
  
  def ajaxtest_partial
    @dateTime = Time.now
    render(:partial => 'ajaxtest')
  end

Ajaxテスト用ページを表示するアクションajaxtestと、Ajaxリクエストに対してレスポンスを返すajaxtest_partialのアクションを定義しました。


次にビュー。
まずは、Ajaxレスポンスを描画するビューを作ります。
app/views/products/_ajaxtest.html.erb

<%= @dateTime %>

コントローラで取得した日時を、ただ表示しているだけですね。


次に、Ajaxレスポンスを取り込む本体のビューです。
app/views/products/ajaxtest.html.erb

<h3>Ajaxのテスト</h3>

<!-- ajaxリクエストを行うハイパーリンク -->
<%= link_to_remote "日時を更新リンク",
                   :url => {:action => :ajaxtest_partial},
                   :update => 'datetime1' %>

<!-- ajaxリクエストを行うフォームボタン -->
<% form_remote_tag :url => {:action => :ajaxtest_partial},
                    :update => 'datetime1' do %>
  <%= submit_tag "日時を更新ボタン" %>
<% end %>

<!-- ajaxリクエストを行うHTML要素 -->
<div onclick = "<%= remote_function(:update => 'datetime1',
                                    :url => {:action => :ajaxtest_partial}) %>">
日時を更新テキスト(クリックしてね)
</div>

<!-- n秒置きに自動更新 -->
<%= periodically_call_remote :url => {:action => :ajaxtest_partial}, 
                             :update => 'datetime1',
                             :frequency => 5
 %>

<p id='datetime1'/>

ちと長いですが、冒頭で書いた4つの部分に分かれていることが分かれば、理解は容易かと思います。


でもって、
http://localhost:3000/products/ajaxtest/1
へアクセス。


すると、こんなページが表示されます。

どれかをクリック or 5秒間待つことで、ページ下部に現在の日時が表示されます。
簡単にできちゃいますね。

オートコンプリート

Google Suggest(http://www.google.com/webhp?hl=ja&complete=1)的な検索ボックスが実現できる機能らしいです。


とりあえずRails2.xではプラグインになったようなので、プラグインをインストールしてみる。

taka@taka-desktop:~/test$ ruby script/plugin install auto_complete
+ ./README
+ ./Rakefile
+ ./init.rb
+ ./lib/auto_complete.rb
+ ./lib/auto_complete_macros_helper.rb
+ ./test/auto_complete_test.rb


で、書籍の内容は役たたずになったようなので、このインストールされたREADMEを参考にコントローラとビューを作ってみる。


まずは、コントローラ。
app/controllers/products_controller.rb

auto_complete_for :post, :autocomplete_favorite_language

  def autocomplete_demo
  end


  def auto_complete_for_post_user_favorite_language
    puts(params[:post][:user_favorite_language])
    render(:text=>'<ul><li>Ruby</li><li>Java</li><li>Haskell</li><li>C#</li></ul>')
  end

テキストフィールドに入れられた内容は、params[:post][:user_favorite_language]で取得できますが、
ここでは簡略のため、固定のリストを返すようにしています。


次にビュー。
app/views/products/autocomplete_demo.html.erb

<p><label for="user_favorite_language">お気に入りの言語</label><br/>
<%= text_field_with_auto_complete :post, :user_favorite_language %>

text_field_with_auto_completeでオートコンプリート用テキストフィールドを生成しています。


で、
http://localhost:3000/products/autocomplete_demo/1
にアクセス。
と、NoMethodErrorで怒られる。


しばらく悩んだのち、
http://koress.jp/2007/08/ruby_on_rails.html
のサイトで、WEBrickを再起動する必要があることを知りました。。
で、再起動でとりあえずNoMethodErrorは解決。


次に、フォームが表示されたものの、
一向にオートコンプリートされない。
POSTだからダメなのかと思い、GETにしてみても同じくダメ。


試しにPOSTで直接アクセスしてみたらどうなるのかと思い、
こんなHTMLを書いてみることに。
public/test.html

<html>
<form method="POST" action="/products/auto_complete_for_post_user_favorite_langu
age">
<input type = "submit" value = "push">
</form>

で、http://localhost:3000/test.htmlにアクセスし、pushボタンをクリック。


すると、

 ActionController::InvalidAuthenticityToken in ProductsController#auto_complete_for_post_user_favorite_language

が出てました。
これは、
http://brass.to/blog/rails_2_0_auto_complete.html
でも書かれている問題で、このページからリンクされている、
http://d.hatena.ne.jp/jakei/20080305/1204764230
のサイトのパッチを当てることで回避できました。(念のためWEBrickは再起動)


ちなみにですが、WEBrickのコンソールログにステータスコード422ってのが表示されてて、
なんだろうと思ってHTTP/1.1の仕様(RFC2616; http://www.ietf.org/rfc/rfc2616.txt)を見たのですが載ってませんでした。
これはWebDAVのために拡張されたもので、
RFC4918; http://www.ietf.org/rfc/rfc4918.txt
の11.2章に載ってますね。