[英]How do I implement a NamingContainer? All children get the same client ID
我嘗試編寫自己的樹組件。 樹節點呈現為包含樹組件的子組件的 div,例如:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</my:tree>
到目前為止,一切都很好 - 一切都按預期工作,但h:outputText
反復獲得相同的 id。
所以我讓我的組件實現javax.faces.NamingController
,覆蓋getContainerClientId()
:
@Override
public String getContainerClientId(FacesContext context) {
String clientId = super.getClientId(context);
String containerClientId = clientId + ":" + index;
return containerClientId;
}
在節點迭代期間設置和更新index
。 但是getContainerClientId()
只為每個孩子調用一次(不是像我期望的那樣為每個迭代和每個孩子調用)。 這會導致每個子 ID 都以相同的容器 ID 為前綴:
form:treeid:0:xxx
覆蓋getClientId()
。
我錯過了什么?
答案隱藏在JSF 1.2 規范第 3.1.6 章的底部:
3.1.6 客戶端標識符
...
除非調用
setId()
,否則從該方法返回的值必須在組件實例的整個生命周期中相同,在這種情況下,它將在下一次調用getClientId()
重新計算。
換句話說, getClientId()
的結果默認由 JSF 組件緩存,如UIComponentBase#getClientId()
(另請參見第 275 行的 nullcheck, 因為它在 Mojarra 1.2_15 中)並且當UIComponentBase#setId()
重置此緩存時UIComponentBase#setId()
被調用(另見第 358 行,因為它在 Mojarra 1.2_15 中)。 只要您不重置緩存的客戶端 ID,它就會在每次getClientId()
調用時返回相同的值。
因此,在您的組件或渲染器的encodeChildren()
實現中渲染子項時,最有可能看起來像這樣,
for (UIComponent child : getChildren()) {
child.encodeAll(context);
}
你應該為每個孩子調用UIComponent#setId()
與UIComponent#getId()
的結果在編碼孩子之前重置內部緩存的客戶端 ID:
for (UIComponent child : getChildren()) {
child.setId(child.getId());
child.encodeAll(context);
}
<h:dataTable>
實現背后的UIData
類也這樣做了(參見Mojarra 1.2_15 中的第 1382 行)。 請注意,這不是 JSF 1.x 特定的,這同樣適用於 JSF 2.x(以及 Facelets <ui:repeat>
后面的UIRepeat
類)。
值得一提的是,如果您的組件的子組件有子組件,那么也可能需要刷新它們的緩存 ID。 有了這個標記,稍微改編自原文:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:panelGroup layout="block" id="nodeBlock">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</h:panelGroup>
</my:tree>
在應用上面的 BalusC 修復后, <panelGroup>
的 id 出來了,但所有子組件在迭代器中都以 0 出來。
為了解決這個問題,也遍歷所有級別的子級並刷新它們的緩存 ID。 所以: child.setId(child.getId());
變成uncacheId(child);
其中定義了uncacheId
函數:
private void uncacheId(UIComponent el) {
el.setId(el.getId());
el.getChildren().forEach(this::uncacheId);
}
這可能很明顯,但我花了一段時間才弄明白,所以......
h:outputText id 為您提供與您沒有使其動態相同的信息。 您可以像這樣創建它:
<h:outputText id="xxx_#{node.id}" value="#{node.name}" />
假設節點有一個唯一的“id”屬性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.