JSF2 फेसलेट्स में JSTL ... समझ में आता है?


163

मैं सशर्त रूप से फेसलेट्स कोड का एक सा आउटपुट करना चाहूंगा।

उस उद्देश्य के लिए, JSTL टैग ठीक काम करने लगते हैं:

<c:if test="${lpc.verbose}">
    ...
</c:if>

हालांकि, मुझे यकीन नहीं है कि क्या यह सबसे अच्छा अभ्यास है? क्या मेरा लक्ष्य हासिल करने का कोई और तरीका है?

जवाबों:


320

परिचय

JSTL <c:xxx>टैग सभी टैगहैंडलर हैं और इन्हें व्यू बिल्ड टाइम के दौरान निष्पादित किया जाता है , जबकि JSF <h:xxx>टैग सभी UI घटक होते हैं और इन्हें व्यू रेंडर समय के दौरान निष्पादित किया जाता है

ध्यान दें कि JSF के स्वयं से <f:xxx>और <ui:xxx>उन जो करते टैग सिर्फ़ नहीं से विस्तार UIComponent, भी taghandlers हैं जैसे <f:validator>, <ui:include>, <ui:define>, आदि लोगों से दूर का विस्तार UIComponentभी JSF UI घटक, जैसे हैं <f:param>, <ui:fragment>, <ui:repeat>, आदि JSF UI घटक से केवल idऔर bindingगुण हैं निर्माण समय देखने के दौरान भी मूल्यांकन किया गया। इस प्रकार जेएसटीएल जीवनचक्र के नीचे दिए गए उत्तर भी जेएसएफ घटकों की विशेषताओं idऔर bindingविशेषताओं पर लागू होते हैं।

व्यू बिल्ड टाइम वह पल होता है जब XHTML / JSP फाइल को जेएसएफ कंपोनेंट ट्री में पार्स और कन्वर्ट करना होता है जिसे बाद में स्टोर किया जाता UIViewRootहै FacesContext। दृश्य प्रस्तुत करने का समय वह क्षण होता है जब JSF घटक ट्री HTML उत्पन्न करने वाला होता है, जिसके साथ शुरू होता हैUIViewRoot#encodeAll() । तो: JSF UI घटक और JSTL टैग सिंक में नहीं चलते हैं जैसा कि आप कोडिंग से उम्मीद करेंगे। आप इसे निम्नानुसार कल्पना कर सकते हैं: जेएसटीएल ऊपर से नीचे तक चलता है, जेएसएफ घटक पेड़ का उत्पादन करता है, फिर यह जेएसएफ की बारी है ऊपर से नीचे तक फिर से चलाने के लिए, HTML आउटपुट का उत्पादन।

<c:forEach> बनाम <ui:repeat>

उदाहरण के लिए, इस फेसलेट्स का उपयोग करके 3 से अधिक आइटम्स को चिह्नित करना है <c:forEach>:

<c:forEach items="#{bean.items}" var="item">
    <h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>

... <h:outputText>जेएसएफ घटक के पेड़ में तीन अलग-अलग घटकों को देखने के समय के दौरान बनाता है , मोटे तौर पर इस तरह का प्रतिनिधित्व किया:

<h:outputText id="item_1" value="#{bean.items[0].value}" />
<h:outputText id="item_2" value="#{bean.items[1].value}" />
<h:outputText id="item_3" value="#{bean.items[2].value}" />

... जो बदले में समय प्रस्तुत करने के दौरान व्यक्तिगत रूप से अपने HTML आउटपुट उत्पन्न करते हैं:

<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>

ध्यान दें कि आपको घटक आईडी की विशिष्टता को मैन्युअल रूप से सुनिश्चित करने की आवश्यकता है और यह भी कि व्यू बिल्ड समय के दौरान उनका मूल्यांकन किया जाता है।

जबकि इस फेसलेट्स का उपयोग करते हुए 3 से अधिक आइटम्स को चिह्नित करना है <ui:repeat>, जो कि JSF UI घटक है:

<ui:repeat id="items" value="#{bean.items}" var="item">
    <h:outputText id="item" value="#{item.value}" />
</ui:repeat>

... पहले से ही जेएसएफ घटक के पेड़ के रूप में समाप्त होता है, जिसके कारण बहुत ही <h:outputText>घटक दृश्य रेंडर समय वर्तमान पुनरावृत्ति दौर के आधार पर HTML उत्पादन उत्पन्न करने के लिए पुन: उपयोग किया जा रहा है :

