jstreeを表示する、操作する

 jstreeは、ディレクトリ表示、組織図表示など木構造を持つデータをブラウザ上に表示するライブラリとして大変優れています。
 jstreeにどのような形式のデータを入れればよいか、また表示した木構造っで、インターラクティブに選択した枝葉を指定した枝に移動させることについて、調べてみました。

jstreeのデータ形式

 jstreeを描画するためのデータ形式として、親子関係が記述されているJSONデータが求められます。そのJSONデータには2つの形式があります。
 一つは、tree形式のJSONデータで、jstreeインスタンスのget_json()で得ることができます。このJSONデータの特徴は、treeデータに見られるparent属性がないことです。必須な属性は、id、children、textです。id属性はノードを識別するもので、ユニークでなければなりません。"id": "#"は予約語で、rootノードを示します。text属性は、表示するノードの呼称を記述します。children属性は、このノードから伸びる枝葉のノード情報を記述します。icon属性はノードを表示するためのアイコン画像を示します。この属性は必須ではありませんが、folderかfileであるかの区別ができ、見た目にも大変便利な属性です。ここにはありませんが、state属性を用いると、tree内のオープン・クローズの制御や要素の選択ができます。
 尚、jstreeオブジェクトのデータ形式の中にはparent属性があり、stree内を葉に行ったり、rootに行ったり、自由にtree内を走査することができます。一方で、jstreeオブジェクト形式のデータをjstreeに入れると、parent属性が影響して、jstree描画ができません。

tree形式のJSONデータ
let jsobj1 = {
   "id": "#",
   "icon": "jstree-folder",
   "text": "#",
   "children": [
      {
         "id": "ajson1",
         "icon": "jstree-folder",
         "text": "Simple root node",
         "children": [
         ]
      },
      {
         "id": "ajson2",
         "icon": "jstree-folder",
         "text": "Root node 2",
         "children": [
            {
               "id": "ajson3",
               "icon": "jstree-file",
               "text": "Child 1",
               "children": [
               ]
            },
            {
               "id": "ajson4",
               "icon": "jstree-file",
               "text": "Child 2",
               "children": [
               ]
            }
         ]
      }
   ]
};

 もう一つは、connection形式のJSONデータで、jstreeオブジェクトへのノードの追加する時に便利なデータ形式です。下記の例はシンプルなものです。属性としては、id、parent、textが必須となります。connection形式のデータでは、parent属性で、自己がどこのノードに属しているかを示します。他の属性は前述と同じです。
 データjsobj1、データjsobj2は、形式は異なるものの、両者とも同じjstreeを表示します。

connection形式のJSONデータ
let jsobj2 = [
  { "id" : "ajson1", "parent" : "#", "text" : "Simple root node", "icon": "jstree-folder" },
  { "id" : "ajson2", "parent" : "#", "text" : "Root node 2", "icon": "jstree-folder" },
  { "id" : "ajson3", "parent" : "ajson2", "text" : "Child 1", "icon": "jstree-file" },
  { "id" : "ajson4", "parent" : "ajson2", "text" : "Child 2", "icon": "jstree-file" }
];

jstreeの表示

jstreeの表示のための抜粋コードを次の通り示します。ポイントは次の通りです。
1.jstreeを描画する要素を設定します。ここでは、<div id="jstree1">、<div id="jstree11">、<div id="jstree2">と<div id="jstree22">の4つを用意しました。
2.jstreeのデータを設定ます。ここでは、形式の異なるjsondata1とjsondata2を用意しました。及び、jsondata1とjsondata2をそのままJSONファイルにしたjstree1.json、jstree2.jsonを用意しました。
3.jstree描画のコードを設定ます。ここではデータに対応する、関数createJSTree1、関数createJSTree11、関数createJSTree2、関数createJSTree22を用意しました。関数createJSTree1、関数createJSTree2は、javascript内に記述したjsondataからjstreeを描画します。関数createJSTree11、関数createJSTree22は、外部ファイルのjsonファイルからjstreeを描画します。
4.結果は全部その通り描画できたと言いたいところです。しかしながら、関数createJSTree22は、JSONデータjstree2.jsonをローディングしていますが、なぜか、jstreeの描画ができませんでした。

サンプルの抜粋コード デモ
jsondata1

jsondata11

jsondata2

jsondata22


特定の移動先への枝葉の移動 デモ

 jstreeは、表示したtree内で、contextmenu内のインターラクティブな操作(copy&paste、cut&paste、drag&drop)によって、木の形を変えることができます。プログラムを組んでいる中で、指定した枝葉をゴミ箱に移動したい、ゴミ箱の指定した枝葉を元に戻したい、このような必要性がありました。copyやcutは通常通りできますが、インターラクティブな指定なしにpasteすることができませんでした。
 正解かどうか分かりませんが、私の解決法は次の通りです。
 ・移動元はインターラクティブに選択
 ・選択したノードから、get_json()で、配下の枝葉情報moveInstを取得
 ・選択したノードの削除
 ・親ノードから選択したノードの削除情報をサーバーに送り、DB上のデータを削除
 ・枝葉情報moveInstはjstree形式のtreeデータをconnection形式のjsonデータに変換
 ・connection形式データの先頭のparent属性を移動先の情報に置き換える
 ・connection形式データを従い、create_node()で枝葉を作成


jstreeのためのデータ形式変換(tp.js)

 get_json()で得たjstreeデータは、下記のコードによってconnection形式のjstreeデータに変換します。

let connection;
function tree(pid, node) {
	connection = [];
	_tree(pid, node);
console.log('Connection\n', connection);
}

function _tree(pid, node) {
	if (node.icon == 'jstree-file') {
		connection.push({id: node.id, parent: pid, icon: node.icon, text: node.text});
	}
	else if (node.icon == 'jstree-folder') {
		if (pid != node.id) connection.push({id: node.id, parent: pid, icon: node.icon, text: node.text});
		pid = node.id;
		for (let i = 0; i < node.children.length; i++)
			_tree(pid, node.children[i]);
	}
}

tree('#', jstree);
2021/6/5 大坂 哲司