2

我正在尝试使用 JSF 2 构建一个自定义复合组件。我在这个组件中有一个对话框,我想在提交表单时显示该对话框,但是当我使用 appendToBody="false" 并且不工作时,该对话框永远不会显示我使用 appendToBody="true"。

我的搜索组件看起来像这样。

在此处输入图像描述

当用户在 inputText 中填写一些值并按下 seach 按钮时,应该会弹出如下对话框:

在此处输入图像描述

现在,我可以完成这项工作的唯一方法是在我的组件中使用两个按钮而不是一个。一个用于提交值,一个用于显示对话框。这是当我使用带有 appendToBody="false" 属性的对话框时。所以我的测试组件看起来像这样:

在此处输入图像描述

当我使用带有 appendToBody="true" 的对话框时,我可以一次提交并显示对话框,但是对话框还有其他问题,比如我无法关闭对话框。数据未更新等

这是我的代码(lov.xhtml):

<ui:component xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui"
    xmlns:composite="http://java.sun.com/jsf/composite">

    <composite:interface componentType="lov">
        <composite:attribute name="value" required="true" />
        <composite:attribute name="definitionFile" required="true" />
        <composite:attribute name="definitionName" required="true" />
    </composite:interface>

    <composite:implementation>
        <h:outputStylesheet library="css" name="styles.css" />
        <p:inputText id="lovInputText" styleClass="lovInputText"
            value="#{cc.selectedValue}" binding="#{cc.lovInputText}" />
        <p:commandButton styleClass="lovButton" icon="ui-icon-search"
            onclick="searchAndSelect.show();" actionListener="#{cc.updateDialog}"
            update="@form" />
        <p:commandButton value="Open" icon="ui-icon-search"
            onclick="searchAndSelect.show();" />
        <h:outputLabel id="outputLabel" value="#{cc.selectedValue}" />
        <p:dialog id="searchAndSelectDialog" header="Search and Select"
            appendToBody="false" closable="false" resizable="false"
            widgetVar="searchAndSelect" showEffect="fade" hideEffect="fade"
            binding="#{cc.searchAndSelectDialog}">
            <p:panelGrid>
                <p:row>
                    <p:column>
                        <h:outputLabel value="Value: " />
                    </p:column>
                    <p:column>
                        <h:form id="sasInputTextForm" prependId="true">
                            <p:inputText id="sasInputText" value="#{cc.selectedValue}"
                                label="Value" binding="#{cc.sasInputText}" />
                        </h:form>
                    </p:column>
                </p:row>
                <p:row>
                    <p:column colspan="2" styleClass="searchAndResetColumn">
                        <h:form>
                            <p:commandButton value="Search" icon="ui-icon-search"
                                actionListener="#{cc.search}" />
                            <p:commandButton type="button" value="Reset"
                                icon="ui-icon-arrowrefresh-1-e" />
                        </h:form>
                    </p:column>
                </p:row>
                <p:row>
                    <p:column colspan="2">
                        <h:form>
                            <p:dataTable var="item" value="#{cc.data}">
                                <p:columns value="#{cc.columns}" var="column"
                                    columnIndexVar="colIndex" sortBy="#{item[column.property]}"
                                    filterBy="#{item[column.property]}">
                                    <f:facet name="header">#{column.header}</f:facet>  
                            #{item[column.property]}
                        </p:columns>
                            </p:dataTable>
                        </h:form>
                    </p:column>
                </p:row>
            </p:panelGrid>
            <f:facet name="footer">
                <p:commandButton type="button" value="OK" icon="ui-icon-check" />
                <p:commandButton type="button" value="Cancel" icon="ui-icon-cancel"
                    onclick="searchAndSelect.hide();" />
            </f:facet>
        </p:dialog>
    </composite:implementation>
</ui:component>

这是组件支持 bean (Lov.java):

@FacesComponent("lov")
public class Lov extends UIInput implements NamingContainer {

    private List<ColumnModel> columns = new ArrayList<ColumnModel>();

    private String definitionFile;

    private String definitionName;

    private String bean;

    private String attribute;

    private List<String> displayAttributes;

    private UIInput lovInputText;

    private UIInput sasInputText;

    private UIComponent searchAndSelectDialog;

    // Fields
    // -------------------------------------------------------------------------------------

    // Actions
    // ------------------------------------------------------------------------------------

    /**
     * Returns the component family of {@link UINamingContainer}. (that's just
     * required by composite component)
     */
    @Override
    public String getFamily() {
        return UINamingContainer.COMPONENT_FAMILY;
    }

