久しぶりに仕事でPrimeFacesを触るようになったので、仕事ネタからメモ書き。
PrimeFacesのTreeTableで利用するTreeNode(org.primefaces.model.TreeNode)では、そのノードの展開状態に関する属性を持っており、isExpanded()/setExpanded(boolean bln)で設定できます。
※ただし、画面更新の際(多分Ajax関係なく)にTreeTable内のノード展開状態がうまくキープされないことがあって(ViewScopedでも駄目)、細かい制御が必要な場合は注意が必要です。今は自分で展開状態を別途キープしたりしていて、何かと厄介です。
単純にTreeTableのノードをすべて展開して開いたり、折りたたんで閉じるのはわりと簡単で、上記プロパティをすべてのノードに設定すればいけます。
ルートのノードが以下のように定義されていたとして
/** * ツリーのルート. */ @Getter private TreeNode root;
再帰的に展開状態を設定するメソッドを定義
/** * 再帰的にすべてのノードに展開状態を設定する. * @param node 対象となるノード * @param isExpand 展開状態 */ private void recursiveSetExpand(TreeNode node, boolean isExpand){ if(node.getChildCount() > 0){ for(TreeNode child : node.getChildren()){ child.setExpanded(isExpand); recursiveSetExpand(child, isExpand); } } }
すべて展開する場合は
/** * ツリーノードを全て展開する. */ public void allExpand(){ recursiveSetExpand(root, true); }
すべて折りたたむ場合は
/** * ツリーノードを全て折りたたむ. */ public void allClose(){ recursiveSetExpand(root, false); }
といった感じに。
サンプルで作ったプログラムは以下のような感じです。
TreeTableが表示されて
マウスの右クリックでメニュー表示させて「ノードを全て開く」を選ぶとTreeTableのノードが全て展開されます。
再度右クリックして今度は「ノードを全て閉じる」を選びます。
すると全て閉じます。
再帰的にすべてのノードを閉じているので、展開してみても、中は全て閉じてます。
堀北さんを選んでみても、その中は全て閉じてます。
画面定義は以下。
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui"> <h:head> <title>バイオグラフィーツリー</title> </h:head> <h:body> <h:form prependId="false"> <!-- ContextMenuで右クリックのメニュー定義 --> <p:contextMenu for="biographyTree"> <p:menuitem value="ノードを全て開く" action="#{treeTableBean.allExpand()}" update="biographyTree" /> <p:menuitem value="ノードを全て閉じる" action="#{treeTableBean.allClose()}" update="biographyTree" /> </p:contextMenu> <!-- TreeTableの定義 --> <p:treeTable id="biographyTree" value="#{treeTableBean.root}" var="bio" selectionMode="single" selection="#{treeTableBean.selectedNode}"> <p:column headerText="カテゴリ"> <p:outputLabel value="#{bio.category}" /> </p:column> <p:column headerText="年"> <p:outputLabel value="#{bio.date}" /> </p:column> <p:column headerText=""> <h:graphicImage rendered="#{bio.imageName != null}" url="resources/images/#{bio.imageName}" /> </p:column> </p:treeTable> </h:form> </h:body> </html>
上記ビュー定義では、selectionModeを定義してますが、これがないとContextMenuは効かないようになっているようです。
定義しなかった場合、以下のように普通の右クリックになります。
CDI管理Beanは
package jp.co.hoge.treetable; import java.io.Serializable; import java.time.LocalDate; import java.time.Month; import javax.annotation.PostConstruct; import javax.faces.view.ViewScoped; import javax.inject.Named; import lombok.Getter; import lombok.Setter; import org.primefaces.model.DefaultTreeNode; import org.primefaces.model.TreeNode; /** * TreeTableのサンプル * @author kikuta */ @Named @ViewScoped public class TreeTableBean implements Serializable{ /** * ツリーのルート. */ @Getter private TreeNode root; /** * 選択されたノード. */ @Getter @Setter private TreeNode selectedNode; @PostConstruct public void init(){ root = new DefaultTreeNode("", null); TreeNode actress = new DefaultTreeNode(new Biography("若手女優", null, null), root); TreeNode kasumin = new DefaultTreeNode(new Biography("有村架純", null, null), actress); TreeNode kasuminBooks = new DefaultTreeNode(new Biography("写真集", null, null), kasumin); TreeNode kasuminFirstBook = new DefaultTreeNode(new Biography("深呼吸-Shin・Kokyu-", LocalDate.of(2013, Month.OCTOBER, 7), null), kasuminBooks); TreeNode kasuminTv = new DefaultTreeNode(new Biography("テレビ", null, null), kasumin); TreeNode makimaki = new DefaultTreeNode(new Biography("堀北真希", null, null), actress); TreeNode makimakiBooks = new DefaultTreeNode(new Biography("写真集", null, null), makimaki); TreeNode horikitaSinkaron = new DefaultTreeNode(new Biography("ほりきた進化論", LocalDate.of(2004, Month.APRIL, 15), "horikitaShinkaron.jpg"), makimakiBooks); TreeNode hikoukigumo = new DefaultTreeNode(new Biography("ひこうきぐも", LocalDate.of(2005, Month.JULY, 15), "hikoukigumo.jpg"), makimakiBooks); TreeNode makimakiTv = new DefaultTreeNode(new Biography("テレビ", null, null), makimaki); TreeNode zenigata = new DefaultTreeNode(new Biography("ケータイ刑事 銭形舞", LocalDate.of(2003, Month.OCTOBER, 1) , "zenigatamai.jpg"), makimakiTv); TreeNode movie = new DefaultTreeNode(new Biography("映画", null, null), makimaki); TreeNode sibuyakaidan = new DefaultTreeNode(new Biography("渋谷怪談2", LocalDate.of(2004, Month.FEBRUARY, 1), null), movie); TreeNode commercial = new DefaultTreeNode(new Biography("CM", null, null), makimaki); } /** * ツリーノードを全て展開する. */ public void allExpand(){ recursiveSetExpand(root, true); } /** * ツリーノードを全て折りたたむ. */ public void allClose(){ recursiveSetExpand(root, false); } /** * 再帰的にすべてのノードに展開状態を設定する. * @param node 対象となるノード * @param isExpand 展開状態 */ private void recursiveSetExpand(TreeNode node, boolean isExpand){ if(node.getChildCount() > 0){ for(TreeNode child : node.getChildren()){ child.setExpanded(isExpand); recursiveSetExpand(child, isExpand); } } } }
これはまだ序の口で、指定以下のノードのみ展開とか、一番末端のグループはくくったままに展開とか…。
ツリー系って便利だけど、お客さんの要望が熱くなりがちなのであまり好きではないんですよねぇ…ボソリ