JSF 2自定义搜索复合组件,里面有对话框

时间:2013-06-11 09:44:50

标签: 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

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

1 个答案:

答案 0 :(得分:3)

罪魁祸首在这里:

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

对话框永远不会显示,因为您在打开它后会更新@form。更新表单会使表单返回其默认状态,因此具有关闭的对话框。它与另一个按钮“工作”是因为另一个按钮不更新表单。它与appendToBody="true"“一起工作”是因为它不再处于相同的形式(因此它需要自己的形式才能成功处理提交)。

您有两个选择:

  • 在更新表单后打开

    <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}">