JSF2 Facelets JSTL… mantıklı?


163

Şartlı kod biraz Facelets kodunu çıktı istiyorum.

Bu amaçla, JSTL etiketleri düzgün çalışıyor gibi görünüyor:

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

Ancak bunun en iyi uygulama olup olmadığından emin değilim? Hedefime ulaşmanın başka bir yolu var mı?

Yanıtlar:


320

Giriş

JSTL <c:xxx>etiketlerinin tümü etiket işleyicilerdir ve görünüm oluşturma süresi boyunca yürütülürken , JSF <h:xxx>etiketleri tüm UI bileşenleridir ve görünüm oluşturma süresi sırasında yürütülür .

Not o JSF kendi gelen <f:xxx>ve <ui:xxx>iciler sadece etiketleri değil uzanmaktadır UIComponent, ayrıca taghandlers vardır mesela <f:validator>, <ui:include>, <ui:define>vb uzanan olanlar UIComponentda mesela JSF UI bileşenleri vardır <f:param>, <ui:fragment>, <ui:repeat>vb MTU UI bileşenleri itibaren sadece idve bindingnitelikleridir ayrıca görünüm oluşturma süresi boyunca değerlendirilir. Dolayısıyla JSTL yaşam döngüsüne ilişkin aşağıdaki cevap JSF bileşenlerinin idve bindingöznitelikleri için de geçerlidir .

Görünüm oluşturma süresi XHTML / JSP ayrıştırılır ve daha sonra da depolanan bir MTU bileşeni ağaca dönüştürülecek olduğu an UIViewRootarasında FacesContext. Görünüm oluşturma süresi, JSF bileşen ağacının HTML üretmek üzere olduğu andan itibaren başlar UIViewRoot#encodeAll(). Yani: JSF UI bileşenleri ve JSTL etiketleri kodlamadan beklediğiniz gibi senkronize çalışmaz. Aşağıdaki gibi görselleştirebilirsiniz: JSTL önce yukarıdan aşağıya doğru çalışır ve JSF bileşen ağacını üretir, sonra JSF'nin HTML çıkışını üreterek tekrar yukarıdan aşağıya doğru sıraya girer.

<c:forEach> vs <ui:repeat>

Örneğin, bu Facelets işaretlemesi 3 öğeden fazla yineleme kullanarak <c:forEach>:

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

... görünüm oluşturma süresi boyunca <h:outputText>, kabaca şu şekilde temsil edilen JSF bileşen ağacında üç ayrı bileşen oluşturur :

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

... bu da görüntüleme oluşturma süresi boyunca HTML çıktılarını ayrı ayrı oluşturur:

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

Bileşen kimliklerinin benzersizliğini manuel olarak sağlamanız gerektiğini ve bunların görünüm oluşturma sırasında da değerlendirildiğini unutmayın.

Bu Facelets biçimlendirmesi <ui:repeat>, bir JSF UI bileşeni olan 3 öğeden fazla yineleme yaparken :

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

... JSF bileşen ağacında olduğu gibi sonlanır ve burada aynı <h:outputText>bileşen, görünüm yineleme süresinin geçerli yineleme turuna dayalı olarak HTML çıktısı oluşturmak için yeniden kullanılması sırasında kullanılır :

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

Not olduğu <ui:repeat>bir varlık olarak NamingContainerbileşen zaten yineleme endeksine dayalı müşteri kimliği benzersizliğini sağlanmalıdır; Ayrıca id, görünüm oluşturma süresi sırasında da değerlendirilirken #{item}, yalnızca görünüm oluşturma süresi sırasında da kullanılabilir olduğundan, EL'i alt bileşenlerin niteliğinde bu şekilde kullanmak da mümkün değildir . Aynı h:dataTableve benzer bileşenler için de geçerlidir .

<c:if>/ <c:choose>vsrendered

Başka bir örnek olarak, bu Facelets biçimlendirmesi aşağıdakileri kullanarak koşullu olarak farklı etiketler ekler <c:if>( <c:choose><c:when><c:otherwise>bunun için de kullanabilirsiniz ):

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

... bileşeni type = TEXTyalnızca <h:inputText>JSF bileşen ağacına ekleyecekse :

<h:inputText ... />

Bu Facelets işaretlemesi olurken:

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

