深入討論JSF中Tree2組件使用方法

來源:互聯網
上載者:User
js   Tree2組件使用HTML表格將你的資料呈現為一個樹。這個樹是動態:當使用者點擊它們時它們可以展開或者摺疊。該組件同時支援用戶端和服務端的互動方式,在用戶端互動時使用了JavaScript。在隨後的例子中,每次使用者的點擊將產生一個Request / Response 周期,並在新的檢視狀態(View State)中重新呈現新的樹結構。

  註:在後面的例子中只有可見的(已經展開的節點)資料被傳送到用戶端。而在第一個例子(用戶端Tree),在每個HTML Response中,整個樹都被發送到用戶端瀏覽器。樹的每個節點都包含了不少的HTML代碼(假定每個節點200個字元,這個大小將取決於你希望在節點上顯示的資訊的量),這些資訊將被傳送到瀏覽器,其中包括了那些不可見的節點(沒有展開的節點),因為它們的一個祖系節點被展開。如果你有一個深度有四層的樹,平均每個結點擁有四個子結點,這時候你就需要傳輸10 + 102 + 103 + 104 = 11 110個節點,每個節點有200個字元,這個樹總共就有2 222 000個字元,也就是2M的資料。這個例子將向使用者說明,雖然純用戶端Tree會給用戶端帶來更好的使用者體驗,但隨之而來的頻寬問題迅速的增長。純用戶端的樹適用於小型的樹,或者在Intranet及寬頻連線中使用的中型大小的樹。對於大型的樹,或者你需要照顧到一些低頻寬的使用者的需要時,你就需要使用服務端樹。你可以通過的clientSideToggle這個屬性來選擇你使用的是用戶端的樹或者服務端的樹,<t:tree2>的clientSideToggle這個屬性來選擇你使用的是用戶端的樹或者服務端的樹,<t:tree2 clientSideToggle="false" ...>將會使用服務端的樹,屬性值設為true將會使用用戶端的樹,預設值為true。

  Backing Bean:

  Tree2組件對Backing Bean中的一個TreeModel進行操作。通常情況,你只要把這個TreeModel綁定到這個組件上就可以了,就像這樣:

<t:tree2 value="#{myHandler.treeModel}"
  下面需要建立一個類MyHandler,在faces-config.xml中的managed bean配置成myHandler,在例子中這個類提供了一個方法getTreeModel()用於返回一個TreeModel用於表示你的資料。

public class MyHandler {
 public TreeModel getTreeModel() {
  ......
 }
}
  TreeModel實際上是對TreeNode執行個體進行了一些簡單的封裝。

  TreeNode是一個介面,其中和tree2相關有四個方法:

String getType()
boolean isLeaf()
List getChildren()
int getChildCount()
  其它方法都沒有什麼用處了,可能會在今後的版本中取消。它們要求開發人員在開發backing bean中做一些並不必要的操作。

  int getChildCount() 方法返回這個節點的子結點數量,這個方法了很容易的採用如下的方式實現:

public final int getChildCount() {
return getChildren().size();
}
  該方法的出現使得對於子結點的消極式載入變的可行。該方法的實現只需要返回該節點的子結點數量,而不需要返回每個子結點的執行個體。

  boolean isLeaf() 方法在該節點沒有子節點的時候返回true。這樣,一個很直截了當的實現可以這樣:

public final boolean isLeaf() {
return getChildren().isEmpty();
}
  不管你提供了什麼樣的實現,在任何時間任何情況下你都得保持getChildren().isEmpty() ==> isLeaf()。isLeaf()方法實際上控制了節點被怎樣呈現:是否被當做樹分葉節點(不能被繼續展開)。

  String getType() 方法決定了用怎樣的方式來呈現這個節點。在 JSF 頁面中,可以在 <t:tree2> 的Tag 中嵌套facet,JSF將會選出與 getType() 方法傳回值同名的 facet 用於呈現。如果該節點沒有找到相符的 facet,將會導致一個錯誤,並且這個方法不會返回null。

  List getChildren() 方法返回一個 List,其中包含了該節點下所有的 TreeNode,這就表示這些節點將被呈現為該節點下的子結點。該 List 不能包含 null,如果該 List 的大小和getChildCount()不符,將會報錯。子結點將按照它們在 List 中的順序呈現出來。

  改變Tree中的內容

  (當展開樹的節點時,在後台消極式載入)

  在郵件清單中有很多關於這項任務的問題和討論,我(Marcel,一個 JSF 的初學者)在這裡總結一下。如果你有更好的解決方案,請更新這些文字。

  在這裡存在的一個問題就是我要這樣把“+”表徵圖去掉:

  · <t:tree2 ... showNav="false" ...>
 
  然後再讓檔案夾表徵圖(代表包含子節點的節點)變的可點擊:

  · <h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}">

  然後在 Java 代碼中接受滑鼠點擊的事件。在 NavigationBacker.java 檔案中的processAction(ActionEvent e) 方法裡,我從 EJB3-persistency 中載入子結點的資料。

  不好的是“+”表徵圖變的不可見,但是我現在沒有辦法擷取點擊“+”表徵圖的事件。

  看起來在org.apache.myfaces.custom.tree2.HtmlTree.java這個檔案裡是通過註冊了_expandControl = new HtmlCommandLink(); 從內部擷取“+”的點擊事件,但是我現在沒有辦法從My Code中接受到這一事件。

  為了導航,我使用了含有entries的TreeNode.getIdentifier() (參見:#{node.identifier}),看起來就是這個樣子:

  · db_id="car_id=7,person_id=2"

  這代表了後台資料庫表的主鍵(我還沒有找到一個更好的解決方案用於導航)

  程式碼如下:

  navigation.jsp

<t:tree2 id="serverTree" value="#{navigationBacker.treeData}"
var="node" varNodeToggler="t" clientSideToggle="false" showNav="false"
showRootNode="false">
<f:facet name="project-folder">
<h:panelGroup>
<h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}">
<t:graphicImage value="/images/yellow-folder-open.png"
rendered="#{t.nodeExpanded}" border="0" />
<t:graphicImage value="/images/yellow-folder-closed.png"
rendered="#{!t.nodeExpanded}" border="0" />
</h:commandLink>
<h:commandLink action="#{navigationBacker.toViewId}"
styleClass="#{t.nodeSelected ? 'documentSelected':'document'}"
actionListener="#{navigationBacker.nodeClicked}"
value="#{node.description}" immediate="true">
<f:param name="db_id" value="#{node.identifier}" />