<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>

ध्यान दें कि <ui:repeat>एक NamingContainerघटक होने के नाते पहले से ही पुनरावृत्ति सूचकांक के आधार पर क्लाइंट आईडी की विशिष्टता सुनिश्चित की गई थी; idइस तरह से बाल घटकों की विशेषता में ईएल का उपयोग करना भी संभव नहीं है क्योंकि यह दृश्य निर्माण समय के दौरान भी मूल्यांकन किया #{item}जाता है जबकि केवल दृश्य रेंडर समय के दौरान उपलब्ध है। समान h:dataTableऔर समान घटकों के लिए समान है।

<c:if>/ <c:choose>बनामrendered

एक अन्य उदाहरण के रूप में, यह फ़ेसलेट सशर्त रूप से अलग-अलग टैग का उपयोग करके जोड़ रहा है <c:if>(आप इसके लिए भी उपयोग कर सकते हैं <c:choose><c:when><c:otherwise>):

<c:if test="#{field.type eq 'TEXT'}">
    <h:inputText ... />
</c:if>
<c:if test="#{field.type eq 'PASSWORD'}">
    <h:inputSecret ... />
</c:if>
<c:if test="#{field.type eq 'SELECTONE'}">
    <h:selectOneMenu ... />
</c:if>

... type = TEXTकेवल <h:inputText>JSF घटक ट्री में घटक जोड़ने के मामले में :

<h:inputText ... />

जबकि यह फेसलेट्स मार्कअप:

<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />

... शर्त की परवाह किए बिना JSF घटक के पेड़ में ऊपर के रूप में बिल्कुल खत्म हो जाएगा। यह इस प्रकार एक "फूला हुआ" घटक वृक्ष में समाप्त हो सकता है जब आपके पास उनमें से कई होते हैं और वे वास्तव में एक "स्थिर" मॉडल पर आधारित होते हैं (यानी fieldकम से कम दृश्य गुंजाइश के दौरान कभी नहीं बदलता है)। जब आप 2.2.7 से पहले मोजरा संस्करणों में अतिरिक्त गुणों के साथ सौदा करते हैं, तो आप ईएल मुसीबत में भाग सकते हैं ।

<c:set> बनाम <ui:param>

वे विनिमेय नहीं हैं। <c:set>सेट ईएल दायरे में एक चर, जो केवल पहुँचा जा सकता है के बाद दृश्य का निर्माण समय के दौरान टैग, लेकिन दृश्य में कहीं भी देखने के दौरान समय प्रस्तुत करना। <ui:param>गुजरता एक Facelet टेम्पलेट के लिए एक ईएल चर के माध्यम से शामिल <ui:include>, <ui:decorate template>, या <ui:composition template>। जेएसएफ के पुराने संस्करणों में बग थे, जिससे <ui:param>फेसलेट टेम्पलेट के बाहर चर भी उपलब्ध है, इस पर कभी भरोसा नहीं करना चाहिए।

<c:set>एक के बिना scopeविशेषता एक उपनाम की तरह व्यवहार करेगा। यह किसी भी दायरे में ईएल अभिव्यक्ति के परिणाम को कैश नहीं करता है। इस प्रकार जेएसएफ घटकों को पुनरावृत्त करने वाले उदाहरण के लिए इसका पूरी तरह से उपयोग किया जा सकता है। इस प्रकार, जैसे नीचे ठीक काम करेगा:

<ui:repeat value="#{bean.products}" var="product">
    <c:set var="price" value="#{product.price}" />
    <h:outputText value="#{price}" />
</ui:repeat>

यह केवल एक लूप में राशि की गणना के लिए उपयुक्त नहीं है। इसके बजाय EL 3.0 स्ट्रीम का उपयोग करें :

<ui:repeat value="#{bean.products}" var="product">
    ...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>

केवल, जब आप सेट scopeस्वीकार्य मानों में से एक के साथ विशेषता request, view, session, या application, तो यह तुरंत दृश्य का निर्माण समय के दौरान मूल्यांकन किया है और निर्दिष्ट दायरे में संग्रहीत किया जाएगा।

<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />

यह केवल एक बार मूल्यांकन किया जाएगा और #{dev}पूरे आवेदन के दौरान उपलब्ध होगा।

जेएसएफ घटक वृक्ष निर्माण को नियंत्रित करने के लिए JSTL का उपयोग करें

