RoR で実現する Ajax アプリ (写経) その 3

写経を続行。

アイテムを作る

task コントローラのタスクリスト部分の機能追加。{修正, 追加}したソースを以下に。

  • app/views/task/_list.rhtml
<div class='tasklist'>
<h2 id='tasklist_<%= list.id %>'><%= h list.title %></h2>
<div class='list_menu'>
<%= link_to_function 'ADD', "Element.toggle('add_item_#{ list.id }')", :class => 'menu_link' %>
<%= link_to_remote 'DELETE',
    {
    :url => { :controller => 'ajax', :action => 'destroy_list', :id => list.id },
    :success => "Element.remove($('tasklist_#{ list.id }').parentNode)",
    :confirm => "「#{list.title}」を削除します。\nよろしいですか?"
    },
    :class => 'menu_link'
    %>

<%= form_remote_tag :url => { :controller => 'ajax', :action => 'add_item', :id => list.id },
    :failure => 'alert(request.responseText)',
    :complete => "Element.hide('add_item_#{ list.id }')",
    :position => :bottom,
    :update => { :success => "list_#{ list.id }" },
    :html => { :style => 'display:none;', :id => "add_item_#{ list.id }" }
    %>
<%= text_field_tag 'note', nil, :id => "note_#{ list.id }" %>
<br />
<%= submit_tag 'ADD' %>
<%= link_to_function 'cancel', "Element.hide('add_item_#{ list.id }')" %>
<%= end_form_tag %>

</div>
<ul id='list_<%= list.id %>'>
<% unless list.items.empty? -%>
<%= render :partial => 'item', :collection => list.items %>
<% end -%>
</ul>
</div>
  • app/controllers/ajax_controller.rb (追加分のみ)
  def destroy_list
    render_destroy List.find(params[:id])
  end

  def add_item
    item = Item.new( :list_id => params[:id],
                     :note => params[:note] )
    render_add(item, 'item')
  end

  protected

  def render_destroy(ar)
    ar.destroy
    render :nothing => true
  end

app/views/task/_item.rhtml

<li id='item_<%= item.id %>'>
<%= h item.note %>
</li>

タイプミス等の細かいミスはありましたが、その 1 で作成した 001_initial_schema.rb で大ボケをぶちかましています。note というカラムを作成しておらぬ。(駄目スギ
とりあえず、まとめでキチンとする、ってコトでここは SQL で誤魔化す。(こら

mysql> alter table items add note text ;
Query OK, 0 rows affected (1.20 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc items ;
+------------+------------+------+-----+---------+----------------+
| Field      | Type       | Null | Key | Default | Extra          |
+------------+------------+------+-----+---------+----------------+
| id         | int(11)    | NO   | PRI |         | auto_increment |
| list_id    | int(11)    | YES  |     |         |                |
| completion | tinyint(1) | YES  |     | 0       |                |
| position   | int(11)    | YES  |     |         |                |
| created_on | time       | YES  |     |         |                |
| updated_on | time       | YES  |     |         |                |
| note       | text       | YES  |     |         |                |
+------------+------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

mysql> \q
Bye

とほほほ ...

とりあえず、item の追加はデキた模様。
# それにしてもスタイルシートを何もしてないので見栄えが最低。

なんか全然 javascript 使わずに ajax 実装してるんで微妙なんですが、link_to_function と form_remote_tag の組み合わせで、ぼよよんとフォームが現われて、なナニが実装できる、とゆー事が分かった。直前エントリで出てきた link_to_remote も画面遷移を伴なわない、というアレな機能の実装が可能、という事ッスか。
あと、item があれば表示、というロジックも非常に簡易に実装できる事が分かる。ajax 凄いなぁ、というのもですが、とりこんじゃってる RoR 凄スギ。
render_add というメソドの使い方も RoR の機能(??)として素晴しひ。

アイテムにチェック機能を付ける

チェック入れたらその item は完了状態に、という機能の実装。view と controller に修正が入る。修正後のソースは以下。

app/views/task/_item.rhtml

<li id='item_<%= item.id %>' class='<%= item.completion ? 'completion' : 'not_completion' %>' >
<%= check_box_tag('check', '1', item.completion, :id => "completion_#{ item.id }" ) %>
<%= observe_field "completion_#{ item.id }",
    :url => { :controller => 'ajax', :action => 'completion_item', :id => item.id },
    :complete => %Q[$("item_#{ item.id }").className == "not_completion"
	            ? $("item_#{ item.id }").className = "completion"
                    : $("item_#{ item.id }").className = "not_completion"]
%>
<%= h item.note %>
</li>

app/controllers/ajax_controller.rb (の一部)

public

  def completion_item
    item = Item.find(params[:id])
    item.toggle! :completion
    render :nothing => true
  end

_$ ふんちゃら_、という構文が出てきているな。意味分からんままやっています。とりあえず全部を実装した時点でまとめを作りたいと。(と言いつつヤラない事が多いが、rails についてはきちんと見ないと理解できんので)

アイテムに編集機能を付ける

script.aculo.us の In Place Editor という機能を使う、との事。編集したいテキストをクリックしたらテキストボックスになる、というアレ。ここでも修正は view と controller となる、との事。修正後のソースを以下に。

app/views/task/_item.rhtml

<li id='item_<%= item.id %>' class='<%= item.completion ? 'completion' : 'not_completion' %>' >
<%= check_box_tag('check', '1', item.completion, :id => "completion_#{ item.id }" ) %>
<%= observe_field "completion_#{ item.id }",
    :url => { :controller => 'ajax', :action => 'completion_item', :id => item.id },
    :complete => %Q[$("item_#{ item.id }").className == "not_completion"
	            ? $("item_#{ item.id }").className = "completion"
                    : $("item_#{ item.id }").className = "not_completion"],
    :update => "", :position => :bottom
%>
<span class='item_edit_area' id='item_edit_area_<%= item.id %>'><%= h item.note %></span>
</li>
<script type="text/javascript">
new Ajax.InPlaceEditor(
  'item_edit_area_<%= item.id %>',
  '<%= url_for :controller => 'ajax', :action => 'edit_item', :id => item.id %>',
  {
    onComplete: function(transport, element) {
      if(element.innerHTML.length == 0){
        Element.remove(element.parentNode);
      }
    }
  }
)
</script>

app/controllers/ajax_controller.rg (一部のみ)

  def edit_item
    item = Item.find(params[:id])
    if params[:value].strip.empty?
      render_destroy item
    else
      item.note = params[:value]
      item.save
      render :text => item.note
    end
  end

編集できるようになったんですが、テキストボックスを空にして修正しても見栄え上は箇条書きの_・_が残ったママとなる。未確認ですがデータ的には削除されている模様。リロードしたら消えます。微妙な不具合がん残っているに違いない。色々とイヂくる余地が残っているな。現時点ではスルーさせて頂きます。

とりあえず、D&D でアイテム移動、などという機能の実装が残っていますが、若干消化不良なんでもすこし噛み砕く暇を下さひ。