Câu trả lời:
Các <c:xxx>
thẻ JSTL là tất cả các trình tạo thẻ và chúng được thực thi trong thời gian xây dựng chế độ xem , trong khi <h:xxx>
các thẻ JSF là tất cả các thành phần UI và chúng được thực thi trong thời gian hiển thị chế độ xem .
Lưu ý rằng từ JSF riêng <f:xxx>
và <ui:xxx>
thẻ chỉ những người mà ta không mở rộng từ UIComponent
cũng taghandlers, ví dụ như <f:validator>
, <ui:include>
, <ui:define>
vv Những người mà kéo dài từ UIComponent
cũng JSF UI thành phần, ví dụ như <f:param>
, <ui:fragment>
, <ui:repeat>
, vv Từ các thành phần JSF UI chỉ id
và binding
thuộc tính cũng được đánh giá trong thời gian xây dựng xem. Do đó, câu trả lời dưới đây về vòng đời của JSTL cũng áp dụng cho các thuộc tính id
và binding
thuộc tính của các thành phần JSF.
Thời gian xem build là khoảnh khắc khi tập tin XHTML / JSP là để được phân tích và chuyển đổi sang một cái cây thành phần JSF mà sau đó được lưu giữ như UIViewRoot
của FacesContext
. Thời gian kết xuất khung nhìn là thời điểm đó khi cây thành phần JSF sắp tạo HTML, bắt đầu bằng UIViewRoot#encodeAll()
. Vì vậy: Các thành phần UI của JSF và các thẻ JSTL không chạy đồng bộ như bạn mong đợi từ mã hóa. Bạn có thể hình dung nó như sau: JSTL chạy từ trên xuống dưới trước, tạo ra cây thành phần JSF, sau đó đến lượt JSF chạy từ trên xuống dưới một lần nữa, tạo ra đầu ra HTML.
<c:forEach>
đấu với <ui:repeat>
Ví dụ: đánh dấu Facelets này lặp lại trên 3 mục bằng cách sử dụng <c:forEach>
:
<c:forEach items="#{bean.items}" var="item">
<h:outputText id="item_#{item.id}" value="#{item.value}" />
</c:forEach>
... tạo trong thời gian xây dựng chế độ xem ba <h:outputText>
thành phần riêng biệt trong cây thành phần JSF, đại diện như thế này:
<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}" />
... lần lượt tạo riêng lẻ đầu ra HTML của họ trong thời gian xem kết xuất:
<span id="item_1">value1</span>
<span id="item_2">value2</span>
<span id="item_3">value3</span>
Lưu ý rằng bạn cần đảm bảo thủ công tính duy nhất của ID thành phần và những ID đó cũng được đánh giá trong thời gian xây dựng chế độ xem.
Trong khi đánh dấu Facelets này lặp lại hơn 3 mục bằng cách sử dụng <ui:repeat>
, đó là thành phần UI của JSF:
<ui:repeat id="items" value="#{bean.items}" var="item">
<h:outputText id="item" value="#{item.value}" />
</ui:repeat>
... đã kết thúc như trong cây thành phần JSF, theo đó <h:outputText>
thành phần tương tự trong thời gian hiển thị được sử dụng lại để tạo đầu ra HTML dựa trên vòng lặp hiện tại:
<span id="items:0:item">value1</span>
<span id="items:1:item">value2</span>
<span id="items:2:item">value3</span>
Lưu ý rằng việc <ui:repeat>
là một NamingContainer
thành phần đã đảm bảo tính duy nhất của ID khách hàng dựa trên chỉ số lặp; Cũng không thể sử dụng EL trong id
thuộc tính của các thành phần con theo cách này vì nó cũng được đánh giá trong thời gian xây dựng chế độ xem trong khi #{item}
chỉ khả dụng trong thời gian hiển thị chế độ xem. Điều tương tự cũng đúng với một h:dataTable
và các thành phần tương tự.
<c:if>
/ <c:choose>
vsrendered
Một ví dụ khác, đánh dấu Facelets này có điều kiện thêm các thẻ khác nhau bằng cách sử dụng <c:if>
(bạn cũng có thể sử dụng <c:choose><c:when><c:otherwise>
cho việc này):
<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>
... trong trường hợp type = TEXT
chỉ thêm <h:inputText>
thành phần vào cây thành phần JSF:
<h:inputText ... />
Trong khi đánh dấu Facelets này:
<h:inputText ... rendered="#{field.type eq 'TEXT'}" />
<h:inputSecret ... rendered="#{field.type eq 'PASSWORD'}" />
<h:selectOneMenu ... rendered="#{field.type eq 'SELECTONE'}" />
... sẽ kết thúc chính xác như trên trong cây thành phần JSF bất kể điều kiện. Do đó, điều này có thể kết thúc trong một cây thành phần "cồng kềnh" khi bạn có nhiều trong số chúng và chúng thực sự dựa trên mô hình "tĩnh" (tức là field
không bao giờ thay đổi trong phạm vi ít nhất là phạm vi xem). Ngoài ra, bạn có thể gặp rắc rối EL khi bạn xử lý các lớp con với các thuộc tính bổ sung trong các phiên bản Mojarra trước 2.2.7.
<c:set>
đấu với <ui:param>
Chúng không thể thay thế cho nhau. Biến <c:set>
đặt trong phạm vi EL, chỉ có thể truy cập được sau vị trí thẻ trong thời gian xây dựng chế độ xem, nhưng ở bất kỳ vị trí nào trong chế độ xem trong thời gian hiển thị chế độ xem. Các <ui:param>
trôi qua một biến EL một mẫu Facelet bao gồm thông qua <ui:include>
, <ui:decorate template>
hoặc <ui:composition template>
. Các phiên bản JSF cũ hơn có lỗi, theo đó <ui:param>
biến cũng có sẵn bên ngoài khuôn mặt Facelet được đề cập, điều này không bao giờ nên dựa vào.
Không <c:set>
có scope
thuộc tính sẽ hoạt động như một bí danh. Nó không lưu trữ kết quả của biểu thức EL trong bất kỳ phạm vi nào. Do đó, nó hoàn toàn có thể được sử dụng bên trong ví dụ lặp lại các thành phần JSF. Do đó, ví dụ dưới đây sẽ hoạt động tốt:
<ui:repeat value="#{bean.products}" var="product">
<c:set var="price" value="#{product.price}" />
<h:outputText value="#{price}" />
</ui:repeat>
Nó chỉ không phù hợp với việc tính tổng trong một vòng lặp. Thay vào đó, hãy sử dụng luồng EL 3.0 :
<ui:repeat value="#{bean.products}" var="product">
...
</ui:repeat>
<p>Total price: #{bean.products.stream().map(product->product.price).sum()}</p>
Chỉ khi bạn thiết lập các scope
thuộc tính với một trong các giá trị cho phép request
, view
, session
, hoặc application
, sau đó nó sẽ được đánh giá ngay trong quá trình xem thời gian xây dựng và lưu trữ trong phạm vi chỉ định.
<c:set var="dev" value="#{facesContext.application.projectStage eq 'Development'}" scope="application" />
Điều này sẽ chỉ được đánh giá một lần và có sẵn #{dev}
trong toàn bộ ứng dụng.
Sử dụng JSTL chỉ có thể dẫn đến kết quả bất ngờ khi được sử dụng bên trong JSF iterating thành phần như <h:dataTable>
, <ui:repeat>
, vv, hoặc khi JSTL thẻ thuộc tính phụ thuộc vào kết quả của các sự kiện JSF như preRenderView
hoặc gửi giá trị hình thức trong mô hình mà không có sẵn trong quá trình xem thời gian xây dựng . Vì vậy, chỉ sử dụng các thẻ JSTL để kiểm soát luồng xây dựng cây thành phần JSF. Sử dụng các thành phần UI của JSF để kiểm soát luồng phát sinh HTML. Không liên kết các var
thành phần lặp lại của JSF với các thuộc tính thẻ JSTL. Không dựa vào các sự kiện JSF trong các thuộc tính thẻ JSTL.
Bất cứ khi nào bạn nghĩ rằng bạn cần liên kết một thành phần với bean hậu thuẫn thông qua binding
hoặc lấy một thành phần thông qua findComponent()
và tạo / thao tác các phần tử con của nó bằng cách sử dụng mã Java trong một bean hậu thuẫn new SomeComponent()
và không, thì bạn nên dừng ngay lập tức và xem xét sử dụng JSTL. Vì JSTL cũng dựa trên XML, mã cần thiết để tự động tạo các thành phần JSF sẽ trở nên dễ đọc và dễ bảo trì hơn rất nhiều.
Điều quan trọng cần biết là các phiên bản Mojarra cũ hơn 2.1,18 có lỗi trong việc lưu trạng thái một phần khi tham chiếu một bean có phạm vi xem trong thuộc tính thẻ JSTL. Toàn bộ phạm vi khung nhìn sẽ được tạo lại mới thay vì được truy xuất từ cây xem (đơn giản vì cây xem hoàn chỉnh chưa có sẵn tại thời điểm JSTL chạy). Nếu bạn đang mong đợi hoặc lưu trữ một số trạng thái trong bean có phạm vi xem theo thuộc tính thẻ JSTL, thì nó sẽ không trả về giá trị mà bạn mong đợi, hoặc nó sẽ bị "mất" trong bean có phạm vi xem thực được khôi phục sau chế độ xem cây được xây dựng. Trong trường hợp bạn không thể nâng cấp lên Mojarra 2.1.18 hoặc mới hơn, công việc xung quanh là tắt tiết kiệm một phần trạng thái web.xml
như sau:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
@ViewScoped
không thành công trong trình xử lý thẻĐể xem một số ví dụ trong thế giới thực nơi thẻ JSTL hữu ích (nghĩa là khi thực sự được sử dụng đúng cách trong khi xây dựng chế độ xem), hãy xem các câu hỏi / câu trả lời sau:
Đối với yêu cầu chức năng cụ thể của bạn, nếu bạn muốn hiển thị các thành phần JSF một cách có điều kiện, rendered
thay vào đó, hãy sử dụng thuộc tính trên thành phần HTML của JSF, đặc biệt nếu #{lpc}
đại diện cho mục được lặp hiện tại của thành phần lặp JSF như <h:dataTable>
hoặc <ui:repeat>
.
<h:someComponent rendered="#{lpc.verbose}">
...
</h:someComponent>
Hoặc, nếu bạn muốn xây dựng (tạo / thêm) các thành phần JSF một cách có điều kiện, thì hãy tiếp tục sử dụng JSTL. Đó là cách tốt hơn nhiều so với làm new SomeComponent()
bằng lời nói trong java.
<c:if test="#{lpc.verbose}">
<h:someComponent>
...
</h:someComponent>
</c:if>
<ui:repeat>
là một trình xử lý thẻ (vì dòng này, " Lưu ý rằng chính của JSF <f:xxx>
và <ui:xxx>
... ") giống như <c:forEach>
và do đó, nó được đánh giá tại thời điểm xây dựng chế độ xem (giống như giống như <c:forEach>
) . Nếu đó là sau đó, không nên có bất kỳ sự khác biệt rõ ràng, chức năng giữa <ui:repeat>
và <c:forEach>
? Tôi không hiểu chính xác đoạn đó có nghĩa là gì :)
<f:xxx>
và <ui:xxx>
các thẻ không mở rộng UIComponent
cũng là các trình xử lý thẻ. " Cố gắng ám chỉ đó <ui:repeat>
cũng là một trình xử lý thẻ vì <ui:xxx>
cũng bao gồm <ui:repeat>
? Điều này có nghĩa <ui:repeat>
là một trong những thành phần <ui:xxx>
mở rộng UIComponent
. Do đó, nó không phải là một trình xử lý thẻ. (Một số trong số chúng có thể không mở rộng UIComponent
. Do đó, chúng là trình xử lý thẻ) Có phải không?
<c:set>
không scope
tạo bí danh biểu thức EL thay vì đặt giá trị được đánh giá trong phạm vi mục tiêu. scope="request"
Thay vào đó, hãy thử đánh giá giá trị (trong thời gian xây dựng chế độ xem thực sự) và đặt nó làm thuộc tính yêu cầu (sẽ không bị "ghi đè" trong quá trình lặp). Dưới vỏ bọc, nó tạo và thiết lập một ValueExpression
đối tượng.
ClassNotFoundException
. Thời gian chạy phụ thuộc của dự án của bạn bị hỏng. Rất có thể bạn đang sử dụng máy chủ không phải JavaEE như Tomcat và bạn đã quên cài đặt JSTL hoặc bạn đã vô tình bao gồm cả JSTL 1.0 và JSTL 1.1+. Bởi vì trong JSTL 1.0, gói là javax.servlet.jstl.core.*
và kể từ JSTL 1.1, điều này đã trở thành javax.servlet.jsp.jstl.core.*
. Có thể tìm thấy manh mối để cài đặt JSTL tại đây: stackoverflow.com/a/4928309
sử dụng
<h:panelGroup rendered="#{lpc.verbose}">
...
</h:panelGroup>
Đối với đầu ra giống như chuyển đổi, bạn có thể sử dụng mặt chuyển đổi từ Tiện ích mở rộng PrimeFaces.