JSF2 Facelets의 JSTL…


163

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

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

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

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

답변:


320

소개

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

... 조건에 관계없이 JSF 컴포넌트 트리에서 정확하게 위와 같이 끝납니다. 따라서 구성 요소 트리가 많고 실제로는 "정적"모델을 기반으로 할 때 "부풀린"구성 요소 트리가 될 수 있습니다 (예 : field뷰 범위 동안 변경되지 않음). 또한 2.2.7 이전의 Mojarra 버전에서 추가 속성이있는 서브 클래스를 처리 할 때 EL 문제가 발생할 수 있습니다 .

<c:set> vs <ui:param>

그들은 서로 바꿔 쓸 수 없습니다. 이 뷰 <c:set>는 EL 빌드 에서 변수를 설정합니다.이 변수는 뷰 빌드 시간 동안 태그 위치 이후 에만 볼 수 있지만 뷰 렌더링 시간 동안 뷰의 어느 곳에서나 액세스 할 수 있습니다 . 는 <ui:param>Facelet 템플릿에 EL 변수로 포함 전달 <ui:include>, <ui:decorate template>또는 <ui:composition template>. 이전 JSF 버전에는 <ui:param>문제가있는 Facelet 템플릿 외부에서도 변수를 사용할 수 있는 버그가 있었으므로 절대 의존해서는 안됩니다.

<c:set>없는 scope속성은 별명처럼 동작합니다. 모든 범위에서 EL 표현식의 결과를 캐시하지 않습니다. 따라서 JSF 구성 요소를 반복하는 것과 같이 내부에서 완벽하게 사용할 수 있습니다. 따라서 아래 예를 들어 제대로 작동합니다.

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

루프에서 합계를 계산하는 것에 만 적합하지 않습니다. 대신 EL 3.0 스트림을 사용하십시오 .

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

당신이 설정 한 경우에만, scope허용되는 값 중 하나를 사용하여 속성을 request, view, session, 또는 application, 그것은보기 빌드 시간 동안 즉시 평가 및 지정된 범위에 저장됩니다.

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

이것은 한 번만 평가되며 #{dev}전체 응용 프로그램에서 사용할 수 있습니다 .

JSTL을 사용하여 JSF 컴포넌트 트리 빌드 제어

같은 JSF 반복하는 구성 요소 내에서 사용되는 경우 JSTL을 사용하면 단지 예기치 않은 결과가 발생할 수 있습니다 <h:dataTable>, <ui:repeat>등, 또는 JSTL 태그 속성은 다음과 같은 JSF 이벤트의 결과에 의존 할 때 preRenderView보기 빌드 시간 동안 사용할 수없는 모델의 양식 값 또는 제출 . 따라서 JSTL 구성 요소 트리 빌드의 흐름을 제어하기 위해 JSTL 태그 만 사용하십시오. JSF UI 구성 요소를 사용하여 HTML 출력 생성의 흐름을 제어하십시오. var반복 JSF 구성 요소를 JSTL 태그 속성에 바인드하지 마십시오 . JSTL 태그 속성에서 JSF 이벤트에 의존하지 마십시오.

언제 당신은 당신이를 통해 백업 빈에 구성 요소 바인딩 할 필요가 있다고 생각 binding, 또는를 통해 잡아 하나를 findComponent()배킹와 콩에 자바 코드를 사용하여 어린이를 만들고 / 조작 new SomeComponent()무엇을, 당신은 즉시 중지해야하지 대신 JSTL을 사용하는 것이 좋습니다. JSTL도 XML 기반이므로 JSF 구성 요소를 동적으로 만드는 데 필요한 코드는 훨씬 더 읽기 쉽고 유지 관리가 쉬워집니다.

2.1.18 이전의 Mojarra 버전은 JSTL 태그 속성에서 뷰 범위 Bean을 참조 할 때 부분 상태 저장에 버그가 있음을 알아야합니다. JSTL이 실행되는 시점에 전체 뷰 트리를 사용할 수 없기 때문에 전체 뷰 범위 Bean이 뷰 트리에서 검색되지 않고 새로 재생성됩니다. JSTL 태그 속성에 의해 뷰 범위 Bean에서 일부 상태를 예상하거나 저장하는 경우 예상 값을 리턴하지 않거나 실제 뷰 범위 Bean에서 "손실"되어 뷰 후에 복원됩니다. 나무가 지어졌습니다. Mojarra 2.1.18 이상으로 업그레이드 할 수없는 경우 해결 방법은 다음 web.xml과 같이 부분 상태 저장을 끄는 것입니다.

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

