JSF 2 を使用してカスタム複合コンポーネントを構築しようとしています。このコンポーネント内に、フォームを送信するときに表示したいダイアログがありますが、appendToBody="false" を使用するとダイアログが表示されず、私は appendToBody="true" を使用します。
私の検索コンポーネントは次のようになります。
ユーザーが inputText に何らかの値を入力して検索ボタンを押すと、次のようなダイアログが表示されます。
これを機能させる唯一の方法は、コンポーネントに 1 つではなく 2 つのボタンを配置することです。1 つは値を送信するため、もう 1 つはダイアログを表示するためです。これは、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 の記事に従って、このコンポーネントを作成しました。リンクは次のとおりです。
値で更新され、一度に表示されるカスタム複合コンポーネントでダイアログを使用するにはどうすればよいですか?