====== Dynamic Select box from JSON ======
このページではツリー構造のリストをselectボックスで表示させて、さらにツリーの親子関係にあるselectボックスを連動
させる方法を説明する。\\
今回の例では、表示するデータを**JSON**(%%JavaScript Object Notation%%)で保存してある。
ツリーの構造は**%%Dojo Campus%%**(([[http://dojocampus.org/explorer/#Dijit_Tree_Basic|Digit Tree Component]]))から確認できる。
あるいは、次節で示す**JSON**データを**JSON Editor**(([[http://braincast.nl/samples/jsoneditor/|JSON Editor]]))にコピーして確認することも可能である。
{{keywords>Dynamic select box json dojo tree}}
===== JSON データ =====
今回のexampleで使用したJSONデータを次に示す。テストの為、**%%Dojo Campus%%**に載っているデータに加えて"Cairo"を追加した。
{ identifier: 'name',
label: 'name',
items: [
{ name:'Africa', type:'continent', index: '1',
children:[{_reference:'Egypt'}, {_reference:'Kenya'}, {_reference:'Sudan'}] },
{ name:'Egypt', type:'country', index: '1.1',
children:[{_reference:'Cairo'}]},
{ name:'Cairo', type:'city', index: '1.1.1'},
{ name:'Kenya', type:'country', index: '1.2',
children:[{_reference:'Nairobi'}, {_reference:'Mombasa'}]},
{ name:'Nairobi', type:'city', index: '1.2.1'},
{ name:'Mombasa', type:'city', index: '1.2.2'},
{ name:'Sudan', type:'country', index: '1.3',
children:[{_reference:'Khartoum'}]},
{ name:'Khartoum', type:'city', index: '1.3.1'},
{ name:'North America', type:'continent', index: '2',
children:[{_reference:'Mexico'}, {_reference:'Canada'}, {_reference:'United States of America'}] },
{ name:'Mexico', type:'country', index: '2.1',
children:[{_reference:'Mexico City'}, {_reference:'Guadalajara'}]},
{ name:'Mexico City', type:'city', index: '2.1.1'},
{ name:'Guadalajara', type:'city', index: '2.1.2'},
{ name:'Canada', type:'country', index: '2.2',
children:[{_reference:'Ottawa'}, {_reference:'Toronto'}]},
{ name:'Ottawa', type:'city', index: '2.2.1'},
{ name:'Toronto', type:'city', index: '2.2.2'},
{ name:'United States of America', type:'country', index: '2.3'},
{ name:'Asia', type:'continent', index: '3',
children:[{_reference:'China'}, {_reference:'India'}, {_reference:'Russia'}, {_reference:'Mongolia'}] },
{ name:'China', type:'country' , index: '3.1'},
{ name:'India', type:'country', index: '3.2'},
{ name:'Russia', type:'country', index: '3.3'},
{ name:'Mongolia', type:'country', index: '3.4' },
{ name:'Australia', type:'continent', index: '4',
children:{_reference:'Commonwealth of Australia'}},
{ name:'Commonwealth of Australia', type:'country', index:'4.1'},
{ name:'Europe', type:'continent', index: '5',
children:[{_reference:'Germany'}, {_reference:'France'}, {_reference:'Spain'}, {_reference:'Italy'}] },
{ name:'Germany', type:'country', index:'5.1' },
{ name:'France', type:'country', index: '5.2' },
{ name:'Spain', type:'country', index: '5.3' },
{ name:'Italy', type:'country', index: '5.4' },
{ name:'South America', type:'continent', index: '6',
children:[{_reference:'Brazil'}, {_reference:'Argentina'}] },
{ name:'Brazil', type:'country', index:'6.1' },
{ name:'Argentina', type:'country', index:'6.2' }
]}
ここで、注目すべきところはツリーの階層データを区別する**type**があるということだ。\\
"continent"-"country"-"city"の順番の階層になっている。\\
ちなみに、「name」はデータの識別子(identifier)であり、データベースでいうと**Primary Key**に該当するもので考えてよい。 \\
それはnameが重なってしまうとツリーで唯一のデータを検索できる手立てが無いからである。
===== 画面layout =====
画面のレイアウトは極シンプルでツリーの代わりに,selectボックスが3個並んである。\\
{{:study:javascript:dojo:select.jpg|dynamic select box}}
画面のソースを次に示す。
ここで、空のoptionを追加したのはIEとの交換性の為である。optionがないと正常に動かないので、
注意する必要がある。
===== 初期リストの表示 =====
ここではページが最初に表示された際に初期リストを出力する仕組みを説明する。
大まかに、二段階が必要である。 まずはサーバー側からの**JSON**データを読み込む準備段階である。
dojo.require("dojo.data.ItemFileReadStore");
//JSONデータの読込み
var store = new dojo.data.ItemFileReadStore({url:'../country.json'});
ここで、利用するのがdojoのコアパッケージにある**%%ItemFileReadStore%%**である。
次は読み込んだ**JSON**から初期リストを出力するコードを示す。
function init(){
//continent pulldown list
dojo.byId('continent').innerHTML="";
store.fetch( { query: { type: 'continent' },
onComplete: function( items ) {
dojo.forEach( items, function(item, count) {
var opt=document.createElement('option');
opt.appendChild(document.createTextNode(store.getValue( item, "name" )));
opt.setAttribute('value',store.getValue( item, 'index'));
dojo.byId('continent').appendChild(opt);
});
},
onError: function(e) {
console.error( "!!!!",arguments );
},
sort: sortAttributes
});
showFirstChildren('continent','country');
showFirstChildren('country','city');
}//init
ツリーの一番上の階層に当たる**contient**を表示し、次は**continent**の先頭データから**country**階層の子供を出力する。\\
次は**country**の先頭データから**city**階層の子供を出力するといった具合だ。
**%%showFirstChildren%%**のメッソドを次に示す。
function showFirstChildren(parent, child){
dojo.byId(child).innerHTML="";
store.fetch( { query: { type: parent },
onComplete: function( items ) {
dojo.forEach( items, function( item, count ) {
var children = store.getValues( item, "children");
if(count==0){
dojo.forEach( children, function(node) {
opt=document.createElement('option');
opt.appendChild(document.createTextNode(store.getValue( node, "name" )));
opt.setAttribute('value',store.getValue( node, 'index'));
if(node.type==child) dojo.byId(child).appendChild(opt);
});
}//if
});
},
onError: function(e) {
console.error( "!!!!",arguments );
},
sort: sortAttributes
});
}//showFirstChildren
基本的には先頭データの子供を出す為、ループのindex番号であるcountが0の場合のみ、子供の階層に当たるデータのループ処理に
入るのを注目してほしい。
===== 子供リストの出力 =====
次は親階層のselectボックスの値が変わるたび、子供リストを動的に出力する部分を説明する。\\
ここでは、基本的に自分の子供のみ考慮すればよい。
function onChange(item, parent, child){
var val = item.options[item.selectedIndex].text;
store.fetch( { query: { type: parent, name:val },
onComplete: function( items ) {
dojo.forEach( items, function( item ) {
dojo.forEach(child, function(e){
if(e) dojo.byId(e).innerHTML="";
});
var children = store.getValues( item, "children");
dojo.forEach(children, function(node){
var opt=document.createElement('option');
opt.appendChild(document.createTextNode(store.getValue( node, "name" )));
opt.setAttribute('value',store.getValue( node, 'index'));
dojo.forEach(child, function(e){
if(e) dojo.byId(e).appendChild(opt);
});
});
});
},
onError: function(e) {
console.error( "!!!!",arguments );
},
sort: sortAttributes
});
}
ここで、注目するところは選択された値の識別子と**JSON**の親階層(parent)の識別子を比較して、該当する親の子供階層のみ表示することである。
===== イベントの登録 =====
最後にイベントの登録について説明する。ここでは、親階層のデータが変わるたびに(onchange)に子供が変わるので、一見自分の子供だけ考えれば
いいと思うかちだが、実はそうではない。\\
なぜなら、”continent”階層が変わると"country"が変わるし、それに連動して"city"階層も変わるからである。\\
要するに、自分の階層の下にある全階層にイベントを登録する必要があると言うことだ。\\
イベント登録コードの一部を次に示す。
dojo.addOnLoad( function() {
init();
dojo.connect(dojo.byId('continent'),'onchange',function(e){
onChange(e.target, 'continent','country');
onChange(dojo.byId('country'), 'country','city');
});
dojo.connect(dojo.byId('country'),'onchange',function(e){
onChange(e.target, 'country','city');
});
});
===== Test Page =====