또한보십시오:

JSTL 태그가 도움이되는 실제 예제를 보려면 (예 :보기를 빌드하는 동안 실제로 올바르게 사용되는 경우) 다음 질문 / 답변을 참조하십시오.


간단히 말해서

구체적인 기능 요구 사항과 관련하여 조건부로 JSF 구성 요소 를 렌더링 하려는 경우 특히 또는 과 같은 JSF 반복 구성 요소의 현재 반복되는 항목을 나타내는 경우 renderedJSF HTML 구성 요소 의 특성을 대신 사용하십시오 .#{lpc}<h:dataTable><ui:repeat>

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

또는 조건부로 JSF 구성 요소 를 빌드 (작성 / 추가) 하려는 경우 JSTL을 계속 사용하십시오. new SomeComponent()자바에서 장황하게하는 것보다 훨씬 낫습니다 .

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

또한보십시오:


3
@Aklin : 아니요? 방법에 대한 ?
BalusC

1
첫 번째 단락 을 오랫동안 제대로 해석 할 수 없습니다 (주어진 예제는 매우 명확합니다). 따라서이 의견을 유일한 방법으로 남겨두고 있습니다. 그 단락에 의해, 나는 <ui:repeat>태그 핸들러 인 인상에 있습니다 (이 줄 때문에 " JSF 자신 <f:xxx><ui:xxx>... "에주의하십시오). <c:forEach>따라서 뷰 빌드 시간 에서 평가됩니다 (다시와 마찬가지로 <c:forEach>) . 그렇다면, <ui:repeat><c:forEach>? 사이에 눈에 보이는 기능적 차이가 없어야합니다 . 나는 그 단락이 정확히 무엇을 의미하는지 이해하지 못한다 :)
Tiny

1
죄송합니다.이 게시물을 더 이상 오염시키지 않습니다. 나는 내 관심에 이전 코멘트를했다하지만이 문장 "않습니다 참고 JSF의 소유 <f:xxx><ui:xxx>확장되지 않는 태그 UIComponent도 태그 핸들러입니다. "그 암시하려는 시도 <ui:repeat>도 있기 때문에이 태그 핸들러 <ui:xxx>도 포함을 <ui:repeat>? 이것은 그것이 확장 <ui:repeat>하는 구성 요소 중 하나임 을 의미해야합니다 . 따라서 태그 처리기가 아닙니다. (일부는 확장되지 않을 수 있습니다. 따라서 태그 처리기입니다)? <ui:xxx>UIComponentUIComponent
Tiny

2
@Shirgill : 대상 범위에서 평가 된 값을 설정하는 대신 EL 표현식의 별명을 작성 <c:set>하지 않습니다 scope. 시도 scope="request"즉시 (참보기 빌드 시간 동안) 값을 평가하는 것이다, 대신과 (반복 중 "덮어 쓰기"하지 않을 것이다) 요청 속성으로 설정합니다. 표지 아래에서 ValueExpression개체를 만들고 설정 합니다.
BalusC

1
@ K.Nicholas : 커버 아래에 있습니다 ClassNotFoundException. 프로젝트의 런타임 종속성이 손상되었습니다. Tomcat과 같은 JavaEE 이외의 서버를 사용 중이거나 JSTL 설치를 잊었거나 실수로 JSTL 1.0과 JSTL 1.1 이상을 모두 포함했을 것입니다. JSTL 1.0에서 패키지는 javax.servlet.jstl.core.*JSTL 1.1 이후로 바뀌 었습니다 javax.servlet.jsp.jstl.core.*. JSTL 설치에 대한 힌트는 여기에서 찾을 수 있습니다 : stackoverflow.com/a/4928309
BalusC

13

사용하다

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

고마워요. 더 일반적으로 : JSTL 태그는 여전히 타당합니까, 아니면 JSF 2.0 이후로 더 이상 사용되지 않는 것으로 간주해야합니까?
Jan

대부분의 경우 그렇습니다. 그러나 때때로 그것들을 사용하는 것이 적절합니다
Bozho

3
h : panelGroup을 사용하면 <span> 태그가 생성되고 c : if는 html 코드에 아무것도 추가하지 않으므로 더티 솔루션입니다. h : panelGroup은 요소를 그룹화하기 때문에 panelGrids에서도 문제가 있습니다.
Rober2D2

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.