... durumdan bağımsız olarak JSF bileşen ağacında yukarıdaki gibi sonuçlanır. Bu nedenle, birçoğuna sahip olduğunuzda "şişirilmiş" bir bileşen ağacıyla sonuçlanabilir ve bunlar aslında bir "statik" modele dayanır (yani fielden azından görünüm kapsamı sırasında hiç değişmez). Ayrıca, EL içine çalışabilir sorun Eğer 2.2.7 önce Mojarra sürümlerinde ek özelliklere sahip alt sınıfları ile anlaşma yaparken.

<c:set> vs <ui:param>

Değiştirilemezler. <c:set>Setleri sadece erişilebilir EL kapsamında değişken, sonra görünüm inşa süresi boyunca etiket konumu, ama hiçbir yerde görünümde görüntüleme sırasında zaman kılmak. <ui:param>Bir Facelet şablonu için bir EL değişken ile yer geçer <ui:include>, <ui:decorate template>ya da <ui:composition template>. Eski JSF sürümlerinde, <ui:param>değişkenin söz konusu Facelet şablonunun dışında da kullanılabileceği hatalar vardı , buna asla güvenilmemelidir.

Niteliği <c:set>olmayan scopebir takma ad gibi davranacaktır. EL ifadesinin sonucunu hiçbir kapsamda önbelleğe almaz. Böylece örneğin JSF bileşenlerini yinelemek için mükemmel şekilde kullanılabilir. Böylece, örneğin aşağıda iyi çalışır:

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

Sadece bir döngüdeki toplamı hesaplamak için uygun değildir. Bunun için EL 3.0 akışını kullanın :

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

Belirlediğiniz Yalnızca, scopeizin verilen değerlerden biri ile niteliğini request, view, session, veya application, o zaman görünümü inşa süresi boyunca hemen değerlendirilir ve belirtilen kapsamda saklanacaktır.

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

Bu sadece bir kez değerlendirilecek #{dev}ve tüm başvuru boyunca olduğu gibi sağlanacaktır .

JSF bileşen ağacı yapısını kontrol etmek için JSTL kullanın

Gibi JSF ilerlerken bileşenlerin içinde kullanılan edilirken JSTL kullanılması, yalnızca beklenmedik sonuçlara yol açabilir <h:dataTable>, <ui:repeat>vb veya JSTL etiket nitelikleri gibi JSF olayların sonuçlarına bağlı olduğunda preRenderViewgörünümü inşa süre içinde bulunmayan modelinde form değerleri veya gönderilen . Bu nedenle, JSTL etiketlerini yalnızca JSF bileşen ağacı oluşturma akışını denetlemek için kullanın. HTML çıkış oluşturma akışını kontrol etmek için JSF UI bileşenlerini kullanın. varYinelenen JSF bileşenlerini JSTL etiketi özniteliklerine bağlamayın . JSTL etiketi niteliklerindeki JSF olaylarına güvenmeyin.

Her zaman bir bileşeni yedekleme çekirdeğine bağlamanız bindingveya bir tane yakalamanız findComponent()ve bir yedekleme çekirdeğinde Java kodunu kullanarak çocuklarını oluşturup / manipüle new SomeComponent()etmeniz gerektiğini düşündüğünüzde, o zaman hemen JSTL kullanmayı bırakmalı ve kullanmayı düşünmelisiniz. JSTL ayrıca XML tabanlı olduğundan, JSF bileşenlerini dinamik olarak oluşturmak için gereken kod çok daha iyi okunabilir ve bakımı yapılabilir hale gelir.

Bilmesi gereken önemli bir nokta, 2.1.18'den daha eski Mojarra sürümlerinde, JSTL etiketi özelliğinde görünüm kapsamındaki bir çekirdeğe başvururken kısmi durum kaydetmede bir hata olmasıdır. Tüm görünüm kapsamı çekirdeği , görünüm ağacından alınmak yerine yeni olarak yeniden oluşturulacaktır (yalnızca görünüm ağacının henüz JSTL'nin çalıştığı noktada mevcut olmaması nedeniyle). Bir JSTL etiketi özniteliği tarafından görünüm kapsamındaki fasulyede bir durum bekliyorsanız veya saklıyorsanız, beklediğiniz değeri döndürmez veya görünümden sonra geri yüklenen gerçek görünüm kapsamındaki fasulyede "kaybolur" ağaç inşa edilmiştir. Mojarra 2.1.18 veya daha yeni bir sürüme geçemiyorsanız, geçici çözüm kısmi durum tasarrufunu web.xmlaşağıdaki gibi kapatmaktır :

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

