2010-08-18 5 views
19

this과 같은 비슷한 질문을 발견했습니다. 그러나이 방법이 너무 많아서 혼란 스럽습니다.동적 JSF 양식 필드를 만드는 방법

우리가 읽고있는 파일은 XML입니다. 이 XML에는 제공해야하는 양식 필드에 대한 정보가 들어 있습니다.

public class DynamicField { 
    private String label; // label of the field 
    private String fieldKey; // some key to identify the field 
    private String fieldValue; // the value of field 
    private String type; // can be input,radio,selectbox etc 

    // Getters + setters. 
} 

그래서 우리는 List<DynamicField> 있습니다

그래서 우리가 필요로하는 모든 정보를 가지고이 사용자 정의 DynamicField.java를 만들었습니다.

나는이 목록을 통해 반복하고 다음과 같이 보입니다 있도록 양식 필드를 채우려 :

<h:dataTable value="#{dynamicFields}" var="field"> 
    <my:someCustomComponent value="#{field}" /> 
</h:dataTable> 

<my:someCustomComponent> 다음 적절한 JSF 폼 구성 요소 (예 : 라벨, InputText]를)

을 반환 또 다른 방법은 <my:someCustomComponent>을 표시하는 것입니다. 그런 다음 양식 요소를 사용하여 HtmlDataTable을 반환합니다. (나는 이것이 아마도 더 쉬울 것이라고 생각한다).

어떤 접근 방식이 가장 좋습니까? 누군가가 나를 어떻게 만들 수 있는지 보여주는 링크 나 코드를 보여줄 수 있습니까? 나는 완벽한 코드 예제를 선호하며 "javax.faces.component.UIComponent의 하위 클래스가 필요합니다"와 같은 대답은 아닙니다.

답변

51

제출 된 값은 Mapfield1, field2, field3하여 사용할 수 있도록

<h:inputText value="#{bean.map.field1}" /> 
<h:inputText value="#{bean.map.field2}" /> 
<h:inputText value="#{bean.map.field3}" /> 
... 

같은 입력 필드를 이름을 경우, 다른 답변은 완전히 다른 맛으로 편집 할 자격이 없습니다. (다른 사람들이 나중에 참조 할 때 유용 할 수 있습니다.) Javabean 기반의 다른 답변을 추가하겠습니다. 기원은 자바 빈즈 때


