Ajax.Autocomplete : Enter ボタンを押すとformがsubmitされて残念だ、の場合。

Autocomplete は script.aculo.us の機能なのでRails 2.0でもHelperを使わずにJavaScriptで書けば普通に使えます。
で、せっかくだからtaggingのautocompleteを以下のように入れてみる。tagging は acts_as_taggable_steroid で。

# View
<div>
  <label style="width: 100px;" for="entry[tag_list]"><%= _("Tags") %></label>
  <%= text_field 'entry', 'tag_list', :size => 40 %> (<%= _("Each tag is separated with comma(,)") %>)
  <div id="tag_choices" class="autocomplete"></div>
</div>
<script type="text/javascript">
new Ajax.Autocompleter("entry_tag_list", "tag_choices", <%= tags_blog_blogs_url.to_json %>, {
   method : "get",
   tokens : [","]
});
</script>

# Contoller
  # GET /components/blog/blogs/tags
  # Returns the tag list for auto compoleter
  def tags
    starts_with = params[:entry][:tag_list] rescue nil
    if starts_with
      @tags = Tag.find(:all, :conditions => ["name LIKE ?", "#{starts_with}%"])
    else
      @tags = Tag.find(:all)
    end
  end

# tag Renderer
<ul>
  <% @tags.each do |tag| %>
  <li><%=h tag.name %></li>
  <% end %>
</ul>
  • RESTful なResourceにアクセスするので、method オプションに"get"を渡すのが必須。デフォルトだとPOSTなので。
  • tokens 指定で、tokenを区切りにしてautocompleteをしてくれる。これ便利。
  • AutoComplete に戻すHTML は ul/li でリストにする。

これはこれでいいんだけど、Autocompleteが働いたときに、候補を選んでEnterキーを押すと、確かにテキストフィールドに追加されるんだが、それと同時にsubmitが発生してしまう。これは、テキストフィールドのkeypressのデフォルトの挙動で、input type="submit"がある時などの挙動(このあたり正直ややこしい)。

うーん、気に入らんな、と思って、text_field メソッドを書き換えた

module ActionView::Helpers::FormHelper
  alias :text_field_original :text_field
  def text_field(object_name, method, options = {})
    override_default_keybehavior = options.delete(:override_default_keybehavior)
    unless override_default_keybehavior
      options[:onkeypress] = "#{options[:onkeypress]}; return event.keyCode != Event.KEY_RETURN;"
    end
    text_field_original(object_name, method, options)
  end
end

これで、:override_keypress_default => true オプションが設定された場合にのみ、デフォルトの挙動が適用される。検索とか、inputが一つしかないような場合にはEnterでsubmitしてほしいので(というのをJavaScriptで自動適用できるようにするとそれはそれで便利だが後でやるflagで)。