Chciałbym warunkowo wypisać trochę kodu Facelets.
W tym celu tagi JSTL wydają się działać dobrze:
<c:if test="${lpc.verbose}">
...
</c:if>
Nie jestem jednak pewien, czy jest to najlepsza praktyka? Czy jest inny sposób na osiągnięcie celu?
JSTL <c:xxx>
tagi są wszystkie taghandlers i są one wykonywane podczas widzenia czas budowy , a JSF <h:xxx>
tagi są wszystkie elementy interfejsu użytkownika i są one wykonywane podczas widzenia czas renderowania .
Należy zauważyć, że z JSF własnych <f:xxx>
i <ui:xxx>
tylko znaczniki które nie nie wychodzą z UIComponent
również taghandlers, np <f:validator>
, <ui:include>
, <ui:define>
, itd. Te, które rozciągają się od UIComponent
są również komponenty JSF UI, na przykład <f:param>
, <ui:fragment>
, <ui:repeat>
, itd. Od komponentów JSF UI tylko id
i binding
atrybuty oceniane również w czasie kompilacji widoku. Zatem poniższa odpowiedź dotycząca cyklu życia JSTL dotyczy również atrybutów id
i binding
komponentów JSF.
Czas widok kompilacji jest ten moment, kiedy plik XHTML / JSP ma być analizowany i konwertowany do drzewa komponentów JSF, która jest następnie przechowywany jako UIViewRoot
z FacesContext
. Czas renderowania widoku to moment, w którym drzewo komponentów JSF ma wygenerować kod HTML, zaczynając od UIViewRoot#encodeAll()
. Tak więc: komponenty interfejsu użytkownika JSF i tagi JSTL nie działają synchronicznie, jak można by oczekiwać po kodowaniu. Możesz to wizualizować w następujący sposób: JSTL działa najpierw od góry do dołu, tworząc drzewo komponentów JSF, a następnie kolej JSF, aby ponownie uruchomić od góry do dołu, tworząc wynik HTML.
<c:forEach>
vs <ui:repeat>
Na przykład ten znacznik Facelets iterujący po 3 elementach przy użyciu <c:forEach>
:
<c:forEach items="#{bean.items}" var="item">
<h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
... tworzy podczas budowania widoku trzy oddzielne <h:outputText>
komponenty w drzewie komponentów JSF, z grubsza reprezentowane w następujący sposób:
<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}" />
... które z kolei indywidualnie generują swoje dane wyjściowe HTML podczas wyświetlania czasu renderowania:
<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Zwróć uwagę, że musisz ręcznie upewnić się, że identyfikatory komponentów są niepowtarzalne i że są one również oceniane w czasie kompilacji widoku.
Podczas gdy ten znacznik Facelets iteruje po 3 elementach przy użyciu <ui:repeat>
, który jest komponentem interfejsu użytkownika JSF:
<ui:repeat id="items" value="#{bean.items}" var="item">
<h:outputText id="item" value="#{item.value}" />
</ui:repeat>
... już kończy się tak, jak jest w drzewie komponentów JSF, dzięki czemu ten sam <h:outputText>
komponent jest ponownie używany podczas renderowania widoku do generowania danych wyjściowych 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 NamingContainer
składnik już zapewnił niepowtarzalność identyfikatora klienta w oparciu o indeks iteracji; nie jest również możliwe użycie EL w id
atrybutach komponentów potomnych w ten sposób, ponieważ jest on również oceniany podczas budowania widoku, podczas gdy #{item}
jest dostępny tylko podczas renderowania widoku. To samo dotyczy h:dataTable
i podobnych komponentów.
<c:if>
/ <c:choose>
vsrendered
Jako kolejny przykład, ten znacznik Facelets warunkowo dodaje różne tagi przy użyciu <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
tylko dodania <h:inputText>
komponentu do drzewa komponentów JSF:
<h:inputText ... />
Podczas gdy ten znacznik Facelets:
<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 powyżej w drzewie komponentów JSF, niezależnie od warunku. Może to więc skończyć się w „rozdętym” drzewie komponentów, jeśli masz ich wiele i są one faktycznie oparte na modelu „statycznym” (tj. field
Nigdy się nie zmieniają przynajmniej w zakresie widoku). Możesz również napotkać problemy z EL, gdy masz do czynienia z podklasami z dodatkowymi właściwościami w wersjach Mojarra starszych niż 2.2.7.
<c:set>
vs <ui:param>
Nie są wymienne. Te <c:set>
zestawy zmienna w zakresie EL, która jest dostępna tylko po położenia znacznika czasu podczas kompilacji widok, ale nigdzie w widoku podczas podglądu na czas renderowania. <ui:param>
Przechodzi zmienna EL do szablonu Facelet zawarte przez <ui:include>
, <ui:decorate template>
lub <ui:composition template>
. Starsze wersje JSF zawierały błędy, w wyniku których <ui:param>
zmienna jest również dostępna poza omawianym szablonem Facelet, nigdy nie należy na tym polegać.
<c:set>
Bez scope
atrybutu będzie zachowywać się jak aliasu. Nie buforuje wyniku wyrażenia EL w żadnym zakresie. Dzięki temu może być doskonale używany wewnątrz, na przykład iteracji 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. Zamiast tego użyj strumienia EL 3.0 :
<ui:repeat value="#{bean.products}" var="product">
...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>
Dopiero, gdy ustawisz scope
atrybut jednej z dopuszczalnych wartości request
, view
, session
, lub application
, po czym zostanie on natychmiast oceniano w czasie widok budować i przechowywane w określonym zakresie.
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Zostanie to ocenione tylko raz i będzie dostępne #{dev}
w całej aplikacji.
Korzystanie JSTL może prowadzić nie tylko nieoczekiwane wyniki, gdy są stosowane w środku elementów JSF iteracji, takie jak <h:dataTable>
, <ui:repeat>
itp, lub gdy znacznik JSTL cechy zależą od wyników zdarzeń JSF jak preRenderView
i złożone wartości tworzą się w modelu, które nie są dostępne w widoku czas budowy . Dlatego używaj znaczników JSTL tylko do kontrolowania przepływu budowania drzewa komponentów JSF. Użyj komponentów JSF UI, aby kontrolować przepływ generowania danych wyjściowych HTML. Nie var
wiąż iteracyjnych komponentów JSF z atrybutami znaczników JSTL. Nie polegaj na zdarzeniach JSF w atrybutach tagów JSTL.
Za każdym razem, gdy myślisz, że musisz powiązać komponent z zapasowym komponentem bean za pośrednictwem binding
lub pobrać jeden za pośrednictwem findComponent()
i utworzyć / manipulować jego elementami podrzędnymi za pomocą kodu Java w komponencie bean zapasowym z, new SomeComponent()
a co nie, należy natychmiast przerwać i rozważyć użycie JSTL. Ponieważ JSTL jest również oparty na XML, kod potrzebny do dynamicznego tworzenia komponentów JSF stanie się o wiele bardziej czytelny i łatwiejszy w utrzymaniu.
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 komponentu bean o zasięgu widoku w atrybucie tagu JSTL. Cały widok scoped fasola będzie nowo odtworzone zamiast pobierane z widoku drzewa (po prostu dlatego, że pełne drzewo widok nie jest jeszcze dostępna w seriach punkt JSTL). Jeśli spodziewasz się lub przechowujesz jakiś stan w komponencie bean z zakresem widoku za pomocą atrybutu tagu JSTL, to nie zwróci on oczekiwanej wartości lub zostanie „utracony” w komponencie bean o zasięgu rzeczywistym, który jest przywracany po wyświetleniu widoku drzewo jest zbudowane. W przypadku, gdy nie możesz zaktualizować Mojarra do wersji 2.1.18 lub nowszej, obejście polega na wyłączeniu częściowego zapisywania stanu w web.xml
następujący sposób:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
@ViewScoped
w obsłudze tagówAby zobaczyć przykłady z rzeczywistego świata, w których tagi JSTL są pomocne (tj. Gdy są naprawdę poprawnie używane podczas tworzenia widoku), zobacz następujące pytania / odpowiedzi:
Jeśli chodzi o konkretne wymaganie funkcjonalne, jeśli chcesz warunkowo renderować komponenty JSF, rendered
zamiast tego użyj atrybutu komponentu JSF HTML, szczególnie jeśli #{lpc}
reprezentuje aktualnie iterowany element komponentu iterującego JSF, taki jak <h:dataTable>
lub <ui:repeat>
.
<h:someComponent rendered="#{lpc.verbose}">
...
</h:someComponent>
Lub, jeśli chcesz warunkowo budować (tworzyć / dodawać) komponenty JSF, nadal używaj JSTL. Jest to o wiele lepsze niż werbalne robienie tego new SomeComponent()
w Javie.
<c:if test="#{lpc.verbose}">
<h:someComponent>
...
</h:someComponent>
</c:if>
<ui:repeat>
jest to program obsługi tagów (z powodu tego wiersza „ Zwróć uwagę, że własny JSF<f:xxx>
i<ui:xxx>
... ”) jest tak samo, jak<c:forEach>
i dlatego jest oceniany w czasie kompilacji widoku (ponownie tak samo jak<c:forEach>
) . Jeśli tak, to nie powinno być żadnej widocznej, funkcjonalnej różnicy między<ui:repeat>
i<c:forEach>
? Nie rozumiem, co dokładnie oznacza ten akapit :)<f:xxx>
i<ui:xxx>
znaczników, które nie rozciągająUIComponent
się również koparki tag. ” Próbuje się sugerować, że<ui:repeat>
to również obsługi znacznika, ponieważ<ui:xxx>
obejmuje również<ui:repeat>
? Powinno to zatem oznaczać, że<ui:repeat>
jest to jeden z elementów,<ui:xxx>
który się rozciągaUIComponent
. Dlatego nie jest to program obsługujący znaczniki. (Niektóre z nich mogą się nie rozszerzaćUIComponent
. W związku z tym służą do obsługi tagów).<c:set>
withoutscope
tworzy alias wyrażenia EL zamiast ustawiania wartości szacowanej w zakresie docelowym. Spróbujscope="request"
zamiast tego, co natychmiast oszacuje wartość (faktycznie w czasie kompilacji widoku) i ustawi ją jako atrybut żądania (który nie zostanie „nadpisany” podczas iteracji). Pod kołdrą tworzy i ustawiaValueExpression
obiekt.ClassNotFoundException
. Zależności wykonawcze twojego projektu są zepsute. Najprawdopodobniej używasz serwera innego niż JavaEE, takiego jak Tomcat i zapomniałeś zainstalować JSTL lub przypadkowo dołączyłeś zarówno JSTL 1.0, jak i JSTL 1.1+. Ponieważ w JSTL 1.0 pakiet jest,javax.servlet.jstl.core.*
a od JSTL 1.1 stał sięjavax.servlet.jsp.jstl.core.*
. Wskazówki dotyczące instalacji JSTL można znaleźć tutaj: stackoverflow.com/a/4928309posługiwać się
źródło
Aby uzyskać wyjście podobne do przełącznika, możesz użyć przełącznika z rozszerzeń PrimeFaces.
źródło