2010-08-18 14 views
19

मुझे कुछ समान प्रश्न हैं जैसे this एक, हालांकि ऐसा कई तरीकों से किया जा सकता है जिससे मुझे और अधिक भ्रमित कर दिया गया।डायनामिक जेएसएफ फॉर्म फ़ील्ड कैसे बनाएं

हमें 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

नाम हैं के बाद से मूल वास्तव में नहीं एक्सएमएल एक JavaBean है, लेकिन, और दूसरा उत्तर पूरी तरह से अलग स्वाद में संपादित करने के लायक नहीं है (यह अभी भी दूसरों द्वारा भावी संदर्भों के लिए उपयोगी हो सकता है), मैं एक जावाव्यू-मूल के आधार पर एक और उत्तर जोड़ूंगा।


मैं मूल रूप से तीन विकल्प दिखाई जब मूल एक JavaBean है। JSF rendered विशेषता या यहाँ तक कि JSTL <c:choose>/<c:if> टैग की

  1. उपयोग करें सशर्त प्रस्तुत करना या वांछित घटक (रों) बनाने के लिए।

    <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 दृष्टिकोण का एक उदाहरण How to make a grid of JSF composite component? में पाया जा सकता नहीं, JSTL बिल्कुल नहीं एक "बुरा व्यवहार" है: नीचे rendered विशेषता का उपयोग कर एक उदाहरण है। यह मिथक जेएसएफ 1.x युग से बचे हुए है और बहुत लंबा है क्योंकि शुरुआत करने वालों ने जेएसटीएल की जीवन चक्र और शक्तियों को स्पष्ट रूप से नहीं समझा। बिंदु पर, आप केवल जेएसटीएल का उपयोग कर सकते हैं जब उपरोक्त स्निपेट में #{bean.fields} के पीछे मॉडल कम से कम जेएसएफ व्यू स्कोप के दौरान कभी नहीं बदलता है। JSTL in JSF2 Facelets... makes sense? भी देखें, इसके बजाय binding का उपयोग बीन संपत्ति में अभी भी एक "खराब अभ्यास" है।

    <ui:repeat><div> के रूप में, यह वास्तव में कोई फर्क नहीं पड़ता जो घटक आप उपयोग पुनरावृत्ति, आप भी <h:dataTable> के रूप में अपने प्रारंभिक सवाल है, या एक घटक पुस्तकालय विशिष्ट बार-बार दोहराना घटक में, इस तरह के <p:dataGrid> या <p:dataList> के रूप में उपयोग कर सकते हैं। Refactor if necessary the big chunk of code to an include or tagfile

    सबमिट किए गए मान एकत्र करने के लिए, #{bean.values} को Map<String, Object> पर इंगित करना चाहिए जो पहले से ही पूर्ववर्ती है। एक HashMap पर्याप्त है। आप नियंत्रण के मामले में मानचित्र को पूर्ववत करना चाहते हैं जो एकाधिक मान सेट कर सकता है। इसके बाद आपको मूल्य के रूप में List<Object> के साथ इसे पूर्ववत करना चाहिए। ध्यान दें कि मुझे Field#getType()enum होने की उम्मीद है क्योंकि यह जावा कोड पक्ष में प्रसंस्करण को आसान बनाता है। इसके बाद आप एक if/else ब्लॉक के बजाय switch कथन का उपयोग कर सकते हैं।


  2. घटकों एक postAddToView घटना श्रोता में प्रोग्राम के रूप में बनाएँ:

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

    :

    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 रूप से बनाए गए! एक, यह एक कभी नहीं है null)

    यह गारंटी देता है कि पेड़ बिल्कुल सही पल पर आबादी है, और व्यापारियों को व्यापार तर्क से मुक्त रखता है, और #{bean} अनुरोध स्कोप की तुलना में व्यापक दायरे में संभावित "डुप्लिकेट घटक आईडी" परेशानी से बचाता है (ताकि आप सुरक्षित रूप से उपयोग कर सकें जैसेएक दृश्य यहां बीन स्कैन किया गया है), और बीन को UIComponent गुणों से मुक्त रखता है जो बदले में संभावित क्रमबद्धता मुसीबत और स्मृति लीकिंग से बचाता है जब घटक एक धारावाहिक बीन की संपत्ति के रूप में आयोजित होता है।

    आप JSF 1.x जहां <f:event> उपलब्ध नहीं है पर अभी भी कर रहे हैं, बजाय प्रपत्र घटक एक अनुरोध के lazily बाँध (नहीं सत्र!) binding

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

    के माध्यम से सेम scoped और फिर में पॉप्युलेट फार्म के गेटर:

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

    जब binding का उपयोग कर, यह समझना महत्वपूर्ण है कि UI घटक मूल रूप से अनुरोध scoped कर रहे हैं और पूरी तरह से एक व्यापक दायरे में एक सेम की संपत्ति के रूप में नियुक्त नहीं किया जाना चाहिए बहुत महत्वपूर्ण है। भी How does the 'binding' attribute work in JSF? When and how should it be used?


  3. देखें एक कस्टम दाता के साथ एक कस्टम घटक बनाएँ। मैं पूर्ण उदाहरण पोस्ट नहीं कर रहा हूं क्योंकि यह बहुत सारे कोड है जो सभी के बाद बहुत सख्त-युग्मित और एप्लिकेशन-विशिष्ट गड़बड़ होगा।