    /**
     * Set the selected and available values of the day, month and year fields
     * based on the model.
     */
    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        System.out.println("Called encodeBeing method...");
        System.out.println("getValue: "+ getValue().toString());
        setSelectedValue(getValue().toString());
        definitionFile = getAttributeValue("definitionFile", null);
        definitionName = getAttributeValue("definitionName", null);

        try {
            parseXml();
            queryData();
        } catch (Exception e) {
            e.printStackTrace();
        }

        createDynamicColumns();

        super.encodeBegin(context);
    }

    /**
     * Returns the submitted value in dd-MM-yyyy format.
     */
    @Override
    public Object getSubmittedValue() {
        System.out.println("====================================================");
        System.out.println("getSubmittedValue method called...");
        System.out.println("submittedValue: " + lovInputText.getSubmittedValue());
        System.out.println("localValue: " +lovInputText.getLocalValue());
        return lovInputText.getSubmittedValue();
    }

    /**
     * Converts the submitted value to concrete {@link Date} instance.
     */
    @Override
    protected Object getConvertedValue(FacesContext context,
            Object submittedValue) {
        return super.getConvertedValue(context, submittedValue);
    }

    public void search(ActionEvent actionEvent) {
        System.out.println("Search method called...");
        try {
            queryData();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Update the available days based on the selected month and year, if necessary.
     */
    public void updateDialog(ActionEvent actionEvent) {
        System.out.println("==========================================================");
        System.out.println("updateDialog method called...");
        System.out.println("getValue: "+getValue().toString());
        setSelectedValue(getValue().toString());
        /*FacesContext context = FacesContext.getCurrentInstance(); // Update dialog
        context.getPartialViewContext().getRenderIds().add(searchAndSelectDialog.getClientId(context));*/
    }

    // Helpers
    // ------------------------------------------------------------------------------------

    /**
     * Return specified attribute value or otherwise the specified default if
     * it's null.
     */
    @SuppressWarnings("unchecked")
    private <T> T getAttributeValue(String key, T defaultValue) {
        T value = (T) getAttributes().get(key);
        return (value != null) ? value : defaultValue;
    }

    /**
     * Create an integer array with values from specified begin to specified
     * end, inclusive.
     */
    private static Integer[] createIntegerArray(int begin, int end) {
        int direction = (begin < end) ? 1 : (begin > end) ? -1 : 0;
        int size = Math.abs(end - begin) + 1;
        Integer[] array = new Integer[size];

        for (int i = 0; i < size; i++) {
            array[i] = begin + (i * direction);
        }

        return array;
    }

    protected void parseXml() throws ParserConfigurationException,
            SAXException, IOException, XPathExpressionException,
            URISyntaxException {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setNamespaceAware(true);
        DocumentBuilder builder = dbFactory.newDocumentBuilder();
        ServletContext servletContext = (ServletContext) FacesContext
                .getCurrentInstance().getExternalContext().getContext();
        InputStream is = servletContext.getResourceAsStream(definitionFile);
        Document doc = builder.parse(is);
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();

        XPathExpression expr = xpath.compile(String.format(
                "/definitions/definition[@name='%s']/bean/@name",
                definitionName));
        Object result = expr.evaluate(doc, XPathConstants.STRING);
        bean = result.toString();
        setBean(bean);
        System.out.println("bean: " + bean);

        expr = xpath
                .compile(String
                        .format("/definitions/definition[@name='%s']/attributes/attribute/@name",
                                definitionName));
        result = expr.evaluate(doc, XPathConstants.STRING);
        attribute = result.toString();
        setAttribute(attribute);
        System.out.println("attribute: " + attribute);

        expr = xpath
                .compile(String
                        .format("/definitions/definition[@name='%s']/displayAttributes/attribute",
                                definitionName));
        result = expr.evaluate(doc, XPathConstants.NODESET);
        displayAttributes = new ArrayList<String>();
        NodeList nodes = (NodeList) result;
        for (int i = 0; i < nodes.getLength(); i++) {
            NamedNodeMap attrs = nodes.item(i).getAttributes();
            for (int j = 0; j < attrs.getLength(); j++) {
                String nodeName = attrs.item(j).getNodeName();
                if (nodeName.equalsIgnoreCase("name")) {
                    displayAttributes.add(attrs.item(j).getNodeValue());
                }
            }
        }
        setDisplayAttributes(displayAttributes);
        System.out.println("displayAttributes: " + displayAttributes.toString());
    }

    protected void queryData() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
        FilterableBean bean = (FilterableBean) findBean(getBean());
        if (lovInputText != null) {
            System.out.println("lovInputText exists");
        }
        System.out.println("queryData value: "+getValue().toString());
        setData(bean.get(getSelectedValue()));
        System.out.println("data: "+ getData().toString());
    }

    @SuppressWarnings("unchecked")
    public static <T> T findBean(String beanName) {
        FacesContext context = FacesContext.getCurrentInstance();
        return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
    }

    protected void createDynamicColumns() {
        if (displayAttributes != null) {
            columns.clear();

            for (String displayAttribute : displayAttributes) {
                String key = displayAttribute.trim();
                columns.add(new ColumnModel(key.toUpperCase(), key));
            }
        }
    }

    // Getters/setters
    // ----------------------------------------------------------------------------

    @SuppressWarnings("rawtypes")
    public List getData() {
        return (List) getStateHelper().get("data");
    }

    @SuppressWarnings("rawtypes")
    public void setData(List data) {
        getStateHelper().put("data", data);
    }

    private String hello = "hello";

    public String getHello() {
        return hello;
    }

    public void setHello(String hello) {
        this.hello = hello;
    }

    public List<ColumnModel> getColumns() {
        return columns;
    }

    public String getSelectedValue() {
        return (String) getStateHelper().get("selectedValue");
    }

    public void setSelectedValue(String selectedValue) {
        getStateHelper().put("selectedValue", selectedValue);
    }

    public UIInput getLovInputText() {
        return lovInputText;
    }

    public void setLovInputText(UIInput lovInputText) {
        this.lovInputText = lovInputText;
    }

    public UIInput getSasInputText() {
        return sasInputText;
    }

    public void setSasInputText(UIInput sasInputText) {
        this.sasInputText = sasInputText;
    }

    public UIComponent getSearchAndSelectDialog() {
        return searchAndSelectDialog;
    }

    public void setSearchAndSelectDialog(UIComponent searchAndSelectDialog) {
        this.searchAndSelectDialog = searchAndSelectDialog;
    }

    public String getBean() {
        return (String) getStateHelper().get("bean");
    }

    public void setBean(String bean) {
        getStateHelper().put("bean", bean);
    }

    public String getAttribute() {
        return (String) getStateHelper().get("attribute");
    }

    public void setAttribute(String attribute) {
        getStateHelper().put("attribute", attribute);
    }

    @SuppressWarnings("unchecked")
    public List<String> getDisplayAttributes() {
        return (List<String>) getStateHelper().get("displayAttributes");
    }

    public void setDisplayAttributes(List<String> displayAttributes) {
        getStateHelper().put("displayAttributes", displayAttributes);
    }

    static public class ColumnModel implements Serializable {

        private String header;
        private String property;

        public ColumnModel(String header, String property) {
            this.header = header;
            this.property = property;
        }

        public String getHeader() {
            return header;
        }

        public String getProperty() {
            return property;
        }
    }
}

