IT story

JSF2 Facelets의 JSTL…

hot-time 2020. 6. 4. 08:13
반응형

JSF2 Facelets의 JSTL…


조건부로 Facelets 코드를 약간 출력하고 싶습니다.

이를 위해 JSTL 태그가 정상적으로 작동하는 것 같습니다.

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

그러나 이것이 최선의 방법인지 확실하지 않습니까? 목표를 달성 할 수있는 다른 방법이 있습니까?


소개

JSTL <c:xxx>태그는 모두 태그 처리기 이며 보기 빌드 시간 동안 실행되는 반면 JSF <h:xxx>태그는 모든 UI 구성 요소 이며 보기 렌더링 시간 동안 실행됩니다 .

참고 JSF 자신의에서 <f:xxx><ui:xxx>않는 그 태그 만 하지 에서 연장 UIComponent도 taghandlers 있습니다 예를 들어 <f:validator>, <ui:include>, <ui:define>, 등에서 확장 것들은 UIComponent또한 예를 들어, JSF UI 구성 요소이다 <f:param>, <ui:fragment>, <ui:repeat>, 등 JSF UI 구성 요소에서 단지 idbinding속성이 있습니다 또한 뷰 빌드 시간 동안 평가되었습니다. 따라서 JSTL 라이프 사이클에 대한 아래 답변 은 JSF 구성 요소의 속성 idbinding속성 에도 적용됩니다 .

뷰 빌드 시간은 XHTML / JSP 파일을 구문 분석하고 저장하는 JSF 컴포넌트 트리로 변환하는 것입니다 순간 UIViewRootFacesContext. 뷰 렌더링 시간은 JSF 컴포넌트 트리가로 시작하여 HTML을 생성하려고하는 순간입니다 UIViewRoot#encodeAll(). 따라서 코딩에서 예상 한대로 JSF UI 구성 요소와 JSTL 태그가 동기화되어 실행되지 않습니다. JSTL은 위에서 아래로 먼저 실행되어 JSF 컴포넌트 트리를 생성 한 다음 JSF가 위에서 아래로 다시 실행되어 HTML 출력을 생성합니다.

<c:forEach> vs <ui:repeat>

예를 들어이 Facelets 마크 업은 <c:forEach>다음을 사용하여 3 개 이상의 항목을 반복합니다 .

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

... 뷰 빌드 시간 동안 <h:outputText>JSF 컴포넌트 트리에서 3 개의 개별 컴포넌트를 생성합니다.

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

... 뷰 렌더링 시간 동안 HTML 출력을 개별적으로 생성합니다.

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

구성 요소 ID의 고유성을 수동으로 확인해야하며보기 빌드 시간 동안 평가됩니다.

이 Facelets 마크 업은 <ui:repeat>JSF UI 구성 요소 인을 사용하여 3 개 이상의 항목을 반복하는 동안 :

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

... 이미 JSF 컴포넌트 트리에서 그대로 종료되며, 현재의 반복 라운드를 기반으로 HTML 출력을 생성하기 위해 <h:outputText>뷰 렌더 시간이 재사용 되는 동안 매우 동일한 컴포넌트가 사용 됩니다.

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

참고 그 <ui:repeat>있는 Being 같은 NamingContainer성분이 이미 반복 인덱스에 기초하여 상기 클라이언트 ID의 고유성을 보장; id뷰 구성 시간 동안도 평가 #{item}되고 뷰 렌더링 시간 동안 만 사용할 수 있으므로 자식 구성 요소의 속성으로 EL을 사용할 수 없습니다. 동일은 마찬가지입니다 h:dataTable와 유사한 구성 요소.

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

또 다른 예로서,이 Facelets의 조건부 사용하여 다른 태그를 추가 마크 업을 <c:if>(당신은 또한 사용할 수있는 <c:choose><c:when><c:otherwise>이를 위해) :

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

... 구성 요소를 JSF 구성 요소 트리 type = TEXT에만 추가하는 경우 <h:inputText>:

<h:inputText ... />

이 Facelets 마크 업 동안 :

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

...will end up exactly as above in the JSF component tree regardless of the condition. This may thus end up in a "bloated" component tree when you have many of them and they are actually based on a "static" model (i.e. the field does not ever change during at least the view scope). Also, you may run into EL trouble when you deal with subclasses with additional properties in Mojarra versions before 2.2.7.

<c:set> vs <ui:param>

They are not interchangeable. The <c:set> sets a variable in the EL scope, which is accessible only after the tag location during view build time, but anywhere in the view during view render time. The <ui:param> passes an EL variable to a Facelet template included via <ui:include>, <ui:decorate template>, or <ui:composition template>. Older JSF versions had bugs whereby the <ui:param> variable is also available outside the Facelet template in question, this should never be relied upon.

The <c:set> without a scope attribute will behave like an alias. It does not cache the result of the EL expression in any scope. It can thus perfectly fine be used inside for example iterating JSF components. Thus, e.g. below will work fine:

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

It's only not suitable for e.g. calculating the sum in a loop. For that instead use 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>

Only, when you set the scope attribute with one of allowable values request, view, session, or application, then it will be evaluated immediately during view build time and stored in the specified scope.

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

This will be evaluated only once and available as #{dev} throughout the entire application.

Use JSTL to control JSF component tree building

Using JSTL may only lead to unexpected results when being used inside JSF iterating components such as <h:dataTable>, <ui:repeat>, etc, or when JSTL tag attributes depend on results of JSF events such as preRenderView or submitted form values in the model which aren't available during view build time. So, use JSTL tags only to control flow of JSF component tree building. Use JSF UI components to control flow of HTML output generation. Do not bind the var of iterating JSF components to JSTL tag attributes. Do not rely on JSF events in JSTL tag attributes.

Anytime you think you need to bind a component to the backing bean via binding, or grab one via findComponent(), and create/manipulate its children using Java code in a backing bean with new SomeComponent() and what not, then you should immediately stop and consider using JSTL instead. As JSTL is also XML based, the code needed to dynamically create JSF components will become so much better readable and maintainable.

Important to know is that Mojarra versions older than 2.1.18 had a bug in partial state saving when referencing a view scoped bean in a JSTL tag attribute. The whole view scoped bean would be newly recreated instead of retrieved from the view tree (simply because the complete view tree isn't available yet at the point JSTL runs). If you're expecting or storing some state in the view scoped bean by a JSTL tag attribute, then it won't return the value you expect, or it will be "lost" in the real view scoped bean which is restored after the view tree is built. In case you can't upgrade to Mojarra 2.1.18 or newer, the work around is to turn off partial state saving in web.xml like below:

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

See also:

To see some real world examples where JSTL tags are helpful (i.e. when really properly used during building the view), see the following questions/answers:


In a nutshell

As to your concrete functional requirement, if you want to render JSF components conditionally, use the rendered attribute on the JSF HTML component instead, particularly if #{lpc} represents the currently iterated item of a JSF iterating component such as <h:dataTable> or <ui:repeat>.

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

Or, if you want to build (create/add) JSF components conditionally, then keep using JSTL. It's way much better than verbosely doing new SomeComponent() in java.

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

See also:


use

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

Sorry for the separate answer, but I couldn't comment answers above.

For switch-like output you can use switch from primefaces-extensions.

참고URL : https://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense

반응형