JSTL का उपयोग केवल अप्रत्याशित परिणाम को जन्म दे सकती है जब इस तरह के रूप JSF बार-बार दोहराना घटकों के अंदर इस्तेमाल किया जा रहा <h:dataTable>, <ui:repeat>, आदि, या JSTL टैग विशेषताओं जैसे JSF घटनाओं के परिणामों पर निर्भर जब preRenderViewया प्रस्तुत जो दृश्य का निर्माण समय के दौरान उपलब्ध नहीं हैं मॉडल में प्रपत्र मूल्यों । इसलिए, JSTL टैग का उपयोग केवल JSF घटक ट्री बिल्डिंग के प्रवाह को नियंत्रित करने के लिए करें। HTML उत्पादन पीढ़ी के प्रवाह को नियंत्रित करने के लिए JSF UI घटकों का उपयोग करें। varJFL टैग विशेषताओं के लिए JSF घटकों की पुनरावृति के लिए बाध्य न करें । JSTL टैग विशेषताओं में JSF घटनाओं पर भरोसा न करें।

कभी भी आपको लगता है कि आपको बैकिंग बीन के लिए एक घटक को बांधने की ज़रूरत है binding, या एक के माध्यम से पकड़ो findComponent(), और अपने बच्चों के साथ एक बैकिंग बीन में जावा कोड का उपयोग करके अपने बच्चों को बनाने या हेरफेर new SomeComponent()करने की ज़रूरत है, तो आपको तुरंत रोकना चाहिए और इसके बजाय जेएसटी का उपयोग करने पर विचार करना चाहिए। जैसा कि JSTL भी XML आधारित है, JSF घटकों को गतिशील रूप से बनाने के लिए आवश्यक कोड इतना बेहतर पठनीय और बनाए रखने योग्य हो जाएगा।

यह जानना महत्वपूर्ण है कि 2.1.18 से अधिक पुराने मोजरा संस्करणों को JSTL टैग विशेषता में देखने वाले बीन बीन का संदर्भ देते समय आंशिक रूप से सहेजने में एक बग था। पूरे दृश्य को काटे हुए बीन को व्यू ट्री से पुनर्प्राप्त करने के बजाय नया बनाया जाएगा (केवल इसलिए कि पूरा दृश्य ट्री अभी तक JSTL रन पर उपलब्ध नहीं है)। यदि आप किसी JSTL टैग विशेषता द्वारा सेम बीन वाले दृश्य में कुछ राज्य की अपेक्षा या भंडारण कर रहे हैं, तो यह आपके द्वारा अपेक्षित मूल्य को वापस नहीं करेगा, या यह वास्तविक दृश्य में खोई हुई बीन में "खो" जाएगा जो दृश्य के बाद पुनर्स्थापित हो जाता है पेड़ बनाया गया है। यदि आप Mojarra 2.1.18 या नए में अपग्रेड नहीं कर सकते हैं, तो web.xmlनीचे दिए गए आंशिक राज्य बचत को बंद करना है :

<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
</context-param>

यह सभी देखें:

कुछ वास्तविक दुनिया उदाहरणों को देखने के लिए जहां JSTL टैग सहायक होते हैं (यानी जब दृश्य के निर्माण के दौरान इसका सही उपयोग किया जाता है), तो निम्न प्रश्न देखें:


संक्षेप में

अपनी ठोस कार्यात्मक आवश्यकता के अनुसार, यदि आप JSF घटकों को सशर्त रूप से प्रस्तुत करना चाहते हैं, तो renderedइसके बजाय JSF HTML घटक पर विशेषता का उपयोग करें , विशेष रूप से यदि #{lpc}वर्तमान में JSF पुनरावृत्त घटक जैसे <h:dataTable>या के रूप में प्रदर्शित आइटम का प्रतिनिधित्व करता है <ui:repeat>

<h:someComponent rendered="#{lpc.verbose}">
    ...
</h:someComponent>

या, यदि आप सशर्त रूप से JSF घटकों को बनाना (बनाना / जोड़ना) चाहते हैं, तो JSTL का उपयोग करते रहें। यह new SomeComponent()जावा में क्रिया करने की तुलना में बहुत बेहतर है ।

<c:if test="#{lpc.verbose}">
    <h:someComponent>
        ...
    </h:someComponent>
</c:if>

यह सभी देखें:


