連載
» 2008年10月22日 00時00分 公開

Ruby on RailsのRJSでかんたんAjax開発(後編):RJSなら数行のRubyコードでAjaxアプリを作成できる (3/4)

[志田裕樹,株式会社アークウェブ]

タブがアクティブかどうかのデザインを変更

 次に、カテゴリ切り替えのタブを、現在表示しているカテゴリがアクティブになるように変更します。例えば、「動物Tシャツ」カテゴリがアクティブな場合は、次のようなマークアップになるようにします。

<li class='category active'>動物Tシャツ</li>
<li class='category inactive'>猫Tシャツ</li>
<li class='category inactive'>犬Tシャツ</li>

 タブはパーシャルで実現されているので、パーシャルに現在アクティブなカテゴリのCategoryインスタンスを渡し、パーシャル内で、現在描画中のカテゴリがアクティブかどうかを判定して描画し分けるようにします。

タブがアクティブかどうかの判定

 localsオプションにハッシュを指定すると、パーシャル内で指定した変数が参照できます(参考:renderメソッドのリファレンス)。

<ul id='categories'>
<%= render :partial => "category",
    :collection => @categories,
    :locals => { :active_category => @category } %>
</ul>

 パーシャル内では、下記のようにactive_category変数と、現在描画中のカテゴリを比較しアクティブかどうかを判定します。

<%
    if active_category == category
        class_value = 'category active'
    else
        class_value = 'category inactive'
    end
%>

 ここで決定したclass_valueで、<li>を描画します。

<li class='<%= class_value %>'>

 これで、アクティブなタブを表現できました。

RJSでアクティブなタブを切り替える

 次に、RJSでタブの<li>のclassも変更できるようにして、アクティブなタブもRJSで切り替えるようにします。

 まず、<li>に「category_現在のID」という値で、idを付けて、RJSで<li>タグの操作が行えるようにしました。

<li class='<%= class_value %>'
    id='<%= "category_#{category.id}" %>'>

 次に、RJSに次の記述を追加して、タブがクリックされたら、一度すべてのタブを非アクティブにします。

<li class='<%= class_value %>'
    id='<%= "category_#{category.id}" %>'>

 その後、現在のカテゴリのみをアクティブにします。

page.select("#category_#{@category.id}").each do |cate|
    cate.removeClassName('inactive').addClassName('active')
end

 selectは、CSSパターンを引数で受け取り、検索された要素に対して処理をすることができるメソッドです(参考:selectのリファレンス)。selectの結果に対してブロック付きでeachを呼び出していますが、これがprototype.jsのeachを無名関数で呼び出す記述に書き換わります(参考:prototype.jsのeachのリファレンス:)。

 これはRJSのコレクションプロキシという機能です(参考:RJS Element and Collection Proxies)。結果的に、次のJavaScriptが生成されます。

$$(".category").each(function(value, index) {
    value.removeClassName("active").addClassName("inactive");
});

 これで、RJSでもアクティブなタブを表現できました。

データベースの商品情報をscaffoldで作成し、表示させる

 ここからは、カテゴリ内商品を表示できるようにしていきます。次の手順で説明していきます。

  1. scaffoldによる商品の作成
  2. テーブル間のリレーションの設定
  3. モデル間のリレーションの設定
  4. 商品のサンプルデータの登録
  5. カテゴリ詳細画面内に商品一覧を埋め込み

scaffoldによる商品の作成とカテゴリとの関連の設定

 カテゴリのときと同様に、scaffoldで商品の一覧表示や追加、修正、削除などができるようにしてから、カテゴリ詳細画面で商品一覧を呼び出すようにします。商品(product)は、商品名(name)や画像のURL(image_url)、価格(price)の情報を持つことにします。次のようにscaffoldを実行しました。

$ script/generate scaffold product name:string image_url:string price:integer
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/products

…【略】…

テーブル間のリレーションの設定

 次にマイグレーションを使って、カテゴリのときと同様にデータベース上にテーブルを作成します。カテゴリと商品には、「1つのカテゴリに複数の商品が登録される」という1対多のリレーションがあります。このため、データベースのproductsテーブル上に、categoriesテーブルのidカラムへの外部キー参照が必要です。

 productsテーブルに外部キー参照を追加するために、「db:migrate」のrakeタスクを実行する前に、scaffoldで生成された「db/migrate/20080929023746_create_products.rb」に次の行を追加します(参考:refencesのリファレンス)。

