JSTL w Jsf2 ma sens?

Chciałbym wypisać trochę kodu Facelets warunkowo.

W tym celu znaczniki JSTL wydają się działać dobrze:

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

Jednak nie jestem pewien, czy jest to najlepsza praktyka? Czy jest inny sposób na osiągnięcie mojego celu?

Author: BalusC, 2010-07-27

3 answers

Wprowadzenie

JSTL <c:xxx> wszystkie znaczniki są taghandlerami i są wykonywane podczas czasu budowania widoku, podczas gdy znaczniki JSF <h:xxx> są składnikami interfejsu użytkownika i są wykonywane podczas czasu renderowania widoku .

Zauważ, że z własnych znaczników JSF <f:xxx> i <ui:xxx> tylko te, które Nie rozciągają się od UIComponent są również taghandlerami, np. <f:validator>, <ui:include>, <ui:define>, itd. Te, które rozciągają się od UIComponent są również JSF UI komponenty, np. <f:param>, <ui:fragment>, <ui:repeat>, itd. Z JSF UI komponenty tylko id i binding atrybuty są również oceniane w czasie budowania widoku. Tak więc poniższa odpowiedź dotycząca cyklu życia JSTL dotyczy również atrybutów id i binding komponentów JSF.

Czas budowania widoku jest momentem, w którym plik XHTML/JSP ma być parsowany i konwertowany do drzewa komponentów JSF, które jest następnie przechowywane jako {[30] } z FacesContext. Czas renderowania widoku jest momentem, w którym drzewo komponentów JSF ma zamiar wygenerować HTML, zaczynając od UIViewRoot#encodeAll(). Tak więc: komponenty interfejsu JSF i znaczniki JSTL nie są zsynchronizowane, jak można się spodziewać po kodowaniu. Możesz to wizualizować w następujący sposób: JSTL uruchamia się najpierw od góry do dołu, tworząc drzewo komponentów JSF, potem kolej JSF, aby ponownie uruchomić od góry do dołu, tworząc wyjście HTML.

<c:forEach> vs <ui:repeat>

Na przykład, ten znacznik jest liczony przez 3 elementy za pomocą <c:forEach>:
<c:forEach items="#{bean.items}" var="item">
    <h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>

...tworzy w czasie budowania widoku trzy oddziel <h:outputText> komponenty w drzewie komponentów JSF, mniej więcej tak:

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

...co z kolei indywidualnie generuje swoje wyjście HTML podczas renderowania widoku:

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

Zauważ, że musisz ręcznie zapewnić unikalność identyfikatorów komponentów i że są one również oceniane podczas budowania widoku.

W przeciwieństwie do JSF UI, JSF UI jest częścią składową JSF UI, która jest częścią składową JSF UI.]}
<ui:repeat id="items" value="#{bean.items}" var="item">
    <h:outputText id="item" value="#{item.value}" />
</ui:repeat>

...już kończy się jak-jest w Drzewo komponentów JSF, w którym ten sam <h:outputText> komponent jest w czasie renderowania widoku ponownie użyty do wygenerowania wyjścia HTML na podstawie bieżącej rundy iteracji:

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

Zauważ, że <ui:repeat> jako składnik NamingContainer już zapewnił unikalność identyfikatora klienta na podstawie indeksu iteracji; nie jest również możliwe użycie EL w atrybucie komponentów potomnych w ten sposób, ponieważ jest on również oceniany podczas budowania widoku, podczas gdy {[42] } jest dostępny tylko podczas renderowania widoku. To samo dotyczy h:dataTable i podobnych składników.

<c:if>/<c:choose> vs rendered

Jako kolejny przykład, Ten facets Markup warunkowo dodając różne tagi za pomocą <c:if> (możesz również użyć <c:choose><c:when><c:otherwise> do tego):

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

...w przypadku type = TEXT dodamy tylko komponent <h:inputText> do drzewa komponentów JSF:

<h:inputText ... />

Podczas gdy ten znacznik:

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

...zakończy się dokładnie tak jak wyżej w drzewie komponentów JSF niezależnie od warunku. To może w ten sposób kończy się w "nadętym" drzewie komponentów, gdy masz ich wiele i są one w rzeczywistości oparte na "statycznym" modelu (tzn. field nie zmienia się przynajmniej podczas zakresu widoku). Ponadto, możesz napotkać problemy z EL , gdy masz do czynienia z podklasami z dodatkowymi właściwościami w wersji Mojarra przed 2.2.7.

<c:set> vs <ui:param>