3
@ अकलिन: नहीं? इस उदाहरण के बारे में कैसे ?
बालुस

1
मैं पहले पैराग्राफ को लंबे समय तक ठीक से व्याख्या नहीं कर सकता (दिए गए उदाहरण हालांकि बहुत स्पष्ट हैं)। इसलिए, मैं इस टिप्पणी को एकमात्र तरीका के रूप में छोड़ रहा हूं। उस पैराग्राफ द्वारा, मैं इस धारणा में हूं कि <ui:repeat>एक टैग हैंडलर है (इस लाइन के कारण, " ध्यान दें कि JSF का अपना <f:xxx>और <ui:xxx>... ") केवल पसंद है <c:forEach>और इसलिए, इसका मूल्यांकन बिल्ड टाइम (फिर से बस की तरह <c:forEach>) पर किया जाता है । यदि ऐसा है, तो, के बीच कोई दृश्यमान, कार्यात्मक अंतर नहीं होना चाहिए <ui:repeat>और <c:forEach>? मुझे नहीं मिलता है कि वास्तव में उस अनुच्छेद का क्या मतलब है :)
छोटे

1
क्षमा करें, मैं आगे इस पोस्ट को प्रदूषित नहीं करूंगा। मैंने आपकी पिछली टिप्पणी को ध्यान में रखा, लेकिन यह वाक्य नहीं है, " ध्यान दें कि JSF के अपने <f:xxx>और <ui:xxx>टैग जो विस्तारित नहीं UIComponentहोते हैं; टैग हैंडलर हैं। " इसका अर्थ यह <ui:repeat>है कि टैग हैंडलर <ui:xxx>भी शामिल है क्योंकि इसमें भी शामिल हैं <ui:repeat>? यह तो मतलब यह होना चाहिए कि <ui:repeat>घटकों में से एक है <ui:xxx>कि फैली हुई है UIComponent। इसलिए, यह एक टैग हैंडलर नहीं है। (उनमें से कुछ का विस्तार नहीं हो सकता है UIComponent। इसलिए, वे टैग हैंडलर हैं) क्या यह है?
टिनी

2
@Sirgill: लक्ष्य मान में मूल्यांकन किए गए मान को सेट करने के बजाय EL अभिव्यक्ति का उपनाम <c:set>नहीं scopeबनाता है। scope="request"इसके बजाय कोशिश करें , जो तुरंत मूल्य का मूल्यांकन करेंगे (वास्तव में निर्माण समय देखें) और इसे अनुरोध विशेषता के रूप में सेट करें (जो पुनरावृत्ति के दौरान "ओवरराइट" नहीं होगा)। कवर के तहत, यह एक ValueExpressionऑब्जेक्ट बनाता है और सेट करता है।
बालुसक

1
@ के। निकोलस: यह कवर के तहत है a ClassNotFoundException। आपकी परियोजना की रनटाइम निर्भरताएं टूट गई हैं। सबसे अधिक संभावना है कि आप गैर-जावाईई सर्वर का उपयोग कर रहे हैं जैसे कि टॉमकैट और आप जेएसटीएल को स्थापित करना भूल गए, या आपने गलती से जेएसटीएल 1.0 और जेएसटीएल 1.1+ दोनों को शामिल किया है। क्योंकि JSTL 1.0 में पैकेज है javax.servlet.jstl.core.*और JSTL 1.1 के बाद से यह बन गया है javax.servlet.jsp.jstl.core.*। JSTL को स्थापित करने के लिए सुराग यहां देखे जा सकते हैं: stackoverflow.com/a/4928309
BalusC

13

उपयोग

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>

Thx, बढ़िया जवाब। सामान्य तौर पर: क्या JSTL टैग अभी भी समझ में आता है या हमें JSF 2.0 के बाद से उन्हें पदावनत माना जाना चाहिए?
Jan

ज्यादातर मामलों में, हाँ। लेकिन कभी-कभी उन्हें उपयोग करने के लिए उपयुक्त होता है
बोझो

3
H का उपयोग करना: पैनलग्रुप एक गंदा समाधान है, क्योंकि यह एक <span> टैग बनाता है, जबकि c: अगर html कोड में कुछ नहीं जोड़ा जाता है। h: पैनलग्रुप भी पैनलग्रिड्स के अंदर समस्याग्रस्त है, क्योंकि यह तत्वों को समूहित करता है।
Rober2D2

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.