</h:commandLink>
<h:outputText value=" (#{node.childCount})" styleClass="childCount"
rendered="#{!empty node.children}" />
</h:panelGroup>
</f:facet>
<f:facet name="person-folder">
<h:panelGroup>
  NavigationBacker.java

/** *//**
* 攔截節點被展開的事件,並載入額外的資料
* @param event
* @throws AbortProcessingException
*/
public void processAction(ActionEvent event) throws AbortProcessingException {
System.out.println("Entering processAction()");
UIComponent component = (UIComponent) event.getSource();
while (!(component != null && component instanceof HtmlTree)) {
 component = component.getParent();
}
if (component != null) {
 HtmlTree tree = (HtmlTree) component;
 TreeNodeBase node = (TreeNodeBase) tree.getNode();
 if (!tree.isNodeExpanded() && node.getChildren().size() == 0) {
  Map<String, String> map = splitKeyValues(node.getIdentifier()); // 一些輔助代碼,用於將 "car_id=7" 或 "car_id=7&person_id=12" 拆分開
  this.car_id = map.get("car_id");
  if (this.car_id != null) {
   appendPersonsNodes(node); // 參見下面的例子
  }
  this.person_id = map.get("person_id");
  if (this.person_id != null) {
   appendLicensesNodes(node); // 沒有顯示
  }
 }
}
}

/** *//** 把當前car_id下的Person子結點加入導航中 */
private void appendPersonsNodes(TreeNodeBase carDetailNode) {
 VariableResolver resolver = FacesContext.getCurrentInstance().getApplication().getVariableResolver();
 PersonsTable personsTable = (PersonsTable) resolver.resolveVariable(FacesContext.getCurrentInstance(),
"personsTable");
 List<Person> personsList = personsTable.getCarPersons();
 for (Person o : personsList) {
  List<TreeNodeBase> list = carDetailNode.getChildren();
  list.add(new TreeNodeBase("person-folder", o.getDescription(),"person_id=" + o.getPersonId(), true));
 }
 System.out.println("NavigationBacker fetched " + personsList.size() + " Persons for carId=" + this.car_id);
}
  這裡有一段輔助代碼用於從 h:commandLink 中擷取 f:param 用於多種用途。

/** *//**
* 當 JSF 組件 h:commandLink 包含有 f:param 成員, 這些 name-value 對被放到
* request 參數表中供後面的action handler使用。不幸的是,這樣的用法不能用在
* h:commandButton上。我們沒有辦法把通過 button 來傳遞這些參數。
*
* 因為 Action Listeners 可以保證在 Action 方法前被執行到,所以 Action Listeners
* 可以調用該方法更新 Action 方法所需要的任何上下文。
*
* From http://cvs.sakaiproject.org/release/2.0.0/
* sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java
* Educational Community License Version 1.0
*/
public static final Map getEventParameterMap(FacesEvent event) {
 Map<String, String> parameterMap = new HashMap<String, String>();
 List children = event.getComponent().getChildren();
 for (Iterator iter = children.iterator(); iter.hasNext();) {
  Object next = iter.next();
  if (next instanceof UIParameter) {
   UIParameter param = (UIParameter) next;
   parameterMap.put(param.getName(), "" + param.getValue());
  }
 }
 //System.out.println("parameterMap=" + parameterMap);
 return parameterMap;
}
  註:在上面的例子裡,backing bean都存放於 session 範圍裡,可以在WEB-INF/examples-config.xml 中進行配置。

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。