在 JavaScript 中访问 JSF 嵌套复合组件元素

Accessing JSF nested composite component elements in JavaScript

我正在尝试使用复合组件在我的 JSF 2 项目中删除弹出窗口 windows。

此代码库使用 Icefaces 3.3.0(由于历史原因使用 1.8.2 兼容层)、Mojarra 2.2.7 和 Glassfish 4.1。

我有 input.xhtml,它提供文本输入并使用 2 按钮弹出窗口 (ok/cancel),它又建立在基本弹出窗口的基础上。

input.xhtml:

<composite:interface>
    <!-- ... -->
    <composite:editableValueHolder name="forInput" targets="theInput"/>
</composite:interface>
<composite:implementation>
    <my:popup2Buttons>
        <ice:inputText id="theInput" value="..."/>
        <script>setInputFocus("#{cc.clientId}:theInput");</script>
    </my:popup2Buttons>
</composite:implementation>         

popup2buttons.xhtml:

<composite:interface>
    <!-- ... -->
</composite:interface>
<composite:implementation>
    <my:popup>
        <composite:insertChildren/>
        <ice:commandButton id="OkButton"
             value="Ok"
             actionListener="..."/>
        <ice:commandButton id="CancelButton"
              value="Cancel"
              actionListener="..."/>
    </my:popup>                    
</composite:implementation>

popup.xhtml:

<composite:interface>
    <!-- ... -->
</composite:interface>
<composite:implementation>
    <script>
    function setInputFocus(id) {
        document.getElementById(id).focus();
    }
    </script>

    <ice:panelPopup>
        <f:facet name="body">
            <h:panelGroup>
                <composite:insertChildren/>
            </h:panelGroup>
        </f:facet>
    </ice:panelPopup>
</composite:implementation>

弹出窗口基本按预期工作,即我可以输入内容,确定和取消按钮有效,验证也有效。

我的 JavaScript 代码不起作用,它试图在弹出窗口打开时聚焦输入。

当我查看 Firebug 中的页面时,我看到输入的 ID 是 MyForm:j_idt63:j_idt64:j_idt67:theInput,但是 JavaScript 代码试图聚焦 ID 为 [=15 的元素=].

为什么 input.xhtml 中的 #{cc.clientId} 不是正确的 ID,输入最终会变得更晚?我需要做什么才能完成这项工作?

我见过 BalusC's hint on adding a binding 但我不想要绑定,以便复合组件可以独立于任何支持 bean。

我还遗漏了什么吗?

复合组件是隐式的 naming containers。 IE。他们将自己的 ID 添加到 children 的客户端 ID 前面。这使得在同一视图中使用多个它们成为可能,而它们的 children 不会在生成的 HTML 输出中导致重复 ID。

在您的特定情况下,您将输入字段包装在另一个复合材料中,而该复合材料又再次包装在另一个复合材料中。如果您绝对肯定在这个特定的组合中不需要多个命名容器相互包裹,那么那些(popup2buttons.xhtmlpopup.xhtml)可能不应该是复合的,而是 <ui:decorate> 模板或 <ui:composition> 标记文件。另见 When to use <ui:include>, tag files, composite components and/or custom components?

回到技术问题,是因为#{cc.clientId}引用的不是嵌套复合组件的ID,而是当前复合组件的ID。因此这将关闭。至于 binding 的潜在解决方案,您找到的答案并没有告诉您应该为此使用辅助 bean。答案中的 binding="#{foo}" 代码是 as-is。它确实是这样工作的,没有 bean 属性,另请参见 JSF component binding without bean property. However, this construct would indeed fail when you include the same composite multiple times in the same view and thus multiple components share the same binding="#{foo}". It indeed isn't supposed to be shared by multiple components, see also What is component binding in JSF? When it is preferred to be used?

要在没有支持 bean 的情况下解决这个问题,您可以使用 so-called 支持组件。

com.example.InputComposite

@FacesComponent("inputComposite")
public class InputComposite extends UINamingContainer {

    private UIInput input;

    // +getter+setter.
}

input.xhtml

<cc:interface componentType="inputComposite">
    ...
</cc:interface>
<cc:implementation>
    ...
    <h:inputText binding="#{cc.input}" ... />
    <script>setInputFocus("#{cc.input.clientId}");</script>
    ...
</cc:implementation>

另一种方法是将它们重新加工成模板或标记文件。小心不要 overvalue/overuse 合成。