पेशेवरों और प्रत्येक विकल्प के विपक्ष स्पष्ट किया जाना चाहिए। यह सबसे कठिन और कम से कम रखरखाव करने के लिए सबसे आसान और सर्वोत्तम रखरखाव से जाता है और बाद में कम से कम पुन: प्रयोज्य से पुन: उपयोग करने योग्य भी होता है। यह आपके लिए निर्भर है कि जो भी आपकी कार्यात्मक आवश्यकता और वर्तमान स्थिति के अनुरूप सर्वोत्तम है।

ध्यान दिया जाना चाहिए है कि वहाँ बिल्कुल कुछ भी नहीं जो xhtml + xml में है केवल जावा में संभव (वैसे # 2) और असंभव (वैसे # 1)। जावा में जितना अच्छा एक्सएचटीएमएल + एक्सएमएल में सबकुछ संभव है। बहुत से स्टार्टर्स गतिशील रूप से घटकों को बनाने में एक्सएचटीएमएल + एक्सएमएल (विशेष रूप से <ui:repeat> और जेएसटीएल) को कम से कम समझते हैं और गलत तरीके से सोचते हैं कि जावा "एक और एकमात्र" तरीका होगा, जबकि आम तौर पर केवल भंगुर और भ्रमित कोड में समाप्त होता है।

+1

एक चौथा विकल्प है: प्राइमफेस विस्तार घटक: डायनाफॉर्म (http://www.primefaces.org/showcase-ext/views/home.jsf)। इसमें कुछ सीमाएं हैं, लेकिन अधिकांश उपयोगकर्ताओं के लिए पर्याप्त होगी। –

+0

हाय बलुस, मैं आपके का बड़ा प्रशंसक हूं। मैं आपके उत्तरों के माध्यम से सीख रहा हूं और मुझे किसी समस्या पर कुछ चर्चा करने के लिए आपके मेल आईडी की आवश्यकता है जिसे मैं अब सामना कर रहा हूं। कृपया मुझे अपनी आईडी [email protected] पर मेल करें –

15

यदि मूल एक्सएमएल है, तो मैं एक पूरी तरह से अलग दृष्टिकोण के लिए जाने का सुझाव देता हूं: XSL। फेसलेट एक्सएचटीएमएल आधारित है। एक्सएमएल से एक्सएचटीएमएल में जाने के लिए आप आसानी से एक्सएसएल का उपयोग कर सकते हैं। यह थोड़ा सभ्य Filter के साथ काम करने योग्य है जो जेएसएफ काम कर रहा है इससे पहले कि इसमें शामिल है।

यहां एक किकऑफ उदाहरण है।

persons.xml

<?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.xsl

<?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 जो FacesServlet की <servlet-name> पर मैप किया और मानता है कि FacesServlet ही *.jsf के <url-pattern> पर मैप किया गया है है।

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.xhtml को persons.xml को बदलने और अंत में होगा persons.xhtml वहाँ डाल जहां JSF उम्मीद यह है।

सच है, एक्सएसएल के पास कुछ सीखने की वक्र है, लेकिन यह आईएमओ नौकरी के लिए सही उपकरण है क्योंकि स्रोत एक्सएमएल है और गंतव्य एक्सएमएल स्वागत के रूप में आधारित है।

फ़ॉर्म और प्रबंधित बीन के बीच मैपिंग करने के लिए, बस Map<String, Object> का उपयोग करें।आप की तरह तो

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

प्रस्तुत मूल्यों Map कुंजी field1, field2, field3 तक उपलब्ध होगा इनपुट फ़ील्ड, आदि

+0

हाय @ बालससी। एक व्यापक उत्तर के लिए धन्यवाद। हालांकि, मुझे यकीन नहीं है कि क्या मैं इस से हमारे वर्तमान मॉडल के साथ लाभ उठा सकता हूं। हां हम XML के माध्यम से डेटा प्राप्त कर रहे हैं, हालांकि यह पहले से ही जावाबीन (xml2Java) में स्थानांतरित Smooks के माध्यम से है। तो मुझे यकीन नहीं है कि मैं जो भी सुझाव देता हूं वह कर सकता हूं ... –

+0

क्या इस पथ में 'persons.xml' और' persons.xsl' को स्टोर करना अनिवार्य है - '.getRealPath ("/")'? जब मैं उन फ़ाइलों को '.getRealPath ("/ public_resources/xsl_xml") '(' के साथ स्थानांतरित करने का प्रयास करता हूं, तो यह शिकायत करता है,' सामग्री की अनुमति नहीं है प्रोलॉग में - जेनरेट की गई एक्सएचटीएमएल फाइल अब अच्छी तरह से स्वरूपित नहीं है। – Tiny

+1

@Tiny ' 'एक्सएमएल संरचना का प्रतिनिधित्व करना चाहिए, न कि पथ जहां एक्सएमएल फाइल है। इसे मत बदलें। – BalusC

संबंधित मुद्दे