这是我使用该组件的方式:

<h:form>
  <my:lov value="#{testBean.region}" definitionFile="/resources/xml/lov/definitions/country.xml" definitionName="LOV_Region"/>
  <p:messages />
</h:form>

我正在关注 BalusC 的文章来创建这个组件。这是链接

如何在自定义复合组件中使用对话框,该对话框将使用值更新并立即显示?

4

1 回答 1

4

罪魁祸首在这里:

    <p:commandButton ...
        onclick="searchAndSelect.show();" ...
        update="@form" />

该对话框永远不会出现,因为您@form在打开它后正在更新它。更新表单会使表单恢复到其默认状态,即关闭对话框。它与另一个按钮“工作”是因为另一个按钮不更新表单。它“工作”appendToBody="true"是因为它不再位于同一个表单中(因此反过来需要它自己的表单才能成功处理提交)。

您有 2 个选项:

  • 更新表单打开它。

    <p:commandButton ... update="@form" oncomplete="searchAndSelect.show();" />
    
  • 不要更新整个表单,只更新真正需要更新的部分,例如对话框的正文。

    <p:commandButton ... update="dialogContent" oncomplete="searchAndSelect.show();" />
    ...
    <p:dialog ...>
        <h:panelGrid id="dialogContent">
    

    (请注意,您可以onclick在此处继续使用,但我保留在oncomplete那里,否则用户可能会看到对话框的内容在打开后直接实时刷新,这令人不安并且可能令人困惑)

顺便说一下,这个复合组件还有另一个问题,但只有在同一视图中使用复合组件的多个实例时它才会暴露。将 修复widgetVar为在整个视图中唯一,否则多个复合组件将相互覆盖,widgetVar直到最后一个。

您可以使用复合材料自己的 ID 来确保唯一性:

<p:commandButton ... oncomplete="searchAndSelect_#{cc.id}.show();" />
...
<p:dialog ... widgetVar="searchAndSelect_#{cc.id}">
于 2013-06-11T12:21:26.487 回答