何时以及如何在 JSF 中生成 clientID?

When and how is clientID generated in JSF?

为了更好地理解 JSF 中的 clientID 生成 (2.x),有人可以向我解释 JSF 究竟何时生成客户端 ID(生命周期阶段、构建时间或渲染时间...) ?

以及如何生成客户端 ID(如果提供/不提供组件 ID 而不是,随机或使用特定逻辑...)?

In order to more understand the clientID generation in JSF (2.x), could someone explain to me when exactely does JSF generates the client ID (which lifecycle phase, build time or render time ...)?

它必须以 HTML 响应结束。因此,它是在渲染响应期间生成的。如果您在 UIComponent#getClientId() 方法上放置一个调试断点,那么您将在堆栈的更下方看到(在 Mojarra 的情况下)RenderResponsePhase#execute() 已被调用。这足以提示命中断点时生命周期中的当前阶段。

And how the client ID will be generated (if providing /not providing component ID and not, randomly or using a specific logic...)?

抽象UIComponent#getClientId()方法的具体实现见UIComponentBase#getClientId(). Its source code can be found at grepcode. How it will be generated is just described in its javadoc:

public abstract String getClientId(FacesContext context)

Return a client-side identifier for this component, generating one if necessary. The associated Renderer, if any, will be asked to convert the clientId to a form suitable for transmission to the client.

The return from this method must be the same value throughout the lifetime of the instance, unless the id property of the component is changed, or the component is placed in a NamingContainer whose client ID changes (for example, UIData). However, even in these cases, consecutive calls to this method must always return the same value. The implementation must follow these steps in determining the clientId:

Find the closest ancestor to this component in the view hierarchy that implements NamingContainer. Call getContainerClientId() on it and save the result as the parentId local variable. Call getId() on this component and save the result as the myId local variable. If myId is null, call context.getViewRoot().createUniqueId() and assign the result to myId. If parentId is non-null, let myId equal parentId +UINamingContainer.getSeparatorChar(javax.faces.context.FacesContext)+ myId. Call Renderer.convertClientId(javax.faces.context.FacesContext, java.lang.String), passing myId, and return the result.

很清楚,是吗?最重要的部分可能是记住实现 NamingContainer 的组件,从而在它们的客户端 ID 前面加上。在标准 JSF 2.x 中,至少有 <h:form><h:dataTable><ui:repeat><f:subview><cc:implementation>If you gently give all components a fixed ID,然后您还会在生成的 HTML 输出中看到该模式。

如果您不给这些组件一个固定的 ID,那么将使用 JSF 生成的 ID,可以通过 UIViewRoot#createUniqueId() (as already hinted in the above javadoc extract). Its javadoc 获得 says:

public String createUniqueId()

Generate an identifier for a component. The identifier will be prefixed with UNIQUE_ID_PREFIX, and will be unique within the non-NamingContainer child sub-trees of this UIViewRoot.

那个前缀是j_id. It isn't explicit on how the implementation should generate it, so all implementors are free to implement it. They usually use an incremented index of the component count in the tree. So, the first component, the UIViewRoot, could get an ID of j_id1. Its first child could get an ID of j_id2. Etcetera. You can track back the logic by putting a debug breakpoint on UIViewRoot#createUniqueId() method or even the UIComponentBase#setId()方法。

另请参阅:

  • How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
  • By default, JSF generates unusable ids, which are incompatible with css part of web standards
  • Avoiding duplicate ids when reusing facelets compositions in the same naming container