나는 기본적으로 세 가지 옵션을 참조하십시오. JSF rendered 속성 또는 JSTL <c:choose>/<c:if> 태그

  1. 만들기 사용은 조건부 렌더링 또는 원하는 구성 요소를 빌드합니다.

    는 JSTL 접근 방식의 예는 How to make a grid of JSF composite component?에서 찾을 수 있습니다
    <ui:repeat value="#{bean.fields}" var="field"> 
        <div class="field"> 
         <h:inputText value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXT'}" /> 
         <h:inputSecret value="#{bean.values[field.name]}" rendered="#{field.type == 'SECRET'}" /> 
         <h:inputTextarea value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXTAREA'}" /> 
         <h:selectOneRadio value="#{bean.values[field.name]}" rendered="#{field.type == 'RADIO'}"> 
          <f:selectItems value="#{field.options}" /> 
         </h:selectOneRadio> 
         <h:selectOneMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTONE'}"> 
          <f:selectItems value="#{field.options}" /> 
         </h:selectOneMenu> 
         <h:selectManyMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTMANY'}"> 
          <f:selectItems value="#{field.options}" /> 
         </h:selectManyMenu> 
         <h:selectBooleanCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKONE'}" /> 
         <h:selectManyCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKMANY'}"> 
          <f:selectItems value="#{field.options}" /> 
         </h:selectManyCheckbox> 
        </div> 
    </ui:repeat> 
    

    아니, JSTL은 절대적으로 "나쁜 관행"되지 않습니다 : 아래 rendered 속성을 사용하는 예입니다. 이 신화는 JSF 1.x 시대의 남은 부분이며 JSTL의 수명주기와 권한을 처음부터 이해하지 못했기 때문에 너무 오랜 시간 지속됩니다. 위에서 언급 한 스 니펫과 같은 #{bean.fields} 뒤에있는 모델이 적어도 JSF 뷰 범위에서 변경되지 않는 경우에만 JSTL을 사용할 수 있습니다. 참고 : JSTL in JSF2 Facelets... makes sense? 빈 속성에 binding을 사용하는 것은 여전히 ​​"나쁜 습관"입니다.

    <ui:repeat><div>에 관해서는, 정말 당신이 사용하는 구성 요소를 반복, 당신은 심지어 <p:dataGrid> 또는 <p:dataList>로, 최초의 질문, 또는 구성 요소 라이브러리 특정 반복하는 구성 요소에로 <h:dataTable>을 사용할 수있는 중요하지 않습니다. Refactor if necessary the big chunk of code to an include or tagfile.

    제출 된 값을 수집하는 것과 마찬가지로 #{bean.values}은 이미 사전 처리 된 Map<String, Object>을 가리켜 야합니다. HashMap이면 충분합니다. 여러 값을 설정할 수있는 컨트롤의 경우 맵을 미리 채울 수 있습니다. 그런 다음 값으로 List<Object>을 채워야합니다. Field#getType()enum 일 것이므로 Java 코드 측에서 처리가 쉬워 지므로주의해야합니다. 그런 다음 불쾌한 if/else 블록 대신 switch 문을 사용할 수 있습니다. !

    public void populateForm(ComponentSystemEvent event) { 
        HtmlForm form = (HtmlForm) event.getComponent(); 
        for (Field field : fields) { 
         switch (field.getType()) { // It's easiest if it's an enum. 
          case TEXT: 
           UIInput input = new HtmlInputText(); 
           input.setId(field.getName()); // Must be unique! 
           input.setValueExpression("value", createValueExpression("#{bean.values['" + field.getName() + "']}", String.class)); 
           form.getChildren().add(input); 
           break; 
          case SECRET: 
           UIInput input = new HtmlInputSecret(); 
           // etc... 
         } 
        } 
    } 
    

    (참고 : 다음 HtmlForm를 직접 작성하지 않는 JSF 생성을 사용

    <h:form id="form"> 
        <f:event type="postAddToView" listener="#{bean.populateForm}" /> 
    </h:form> 
    

    으로 :


  2. postAddToView 이벤트 리스너에 프로그램 구성 요소 만들기 하나,이 사람은 결코 null)

    이렇게하면 정확한 시점에 트리가 채워지고 게터에 비즈니스 로직이 적용되지 않으며 #{bean}이 요청 범위보다 넓은 범위에있을 때 "중복 된 구성 요소 ID"문제가 발생하지 않습니다. 따라서 안전하게 사용할 수 있습니다. 예view scoped bean)을 생성하고, bean이 UIComponent 속성을 사용하지 않도록 유지합니다. 그러면 구성 요소가 serializable bean의 속성으로 유지 될 때 잠재적 직렬화 문제와 메모리 누수가 방지됩니다. 당신이 <f:event> 사용할 수없는 JSF 1.x에서 여전히 경우

    대신 binding

    <h:form id="form" binding="#{bean.form}" /> 
    

    를 통해 콩을 범위 (!하지 세션) 요청에 양식 구성 요소를 결합 그리고 느리게 그것을 채울 형태의 게터는 :

    public HtmlForm getForm() { 
        if (form == null) { 
         form = new HtmlForm(); 
         // ... (continue with code as above) 
        } 
        return form; 
    } 
    

    binding를 사용하여, 그 UI 구성 요소는 기본적으로 범위 요청할하고 절대적으로 넓은 범위에서 빈의 속성으로 할당 할 수 없습니다해야 이해하는 것이 매우 중요합니다. 사용자 정의 렌더러와 사용자 정의 구성 요소를 만들기도 How does the 'binding' attribute work in JSF? When and how should it be used?


  3. 참조하십시오. 필자는 완전한 예제를 게시하지 않을 것입니다. 왜냐하면이 코드는 결국 매우 밀접하게 결합되고 응용 프로그램에 특정한 혼란이 될 것이기 때문입니다.


장점과 각 옵션의 단점은 명확해야한다. 가장 쉽고 유지하기 쉬우 며 유지 보수가 가장 어렵고 유지하기가 쉽지 않으며 최소한 재사용이 가능한 재사용이 가능한 최상의 재사용이 가능합니다. 기능 요구 사항 및 현재 상황에 가장 적합한 것이 무엇이든 선택하는 것은 사용자의 몫입니다.

