jQuery TreeView, Flexigrid を Rails で使ってみた
訳あって、jQuery プラグインの TreeView, Flexigrid を Ruby on Rails + jRails で使ってみました。以下の画像のように左のツーリー(TreeView)で選択したディレクトリー内のファイル一覧が 右側の テーブル(Flexgrid) に表示されます。
使っているソフトなど
- jQuery jQuery
- Flexigrid for jQuery クールなグリッド(テーブル)の表示
- jQuery plugin: Treeview ツーリー表示
- jRails - jQuery on Rails Railsのlink_to_remote等のAjax機能を jQueryで使えるようにするプラグイン
- nickfessel.com - Put Flexigrid on Rails Flexigrid用 Ruby on Railsのコード例
今回作ったコード
実際にディレクトリーを参照しているわけではなく、listsテーブルに ディレクトリー階層 dir1,dir2,dir3 と ファイル情報 file,size,mtime を持っていて、その内容を表示します。
View
layoutでは、jQueryやプラグインのJavascript,CSSを追加しています。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>Lists: <%= controller.action_name %></title> <%= stylesheet_link_tag 'scaffold' %> <%= stylesheet_link_tag 'flexgrid/flexigrid/flexigrid' %> <%= stylesheet_link_tag 'treeView/jquery.treeview.css' %> <%= javascript_include_tag 'jquery-1.2.6' %> <%= javascript_include_tag 'flexigrid' %> <%= javascript_include_tag 'jquery.treeview' %> </head> <body> <p style="color: green"><%= flash[:notice] %></p> <%= yield %> </body> </html>
テンプレート index.html.erb に TreeView, Frexgrid の設定が書かれています。
- Frexgridへのデータ表示は flexReload()を呼び出す事でブラウザー側からリクエストするようになっています。
- Frexgridはまだドキュメントが整備されていませんが、ソースを見るとアトリビュートは直ぐに判ります。
- FrexgridにURL指定でreloadするメソッド flexLoadUrl を定義しています (これで良いのかな?)
<h2>ファイル一覧</h2> <script type="text/javascript" charset="utf-8"> jQuery.fn.extend({ flexLoadUrl: function(load_url) { return this.flexOptions({url: load_url, newp: 1}).flexReload(); } }); $(document).ready(function() { $("#dir_tree").treeview({ animated: "fast", collapsed: true, unique: true, }); $("#file_list").flexigrid( { url: '', dataType: 'json', method: 'GET', colModel : [ {display: 'ファイル名', name : 'file', width : 220, sortable : true, align: 'left'}, {display: 'サイズ', name : 'size', width : 70, sortable : true, align: 'right'}, {display: '更新日時', name : 'mtime', width: 180, sortable : true, align: 'center'}, ], sortname: "file", sortorder: "asc", usepager: true, useRp: true, rp: 10, width: "auto", height: "auto", showToggleBtn: false, pagestat: '{from} 〜 {to} 件を表示 (全件:{total})', procmsg: 'データ取得中 ...', }); $('#file_area').hide(); }); </script> <div id="dir_area" style="margin-left: 10px; float:left; width: 240px;"> <h4 style="margin: 0 0 10px 0;">ディレクトリー選択</h4> <%= tree_view @tree, :id=>"dir_tree", :class=>"filetree", :span_class=>"folder", :list_id=>"file_list" %> </div> <div id="file_area" style="margin-left: 240px; padding-right:60px;"> <h4 style="margin: 0 0 10px 0;"></h4> <table id="file_list"></table> </div>
helper
TreeViewは <ul> <li> ... </ul> で作られた箇条書をツリーとして表示していますので ul 箇条書 を生成するコードをヘルパーに作りました。Railsの link_to_function APIでツリーをクリックした際に、 FlexGridでデータをロードするようにしています。通常のlink_to_function APIは Prototype.js用のコードを生成しますが、jRailsプラグインを使うと jQuery用のコードが生成されます。
module ListsHelper def tree_view(tree, opt = {}) ul_tag_tree(tree, opt, 1) end private def ul_tag_tree(tree, opt, level) html = " " * level + (level == 1 ? "<ul id='#{opt[:id]}' class='#{opt[:class]}'>\n" : "<ul>\n") while tree.first t = tree.first if t.level == level html << " " * level + " <li>" + link_to_function("<span class='#{opt[:span_class]}'>#{t.name}</span>", "$('##{opt[:list_id]}').flexLoadUrl('#{formatted_list_path(t.path, :json)}'); $('#file_area').show(); $('#file_area > h4').html('/#{t.path} ファイル一覧')") html << (tree[1] && tree[1].level == level ? "</li>\n" : "\n") tree.shift elsif t.level > level html << ul_tag_tree(tree, opt, level + 1) else break end end html + " " * level + "</li></ul>\n" end end
Controller
- index : まずは ディレクトリーのツリーを表示します
- show : Flexgrid からのリクエストで1ページ分のファイル一覧情報を JSON で戻します。
{"page": 1, "rows": [{"cell": ["excel_text.jar", 14674, "2008-04-28 22:22:11"]}, {"cell": ["poi-3.0.2-FINAL-20080204.jar", 964285, "2008-04-08 16:19:10"]}, {"cell": ["template.xls", 35328, "2008-04-08 16:19:10"]}], "total": 3}
class ListsController < ApplicationController def index @tree = List.find_tree_info() end def show lines = params[:rp].to_i page = params[:page].to_i @lists = List.find_by_path(params[:id], :order => params[:sortname] + ' '+ params[:sortorder], :limit => lines, :offset => (page - 1) * lines) count = List.count_by_path(params[:id]) data = {:page => page, :total => count, :rows => @lists.map {|u| {:cell => [u.file, u.size, u.mtime.strftime('%Y-%m-%d %H:%M:%S')]}}} render :json => data.to_json end end
Model
- find_tree_info : 下のようなディレクトリーツリーの情報を戻します。
[#<struct List::TreeInfo level=1, name="acl81", path="acl81">, #<struct List::TreeInfo level=2, name="acache", path="acl81/acache">, #<struct List::TreeInfo level=3, name="doc", path="acl81/acache/doc">, ....
- find_by_path : path で指定されたディレクトリー下のファイルの一覧を戻します。
- count_by_path : path で指定されたディレクトリー下のファイル数を戻します。
class List < ActiveRecord::Base TreeInfo = Struct.new(:level, :name, :path) def self.find_tree_info find_dir_tree([]) end def self.find_by_path(path, opt = {}) List.find(:all, opt.merge({:conditions => build_path_conditions(path)})) end def self.count_by_path(path, opt = {}) List.count(:all, opt.merge({:conditions => build_path_conditions(path)})) end private def self.build_path_conditions(path) cond = {} path.split(/\//).each_with_index {|e,ix| cond["dir#{ix + 1}"] = e} cond end def self.find_dir_tree(tree) cond = {} tree.each_with_index {|e,ix| cond["dir#{ix + 1}"] = e} level = tree.size + 1 dir_col = "dir#{level}" entries = List.find(:all, :select => "distinct #{dir_col}", :conditions => cond).map {|e| e[dir_col]} list = [] entries.select {|e| e != ""}.each {|e| down_tree = tree + [e] list.push(TreeInfo.new(level, e, down_tree.join('/'))) list.concat(find_dir_tree(down_tree)) if (level <= 2) } list end end
感想
- jQueryを使うのは初めてでしたがとても簡潔に書けていいですね。どこかに書いてありましたが、もう普通のJSを書く気がしなくなります ^^)
- jQueryの概要は jQuery を使って Ajax 開発を単純化する が良いと思いました。
- リファレンスとしては jQuery 1.2.6 日本語リファレンス が役に立ちました。
- jQuery にはたくさんのプラグインがあり、どれを選ぶかは時間がかかります。
- しかし、プラグインのページにはデモ等があるので動作や機能が判ります。
- また、ソースをザッと眺めてみるのも必要かも知れません、大規模ではない物の方が良いかもしれません。
- jQuery UI というものがありますが、プラグインとの関係が良く判りませんでした。 まだ、Table/GridやTree はありませんし・・・だれか教えて!!
- jRailsプラグインを入れると、 Rails の link_to_remote, link_to_function ... API が使えるようになるので便利です。しかし同梱されている jQuery.js等は 最新ではないので置き換えました。