JSF 2 自定义搜索复合组件,内部带有对话框

标签 jsf jsf-2 custom-component

我正在尝试使用 JSF 2 构建自定义复合组件。 我想在提交表单时显示此组件内的一个对话框,但是当我使用appendToBody =“false”时,该对话框永远不会显示,而当我使用appendToBody =“true”时,该对话框不会工作。

我的搜索组件如下所示。

enter image description here

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

enter image description here

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

enter image description here

当我使用带有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 的文章来创建这个组件的。 这是link :

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

最佳答案

罪魁祸首在这里:

    <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,否则用户可能会直接看到对话框的内容被实时刷新打开它后,这令人不安并且可能令人困惑)

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

可以使用composite自己的ID来保证唯一性:

<p:commandButton ... oncomplete="searchAndSelect_#{cc.id}.show();" />
...
<p:dialog ... widgetVar="searchAndSelect_#{cc.id}">

关于JSF 2 自定义搜索复合组件,内部带有对话框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17041101/

相关文章:

NetBeans 8.2 中的 JSF 2.3 支持(f :importConstants, f :websocket, 等)

html - DataTableemptyMessage 中的 Primefaces 10 HTML 样式

jsf - @this的功能到底是什么?

c# - MarkupString属性能否将自定义组件的字符串转换为DOM中的HTML元素

redirect - JSF/Mojarra ExternalContext.redirect() 创建一个 HttpSession

jsf - 如何保持 FacesMessage 显示

java - JSF 中每个用户/ session 的唯一标识符

java - 将一些单词作为 block 添加到 JTextarea

javascript - React-Leaflet:将 map 控制组件放置在 map 之外?

jsf - javax.el.E​​LException : Error reading 'foo' on type com. 示例.Bean