는 XHTML + XML을 자바에서 가능한 (방법 # 2)와 불가능을 절대적으로 아무것도이 없다는 것을해야 명물 (방법 # 1). XHTML + XML은 자바만큼이나 모든 것이 가능합니다. 많은 초보자들은 컴포넌트를 동적으로 생성 할 때 XHTML + XML (특히 <ui:repeat> 및 JSTL)을 과소 평가하고, Java가 "유일한 방법"이라고 잘못 생각합니다. 일반적으로 이는 혼란스럽고 혼란스러운 코드로 끝납니다.

+1

네 번째 대안이 있습니다 : PrimeFaces 확장 구성 요소 : DynaForm (http://www.primefaces.org/showcase-ext/views/home.jsf). 여기에는 몇 가지 한계가 있지만 대부분의 사용자에게는 충분할 것입니다. –

+0

안녕 BalusC, 전 당신의 큰 팬이에요. 나는 당신의 답변을 통해 배우고 있으며 나는 지금 당면하고있는 문제에 대해 토론을하기 위해 당신의 메일 ID를 필요로합니다. 귀하의 ID를 [email protected]으로 보내주십시오. –

15

원산지가 XML 인 경우 완전히 다른 접근 방식 인 XSL을 제안합니다. Facelets는 XHTML을 기반으로합니다. XSL을 사용하여 XML에서 XHTML로 쉽게 이동할 수 있습니다. JSF가 작업을 수행하기 전에 약간의 괜찮은 내용으로 Filter을 실행할 수 있습니다.

다음은 킥오프 예제입니다.

persons.xsl

FacesServlet<servlet-name>에 매핑하고 FacesServlet 자체 *.jsf<url-pattern>에 매핑됨을 가정한다
<?xml version="1.0" encoding="UTF-8"?> 

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:h="http://java.sun.com/jsf/html"> 

    <xsl:output method="xml" 
     doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" 
     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/> 

    <xsl:template match="persons"> 
     <html> 
     <f:view> 
      <head><title>Persons</title></head> 
      <body> 
       <h:panelGrid columns="2"> 
        <xsl:for-each select="person"> 
         <xsl:variable name="name"><xsl:value-of select="name" /></xsl:variable> 
         <xsl:variable name="age"><xsl:value-of select="age" /></xsl:variable> 
         <h:outputText value="{$name}" /> 
         <h:outputText value="{$age}" /> 
        </xsl:for-each> 
       </h:panelGrid> 
      </body> 
     </f:view> 
     </html> 
    </xsl:template> 
</xsl:stylesheet> 

JsfXmlFilter

<?xml version="1.0" encoding="UTF-8"?> 
<persons> 
    <person> 
     <name>one</name> 
     <age>1</age> 
    </person> 
    <person> 
     <name>two</name> 
     <age>2</age> 
    </person> 
    <person> 
     <name>three</name> 
     <age>3</age> 
    </person> 
</persons> 

persons.xml

. JSF는 그것이 기대하는 곳

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException 
{ 
    HttpServletRequest r = (HttpServletRequest) request; 
    String rootPath = r.getSession().getServletContext().getRealPath("/"); 
    String uri = r.getRequestURI(); 
    String xhtmlFileName = uri.substring(uri.lastIndexOf("/")).replaceAll("jsf$", "xhtml"); // Change this if FacesServlet is not mapped on `*.jsf`. 
    File xhtmlFile = new File(rootPath, xhtmlFileName); 

    if (!xhtmlFile.exists()) { // Do your caching job. 
     String xmlFileName = xhtmlFileName.replaceAll("xhtml$", "xml"); 
     String xslFileName = xhtmlFileName.replaceAll("xhtml$", "xsl"); 
     File xmlFile = new File(rootPath, xmlFileName); 
     File xslFile = new File(rootPath, xslFileName); 
     Source xmlSource = new StreamSource(xmlFile); 
     Source xslSource = new StreamSource(xslFile); 
     Result xhtmlResult = new StreamResult(xhtmlFile); 

     try { 
      Transformer transformer = TransformerFactory.newInstance().newTransformer(xslSource); 
      transformer.transform(xmlSource, xhtmlResult); 
     } catch (TransformerException e) { 
      throw new RuntimeException("Transforming failed.", e); 
     } 
    } 

    chain.doFilter(request, response); 
} 

http://example.com/context/persons.jsf에 의해 실행하고 마지막 킥과 persons.xsl를 사용하여 persons.xhtmlpersons.xml 변환 것이 필터가 persons.xhtml을 넣어.

사실, XSL은 약간의 학습 곡선이 있지만, 소스가 XML이고 대상이 XML 기반이므로 작업에 적합한 도구입니다.

양식과 관리 빈 간의 매핑을 수행하려면 Map<String, Object>을 사용하십시오.기원은 자바 빈즈 실제로하지 XML이지만, 때문에 당신이 등

+0

안녕하세요 @BalusC. 광범위한 답변을 주셔서 감사합니다. 그러나 현재 모델을 통해이 혜택을 누릴 수 있는지 확실하지 않습니다. 예, 우리는 XML을 통해 데이터를 가져오고 있지만 Smooks는 이미 JavaBean (xml2Java)로 전송되었습니다. 여기서 내가 제안한 것을 할 수 있는지 확신 할 수 없다. –

+0

'persons.xml'과'persons.xsl'을이 경로에 저장하는 것이 필수적인가? -'.getRealPath ("/")'? 이 파일들을'.getRealPath ("/ public_resources/xsl_xml")'(''과 함께)로 옮기려고하면'내용이 허락되지 않는다 in Prolog' - 생성 된 XHTML 파일은 더 이상 형식이 올바르지 않습니다. – Tiny

+1

@Tiny''는 XML 파일이있는 경로가 아닌 XML 구조를 나타내야합니다. 변경하지 마십시오. – BalusC