Nie są wymienne. <c:set> ustawia zmienną w zakresie EL, która jest dostępna tylko po znaczniku lokalizacja w czasie tworzenia widoku, ale w dowolnym miejscu w widoku w czasie renderowania widoku. <ui:param> przekazuje zmienną EL do szablonu Facelet zawartego poprzez <ui:include>, <ui:decorate template>, lub <ui:composition template>. Starsze wersje JSF miały błędy, które powodowały, że zmienna <ui:param> jest również dostępna poza danym szablonem Facelet, nigdy nie należy na tym polegać.

<c:set> bez atrybutu scope będzie zachowywał się jak alias. Nie buforuje wyniku wyrażenia EL w żadnym zakresie. Może więc doskonale być stosowane wewnątrz np. iteracyjnych komponentów JSF. Tak więc, np. poniżej będzie działać dobrze:

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

Nie nadaje się tylko do np. obliczania sumy w pętli. W tym celu użyj el 3.0 stream :

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

Tylko wtedy, gdy ustawisz atrybut scope z jedną z dopuszczalnych wartości request, view, session, or application, wtedy zostanie on oceniony natychmiast w czasie budowania widoku i zapisany w określonym zakresie.

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

Zostanie To ocenione tylko raz i dostępne jako #{dev} przez całą aplikację.

Użyj JSTL do sterowania budową drzewa komponentów JSF

Używanie JSTL może prowadzić do nieoczekiwanych rezultatów tylko wtedy, gdy jest używane wewnątrz komponentów iteracyjnych JSF, takich jak <h:dataTable>, <ui:repeat>, itd, lub gdy atrybuty znacznika JSTL zależą od wyników zdarzeń JSF, takich jak preRenderView lub przesłanych wartości formularza w modelu, które nie są dostępne w czasie budowania widoku. Tak więc, używaj znaczników JSTL tylko do sterowania przepływem budowania drzewa komponentów JSF. Użyj JSF UI komponenty do sterowania przepływem generowania wyjścia HTML. Nie wiąże var iteracji komponentów JSF z atrybutami znaczników JSTL. Nie polegaj na zdarzeniach JSF w atrybutach znaczników JSTL.

Za każdym razem, gdy wydaje ci się, że musisz powiązać komponent z backing bean za pomocą binding, lub złapać go za pomocą findComponent() i stworzyć/manipulować jego dziećmi za pomocą kodu Java w backing bean za pomocą new SomeComponent(), a co nie, powinieneś natychmiast przestać i rozważyć użycie JSTL. Ponieważ JSTL jest również oparty na XML, kod potrzebne do dynamicznego tworzenia komponentów JSF staną się o wiele lepiej czytelne i łatwe do utrzymania.

Ważne jest, aby wiedzieć, że wersje Mojarra starsze niż 2.1.18 miały błąd w częściowym zapisywaniu stanu podczas odwoływania się do zakresu widoku bean w atrybucie znacznika JSTL. Cały zakres widoku bean będzie nowo odtworzony zamiast pobieranego z drzewa widoku(po prostu dlatego, że pełne drzewo widoku nie jest jeszcze dostępne w momencie uruchomienia JSTL). Jeśli oczekujesz lub przechowujesz jakiś stan w widoku scoped bean za pomocą atrybutu znacznika JSTL nie zwróci oczekiwanej wartości lub zostanie "utracona" w rzeczywistym widoku scoped bean, który zostanie przywrócony po zbudowaniu drzewa widoków. Jeśli nie możesz uaktualnić do Mojarra 2.1.18 lub nowszej, obejściem jest wyłączenie częściowego zapisywania stanu w web.xml, jak poniżej:

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

Zobacz też:

Aby zobaczyć kilka rzeczywistych przykładów, w których znaczniki JSTL są pomocne (tzn. gdy są naprawdę prawidłowo używane podczas budowania widoku), zobacz następujące pytania/odpowiedzi:]}


W skrócie

Jeśli chcesz renderować komponenty JSF warunkowo, użyj atrybutu rendered na komponencie HTML JSF, szczególnie jeśli #{lpc} reprezentuje aktualnie iteracyjną pozycję komponentu iteracyjnego JSF, takiego jak <h:dataTable> lub <ui:repeat>.

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

Lub, jeśli chcesz budować (tworzyć/dodawać) komponenty JSF warunkowo, używaj JSTL. On o wiele lepiej niż werbalnie robić new SomeComponent() w Javie.

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

Zobacz też:

 287
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 17:15:55

Użyj

<h:panelGroup rendered="#{lpc.verbose}">
  ...
</h:panelGroup>
 12
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-27 07:47:03

Przepraszam za osobną odpowiedź, ale nie mogłem skomentować odpowiedzi powyżej.

Dla wyjścia podobnego do switcha można użyć switcha z primefaces-extensions.

 4
Author: Ravshan Samandarov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-25 09:58:47