Jawaban:
<c:xxx>
Tag JSTL adalah semua pembuat tag dan mereka dieksekusi selama waktu pembuatan view , sedangkan <h:xxx>
tag JSF adalah semua komponen UI dan mereka dieksekusi selama view render time .
Perhatikan bahwa dari JSF sendiri <f:xxx>
dan <ui:xxx>
tag hanya mereka yang tidak tidak memperpanjang dari UIComponent
juga taghandlers, misalnya <f:validator>
, <ui:include>
, <ui:define>
, dll yang yang memperpanjang dari UIComponent
juga komponen JSF UI, misalnya <f:param>
, <ui:fragment>
, <ui:repeat>
, dll Dari komponen JSF UI hanya id
dan binding
atribut juga dievaluasi selama tampilan build time. Jadi jawaban di bawah ini tentang siklus hidup JSTL juga berlaku untuk id
dan binding
atribut komponen JSF.
Tampilan build time adalah saat ketika file XHTML / JSP akan diurai dan dikonversi ke pohon komponen JSF yang kemudian disimpan UIViewRoot
pada FacesContext
. Waktu render tampilan adalah saat ketika komponen pohon JSF akan menghasilkan HTML, dimulai dengan UIViewRoot#encodeAll()
. Jadi: Komponen JSF UI dan tag JSTL tidak berjalan dalam sinkronisasi seperti yang Anda harapkan dari pengkodean. Anda dapat memvisualisasikannya sebagai berikut: JSTL berjalan dari atas ke bawah terlebih dahulu, menghasilkan pohon komponen JSF, lalu giliran JSF untuk menjalankan dari atas ke bawah lagi, menghasilkan output HTML.
<c:forEach>
vs. <ui:repeat>
Sebagai contoh, markas Facelet ini berulang di atas 3 item menggunakan <c:forEach>
:
<c:forEach items="#{bean.items}" var="item">
<h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
... membuat selama waktu pembuatan, tiga <h:outputText>
komponen terpisah di pohon komponen JSF, secara kasar direpresentasikan seperti ini:
<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}" />
... yang pada gilirannya secara individual menghasilkan output HTML mereka selama waktu render tampilan:
<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Perhatikan bahwa Anda perlu memastikan keunikan ID komponen dan secara manual juga dievaluasi selama waktu pembuatan tampilan.
Sementara markas Facelet ini mengulangi lebih dari 3 item menggunakan <ui:repeat>
, yang merupakan komponen UI JSF:
<ui:repeat id="items" value="#{bean.items}" var="item">
<h:outputText id="item" value="#{item.value}" />
</ui:repeat>
... sudah berakhir seperti apa adanya di komponen pohon JSF di mana <h:outputText>
komponen yang sama adalah selama tampilan membuat waktu digunakan kembali untuk menghasilkan output HTML berdasarkan putaran iterasi saat ini:
<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>
Perhatikan bahwa <ui:repeat>
sebagai NamingContainer
komponen sudah memastikan keunikan ID klien berdasarkan indeks iterasi; itu juga tidak mungkin untuk menggunakan EL dalam id
atribut komponen anak dengan cara ini karena juga dievaluasi selama view build time sementara #{item}
hanya tersedia selama view render time. Hal yang sama juga berlaku untuk h:dataTable
komponen dan sejenisnya.
<c:if>
/ <c:choose>
vs.rendered
Sebagai contoh lain, marka Facelets ini secara kondisional menambahkan tag yang berbeda menggunakan <c:if>
(Anda juga dapat menggunakan <c:choose><c:when><c:otherwise>
ini):
<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
hanya akan menambahkan <h:inputText>
komponen ke komponen pohon JSF:
<h:inputText ... />
Sementara markas Facelets ini:
<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />
... akan berakhir persis seperti di atas dalam komponen pohon JSF terlepas dari kondisinya. Ini dengan demikian dapat berakhir di pohon komponen "kembung" ketika Anda memiliki banyak dari mereka dan mereka benar-benar didasarkan pada model "statis" (yaitu field
tidak pernah berubah selama setidaknya ruang lingkup tampilan). Juga, Anda mungkin mengalami masalah EL ketika Anda berurusan dengan subclass dengan properti tambahan di versi Mojarra sebelum 2.2.7.
<c:set>
vs. <ui:param>
Mereka tidak bisa dipertukarkan. The <c:set>
set variabel dalam lingkup EL, yang dapat diakses hanya setelah lokasi tag selama waktu pandangan membangun, tetapi di mana saja dalam pandangan selama tampilan waktu render. The <ui:param>
melewati variabel EL untuk template facelet termasuk melalui <ui:include>
, <ui:decorate template>
atau <ui:composition template>
. Versi JSF yang lebih lama memiliki bug di mana <ui:param>
variabel juga tersedia di luar templat Facelet yang dipermasalahkan, ini seharusnya tidak pernah diandalkan.
The <c:set>
tanpa scope
atribut akan berperilaku seperti sebuah alias. Itu tidak men-cache hasil ekspresi EL dalam lingkup apa pun. Dengan demikian dapat digunakan dengan baik di dalam misalnya komponen iterasi JSF. Jadi, misalnya di bawah ini akan berfungsi dengan baik:
<ui:repeat value="#{bean.products}" var="product">
<c:set var="price" value="#{product.price}" />
<h:outputText value="#{price}" />
</ui:repeat>
Hanya saja tidak cocok untuk misalnya menghitung jumlah dalam satu lingkaran. Untuk itu alih-alih gunakan aliran EL 3.0 :
<ui:repeat value="#{bean.products}" var="product">
...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>
Hanya saja, ketika Anda mengatur scope
atribut dengan satu nilai-nilai yang diijinkan request
, view
, session
, atau application
, maka akan dievaluasi segera selama waktu pandangan membangun dan disimpan dalam ruang lingkup tertentu.
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Ini akan dievaluasi hanya sekali dan tersedia #{dev}
di seluruh aplikasi.
Menggunakan JSTL hanya dapat menyebabkan hasil yang tak terduga ketika sedang digunakan dalam komponen JSF iterasi seperti <h:dataTable>
, <ui:repeat>
, dll, atau ketika JSTL atribut tag tergantung pada hasil dari peristiwa JSF seperti preRenderView
atau diserahkan nilai bentuk dalam model yang tidak tersedia selama pandangan membangun waktu . Jadi, gunakan tag JSTL hanya untuk mengontrol aliran bangunan pohon komponen JSF. Gunakan komponen JSF UI untuk mengontrol aliran pembuatan output HTML. Jangan ikat var
komponen iterasi JSF ke atribut tag JSTL. Jangan mengandalkan acara JSF di atribut tag JSTL.
Kapan pun Anda merasa perlu untuk mengikat komponen ke backing bean via binding
, atau ambil satu via findComponent()
, dan membuat / memanipulasi anak-anaknya menggunakan kode Java dengan backing bean dengan new SomeComponent()
dan apa yang tidak, maka Anda harus segera berhenti dan mempertimbangkan untuk menggunakan JSTL sebagai gantinya. Karena JSTL juga berbasis XML, kode yang diperlukan untuk secara dinamis membuat komponen JSF akan menjadi jauh lebih mudah dibaca dan dipelihara.
Penting untuk diketahui adalah bahwa versi Mojarra yang lebih tua dari 2.1.18 memiliki bug dalam penyimpanan sebagian saat mereferensikan tampilan kacang scoped di atribut tag JSTL. Kacang scoped seluruh tampilan akan baru dibuat alih-alih diambil dari pohon tampilan (hanya karena pohon tampilan lengkap belum tersedia pada titik JSTL berjalan). Jika Anda mengharapkan atau menyimpan beberapa negara dalam tampilan yang dicakup kacang oleh atribut tag JSTL, maka itu tidak akan mengembalikan nilai yang Anda harapkan, atau itu akan "hilang" dalam kacang yang dicakup tampilan nyata yang dikembalikan setelah tampilan pohon dibangun. Jika Anda tidak dapat memutakhirkan ke Mojarra 2.1.18 atau yang lebih baru, pekerjaan di sekitar adalah mematikan sebagian penghematan negara web.xml
seperti di bawah ini:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
@ViewScoped
gagal dalam penangan tagUntuk melihat beberapa contoh dunia nyata di mana tag JSTL sangat membantu (yaitu ketika benar-benar digunakan selama membangun tampilan), lihat pertanyaan / jawaban berikut:
Mengenai persyaratan fungsional konkret Anda, jika Anda ingin merender komponen JSF secara kondisional, gunakan rendered
atribut pada komponen HTML JSF sebagai gantinya, terutama jika #{lpc}
mewakili item iterasi yang saat ini di-iterasi dari komponen iterasi JSF seperti <h:dataTable>
atau <ui:repeat>
.
<h:someComponent rendered="#{lpc.verbose}">
...
</h:someComponent>
Atau, jika Anda ingin membangun (membuat / menambahkan) komponen JSF secara kondisional, maka tetap gunakan JSTL. Ini jauh lebih baik daripada melakukan new SomeComponent()
di Jawa secara lisan.
<c:if test="#{lpc.verbose}">
<h:someComponent>
...
</h:someComponent>
</c:if>
<ui:repeat>
adalah penangan tag (karena baris ini, " Perhatikan bahwa JSF sendiri <f:xxx>
dan <ui:xxx>
... ") sama seperti <c:forEach>
dan karenanya, dievaluasi pada tampilan waktu pembuatan (sekali lagi sama seperti saja <c:forEach>
) . Jika memang demikian, seharusnya tidak ada perbedaan fungsional yang terlihat antara <ui:repeat>
dan <c:forEach>
? Saya tidak mengerti arti sebenarnya dari paragraf itu :)
<f:xxx>
dan <ui:xxx>
tag yang tidak diperpanjang UIComponent
juga merupakan penangan tag. " Berusaha menyiratkan bahwa <ui:repeat>
itu juga penangan tag karena <ui:xxx>
juga termasuk <ui:repeat>
? Ini kemudian harus berarti bahwa itu <ui:repeat>
adalah salah satu komponen <ui:xxx>
yang meluas UIComponent
. Karenanya, ini bukan penangan tag. (Beberapa dari mereka mungkin tidak memperpanjang UIComponent
. Oleh karena itu, mereka penangan tag) Apakah itu?
<c:set>
tanpa scope
membuat alias dari ekspresi EL alih-alih menetapkan nilai yang dievaluasi dalam cakupan target. Coba scope="request"
sebagai gantinya, yang akan segera mengevaluasi nilai (selama waktu pembuatan tampilan memang) dan menetapkannya sebagai atribut permintaan (yang tidak akan "ditimpa" selama iterasi). Di bawah selimut, itu membuat dan menetapkan ValueExpression
objek.
ClassNotFoundException
. Ketergantungan runtime proyek Anda rusak. Kemungkinan besar Anda menggunakan server non-JavaEE seperti Tomcat dan Anda lupa menginstal JSTL, atau Anda tidak sengaja memasukkan JSTL 1.0 dan JSTL 1.1+. Karena dalam JSTL 1.0 paketnya adalah javax.servlet.jstl.core.*
dan sejak JSTL 1.1 ini telah menjadi javax.servlet.jsp.jstl.core.*
. Petunjuk untuk menginstal JSTL dapat ditemukan di sini: stackoverflow.com/a/4928309
menggunakan
<h:panelGroup rendered="#{lpc.verbose}">
...
</h:panelGroup>
Untuk keluaran seperti sakelar, Anda bisa menggunakan sakelar tampilan dari PrimeFaces Extensions.