create_table :products do |t|
    t.references :category #この行を追加
    t.string :name
    t.string :image_url
    t.integer :price
    t.timestamps
end

 追加したら「db:migrate」を実行します。

$ rake db:migrate
(in /home/staff/shida/public_html/rails/rjs_tutorial)
== 20080929023746 CreateProducts: migrating ========================
-- create_table(:products)
-> 0.0671s
== 20080929023746 CreateProducts: migrated (0.0675s) ===============

 生成されたproductsテーブルの構造を、sqliteコマンドを使って確認しましょう。

$ sqlite3 db/development.sqlite3
> .schema products
CREATE TABLE "products" (
    "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    "category_id" integer DEFAULT NULL NULL,
    "name" varchar(255) DEFAULT NULL NULL,
    "image_url" varchar(255) DEFAULT NULL NULL,
    "price" integer DEFAULT NULL NULL,
    "created_at" datetime DEFAULT NULL NULL,
    "updated_at" datetime DEFAULT NULL NULL
);

 「category_id」というカラムが存在します。これで、カテゴリテーブルと商品テーブルのリレーションの設定はできました。

モデル間のリレーションの設定

 次に、ActiveRecordの「アソシエーション」という機能を使用し、モデルクラス間のリレーションを設定します。CategoryクラスとProductクラスに、次の記述を追加します。

class Category < ActiveRecord::Base
    has_many :products #この記述を追加
end
app/model/category.rb

class Product < ActiveRecord::Base
    belongs_to :category #この記述を追加
end
app/model/product.rb

 アソシエーションを設定すると、そのモデルクラスに、関連付けられたモデルを簡単に処理するためのメソッドが追加され、プログラミングがより楽になります。

  • Categoryクラスに「has_many :products」を設定することで追加されるメソッドの例(参考:has_manyのリファレンス
    • category.products
      そのカテゴリに所属する商品を配列として返す
    • category.products << another_product
      そのカテゴリに所属する商品を新しく追加する
  • Productクラスに「belongs_to :category」を設定することで追加されるメソッドの例 (参考:belongs_toのリファレンス
    • product.category
      その商品が属するカテゴリを返す
    • product.category = another_category
      その商品が属するカテゴリを設定する

YAML形式で商品のサンプルデータを登録

 次に、カテゴリのときと同様に、商品のサンプルデータをtest/fixtures/products.yml内にYAML形式で記述します。商品はカテゴリに所属するので、次のようにサンプルデータ上で所属するカテゴリ情報を指定します。

owl:
    name: ふくろう
    image_url: animal/owl.gif
    price: 1500
    category: animal
test/fixtures/products.yml

 「category」のカラムに指定している「animal」は、前回記事でカテゴリのサンプルデータを登録する際に指定していたレコードの識別名です。

animal:
    name: 動物Tシャツ
    description: さまざまな動物の写真をあしらったTシャツです
test/fixtures/categories.yml

 このように、リレーションの関係にあるレコードを識別名で指定できる機能はFoxy Fixturesなどと呼ばれています。products.ymlを作成したら、rakeタスク「db:fixtures:load」を実行して、サンプルデータを登録します。

$ rake db:fixtures:load
(in /home/staff/shida/public_html/rails/rjs_tutorial)

 商品「ふくろう」とひも付けられたカテゴリの情報を確認してみましょう。

$ sqlite3 db/development.sqlite3
sqlite> .mode line
sqlite> SELECT categories.*
        FROM categories, products
        WHERE categories.id=products.category_id
        AND products.name='ふくろう';

id = 523709510
name = 動物Tシャツ
description = さまざまな動物の写真をあしらったTシャツです
created_at = 2008-09-29 03:31:56
updated_at = 2008-09-29 03:31:56

 これで、商品とカテゴリをひも付けながら商品を登録できました。scaffoldが生成される商品一覧画面を見てみましょう。

図 商品一覧画面 図 商品一覧画面

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。