改修プロジェクト。
もともとツリービューがあったのだけど、レスポンスが遅いと。
もとはDestroydrop » Javascripts » Treeを参考に
プロトタイプを作ってビューを生成していた。
要素の数だけnewしていたため、とても遅かった。
サーバーサイドでJSON形式にして、そのままツリーに利用できないか考えていたけど、Yahoo! UI LibraryがまさにJSONからツリーを生成するクラスを提供していた。
http://developer.yahoo.com/yui/treeview/
<html> <head> <link rel="StyleSheet" href="css/treeview.css" type="text/css" /> <script type="text/javascript" src="js/yahoo-dom-event.js"></script> <script type="text/javascript" src="js/treeview-min.js"></script> <style type="text/css"> .icon-root { display:block; height: 22px; padding-left: 20px; background: transparent url(img/check0.gif) 0 0px no-repeat; } .icon-folder { display:block; height: 22px; padding-left: 20px; background: transparent url(img/check1.gif) 0 0px no-repeat; } .icon-leaf { display:block; height: 22px; padding-left: 20px; background: transparent url(img/check2.gif) 0 0px no-repeat; } </style> </head> <body> <div id="treeDiv1"> </div> <script type="text/javascript"> //global variable to allow console inspection of tree: var tree; //anonymous function wraps the remainder of the logic: (function() { //function to initialize the tree: function treeInit() { buildRandomTextNodeTree(); } //Function creates the tree and function buildRandomTextNodeTree() { //instantiate the tree: tree = new YAHOO.widget.TreeView("treeDiv1", [ {type:"text", label:"List 0", labelStyle:"ygtvlabel icon-root", expanded:true, hasIcon:false, children: [ {type:"text", label:"List 0-0", labelStyle:"ygtvlabel icon-folder", children: [ "item 0-0-0", "item 0-0-1" ]}, {type:"text", label:"item 0-1", labelStyle:"ygtvlabel icon-folder", children: [ {type:"text", label:"elsewhere", labelStyle:"ygtvlabel icon-leaf", children: [ "item 0-1-0", "item 0-1-1" ]} ]} ]} ]); //The tree is not created in the DOM until this method is called: tree.draw(); tree.subscribe('clickEvent',function(e) { alert(e.node.label); }); } //Add an onDOMReady handler to build the tree when the document is ready YAHOO.util.Event.onDOMReady(treeInit); })(); </script> </html>
基本的にはTreeViewをnewするときの第2引数にJSONを渡す。
プロパティとしてlabelがツリーの項目名、typeがNodeの種類(textならTextNode。ほかにHtmlNodeもある)、childrenが子ノードとなる。
実際にJavaではこういうフィールドを持ったJavaBeansを作った。
private String type = "text"; private String label; private XxxDto data; private String labelStyle; private boolean expanded; private boolean hasIcon = true; private List children;
dataはイベントリスナーでNodeが渡されたときに利用するデータを入れておく。複数あるならDtoにして、いくつかのフィールドを定義しておけばいい。
expandedをtrueにすると最初からツリーが開いている。
hasIconは十字のアイコンに当たる部分の有無を表す。
JavaオブジェクトからJSONへの変換は、今回Maven - Json-lib::Welcomeを使った。
JSONIC - simple json encoder/decoder for javaの方が使いやすそうだったけど、JDKが1.5以降のみ対応だったため、Json-libを使った。
ちなみにJson-libでの変換は簡単だった。
String[] excluedeProperties = {"id"}; JsonConfig config = new JsonConfig(); config.setExcludes(excluedeProperties); JSONArray json = JSONArray.fromObject(root, config);
出力したくないプロパティは、JsonConfigに配列で渡す。
あとはJSONArray.fromObject()にJSONにしたいオブジェクトとConfigを渡すだけ。
.ygtvitem { } .ygtvitem table { margin-bottom: 0; border: none; } .ygtvrow td { border: none; padding: 0; } .ygtvrow td a { text-decoration: none; } .ygtvtn { width: 18px; height: 22px; background: url(../img/treeview-sprite.gif) 0 -5600px no-repeat; } .ygtvtm { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -4000px no-repeat; } .ygtvtmh,.ygtvtmhh { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -4800px no-repeat; } .ygtvtp { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -6400px no-repeat; } .ygtvtph,.ygtvtphh { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -7200px no-repeat; } .ygtvln { width: 18px; height: 22px; background: url(../img/treeview-sprite.gif) 0 -1600px no-repeat; } .ygtvlm { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 0px no-repeat; } .ygtvlmh,.ygtvlmhh { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -800px no-repeat; } .ygtvlp { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -2400px no-repeat; } .ygtvlph,.ygtvlphh { width: 18px; height: 22px; cursor: pointer; background: url(../img/treeview-sprite.gif) 0 -3200px no-repeat; } .ygtvloading { width: 18px; height: 22px; background: url(../img/treeview-loading.gif) 0 0 no-repeat; } .ygtvdepthcell { width: 18px; height: 22px; background: url(../img/treeview-sprite.gif) 0 -8000px no-repeat; } .ygtvblankdepthcell { width: 18px; height: 22px; } .ygtvchildren { } * html .ygtvchildren { height: 2%; } .ygtvlabel,.ygtvlabel:link,.ygtvlabel:visited,.ygtvlabel:hover { margin-left: 2px; text-decoration: none; background-color: white; cursor: pointer; } .ygtvcontent { cursor: default; } .ygtvspacer { height: 22px; width: 12px; } .ygtvfocus { background-color: #c0e0e0; border: none; } .ygtvfocus .ygtvlabel,.ygtvfocus .ygtvlabel:link,.ygtvfocus .ygtvlabel:visited,.ygtvfocus .ygtvlabel:hover { background-color: #c0e0e0; } .ygtvfocus a,.ygtvrow td a { outline-style: none; } .ygtvok { width: 18px; height: 22px; background: url(../img/treeview-sprite.gif) 0 -8800px no-repeat; } .ygtvok:hover { background: url(../img/treeview-sprite.gif) 0 -8844px no-repeat; } .ygtvcancel { width: 18px; height: 22px; background: url(../img/treeview-sprite.gif) 0 -8822px no-repeat; } .ygtvcancel:hover { background: url(../img/treeview-sprite.gif) 0 -8866px no-repeat; } .ygtv-label-editor { background-color: #f2f2f2; border: 1px solid silver; position: absolute; display: none; overflow: hidden; margin: auto; z-index: 9000; } .ygtv-edit-TextNode { width: 190px; } .ygtv-edit-TextNode .ygtvcancel,.ygtv-edit-TextNode .ygtvok { border: none; } .ygtv-edit-TextNode .ygtv-button-container { float: right; } .ygtv-edit-TextNode .ygtv-input input { width: 140px; } .ygtv-edit-DateNode .ygtvcancel { border: none; } .ygtv-edit-DateNode .ygtvok { display: none; } .ygtv-edit-DateNode .ygtv-button-container { text-align: right; margin: auto; }
クラス名が非常にわかりづらいけど、意味はこうらしい。
Icon styles:
* ygtvtn: First or middle sibling, no children
YUI 2: TreeView
* ygtvtm: First or middle sibling, collapsible
* ygtvtmh: First or middle sibling, collapsible, hover state
* ygtvtp: First or middle sibling, expandable
* ygtvtph: First or middle sibling, expandable, hover state
* ygtvln: Last sibling, no children
* ygtvlm: Last sibling, collapsible
* ygtvlmh: Last sibling, collapsible, hover state
* ygtvlp: Last sibling, expandable
* ygtvlph: Last sibling, expandable, hover state
* ygtloading: Loading dynamic data indicator
結果としてはレスポンスもすごく早くなった。