Ayrıca bakınız:

JSTL etiketlerinin yararlı olduğu bazı gerçek dünya örneklerini görmek için (örneğin, görünüm oluştururken gerçekten doğru bir şekilde kullanıldığında), aşağıdaki sorulara / cevaplara bakın:


Kısaca

Somut işlevsel gereksiniminizle ilgili olarak, JSF bileşenlerini koşullu olarak oluşturmak istiyorsanız , renderedbunun yerine JSF HTML bileşenindeki özniteliği kullanın, özellikle veya #{lpc}gibi bir JSF yineleme bileşeninin şu anda yinelenen öğesini temsil ediyorsa .<h:dataTable><ui:repeat>

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

Veya koşullu olarak JSF bileşenleri oluşturmak (oluşturmak / eklemek) istiyorsanız , JSTL kullanmaya devam edin. Java'da ayrıntılı olarak yapmaktan çok daha iyi new SomeComponent().

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

Ayrıca bakınız:


3
@Aklin: Hayır? Bu örneğe ne dersin ?
BalusC

1
İlk paragrafı uzun süre doğru yorumlayamam (verilen örnekler çok açık olsa da). Bu nedenle, bu yorumu tek yol olarak bırakıyorum. Bu paragrafa göre <ui:repeat>, bir etiket işleyicisi olan izlenim içindeyim (bu satırdan dolayı, " JSF'nin kendisinin <f:xxx>ve <ui:xxx>... " olduğunu unutmayın) <c:forEach>ve bu nedenle, görünüm oluşturma zamanında değerlendirilir (yine aynı gibi <c:forEach>) . Eğer öyleyse, <ui:repeat>ve <c:forEach>? Arasında görünür, işlevsel bir fark olmamalıdır. Bu paragrafın tam olarak ne anlama geldiğini anlamıyorum :)
Tiny

1
Üzgünüm, bu yazıyı daha fazla kirletmeyeceğim. Benim dikkat içine önceki yorumunu aldı ancak bu cümleyi, "does Not MTU kendi olduğunu <f:xxx>ve <ui:xxx>uzatmaz etiketleri UIComponentde etiket işleyicileri vardır. " İma etmek girişimleri <ui:repeat>nedeniyle de bir etiket işleyicisi olan <ui:xxx>da kapsar <ui:repeat>? Bu daha sonra bunun <ui:repeat>, <ui:xxx>uzayan bileşenlerden biri olduğu anlamına gelmelidir UIComponent. Bu nedenle, bir etiket işleyici değildir. (Bazıları uzanmayabilir UIComponent. Bu nedenle, etiket işleyicisidir) Öyle mi?
Küçük

2
@Shirgill: <c:set>without scope, değerlendirilen değeri hedef kapsamda ayarlamak yerine EL ifadesinin bir takma adını oluşturur. Deneyin scope="request"hemen (aslında görünüm oluşturma süresi boyunca) değerini değerlendirmek, hangi yerine ve (yineleme sırasında "üzerine yazılır" olmayacak olan) istek niteliği olarak ayarlayın. Kapakların altında bir ValueExpressionnesne oluşturur ve ayarlar .
BalusC

1
@ K.Nicholas: Kapakların altında a ClassNotFoundException. Projenizin çalışma zamanı bağımlılıkları bozuldu. Büyük olasılıkla Tomcat gibi JavaEE olmayan bir sunucu kullanıyorsunuz ve JSTL'yi yüklemeyi unuttunuz veya yanlışlıkla JSTL 1.0 ve JSTL 1.1+ sürümlerini dahil ettiniz. Çünkü JSTL 1.0'da paket javax.servlet.jstl.core.*ve JSTL 1.1'den beri bu olmuştur javax.servlet.jsp.jstl.core.*.
JSTL'yi

13

kullanım

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

Teşekkürler, harika cevap. Daha genel olarak: JSTL etiketleri hala mantıklı mı yoksa JSF 2.0'dan beri kullanılmıyor mu?
Ocak

Çoğu durumda, evet. Ama bazen onları kullanmak uygun olur
Bozho

3
H: panelGroup kullanımı kirli bir çözümdür, çünkü bir <span> etiketi oluşturur, c: if ise html koduna hiçbir şey eklemez. h: panelGroup, öğeleri gruplandırdığından panelGrid öğelerinin içinde de sorunludur.
Rober2D2

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.