ajax 업데이트 / 렌더링을위한 컴포넌트의 클라이언트 ID를 찾는 방법은 무엇입니까? “bar”에서 참조 된“foo”표현을 가진 구성 요소를 찾을 수 없습니다


140

다음 코드는 PrimeFaces DataGrid + DataTable Tutorials에서 영감을 얻어 <p:tab><p:tabView>거주 <p:layoutUnit>하고 <p:layout>있습니다. 다음은 코드의 내부 부분입니다 ( p:tab구성 요소 에서 시작 ). 바깥 부분은 사소합니다.

<p:tabView id="tabs">
    <p:tab id="search" title="Search">                        
        <h:form id="insTable">
            <p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
                <p:column>
                    <p:commandLink id="select" update="insTable:display" oncomplete="dlg.show()">
                        <f:setPropertyActionListener value="#{lndInstrument}" 
                                        target="#{instrumentBean.selectedInstrument}" />
                        <h:outputText value="#{lndInstrument.name}" />
                    </p:commandLink>                                    
                </p:column>
            </p:dataTable>
            <p:dialog id="dlg" modal="true" widgetVar="dlg">
                <h:panelGrid id="display">
                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

를 클릭하면 <p:commandLink>코드 작동이 중지되고 메시지가 나타납니다.

"tabs : insTable : select"에서 "insTable : display"표현식을 가진 구성 요소를 찾을 수 없습니다.

을 사용하여 동일하게 시도하면 <f:ajax>기본적으로 동일한 메시지를 나타내는 다른 메시지와 함께 실패합니다.

<f:ajax> 알 수없는 ID "insTable : display"를 포함하고 "tabs : insTable : select"구성 요소의 컨텍스트에서 찾을 수 없습니다.

이 문제는 어떻게 발생하며 어떻게 해결할 수 있습니까?

답변:


313

실제 클라이언트 ID에 대한 HTML 출력을 찾습니다

올바른 클라이언트 ID를 찾으려면 생성 된 HTML 출력을 조사해야합니다. 브라우저에서 페이지를 열고 마우스 오른쪽 단추를 클릭하고 소스보기를 수행하십시오 . 관심있는 JSF 구성 요소의 HTML 표시를 찾아 id클라이언트 ID로 사용하십시오. 현재 이름 지정 컨테이너에 따라 절대적 또는 상대적 방식으로 사용할 수 있습니다. 다음 장을 참조하십시오.

참고 :이 같은 반복 인덱스 포함 발생할 경우 :0:, :1:(그것이 반복하는 구성 요소 내부에 있기 때문에), 등, 당신은 특정의 반복 라운드를 업데이트하는 것은 항상 지원되지 않는다는 것을 인식 할 필요가있다. 이에 대한 자세한 내용은 답변 하단을 참조하십시오.

NamingContainer구성 요소를 외우고 항상 고정 ID를 부여하십시오

ajax process / execute / update / render에서 참조하려는 구성 요소가 동일한 NamingContainer상위 내에 있으면 자체 ID를 참조하십시오.

<h:form id="form">
    <p:commandLink update="result"> <!-- OK! -->
    <h:panelGroup id="result" />
</h:form>

그것이 있다면 하지 같은 내부 NamingContainer, 당신은 절대 클라이언트 ID를 사용하여 참조 할 필요가있다. 절대 클라이언트 ID는 NamingContainer구분 기호 문자 (기본적으로)로 시작 :합니다.

<h:form id="form">
    <p:commandLink update="result"> <!-- FAIL! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- OK! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- FAIL! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>
<h:form id="form">
    <p:commandLink update=":otherform:result"> <!-- OK! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>

NamingContainer구성 요소, 예를 들어 있습니다 <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(따라서, 모든 복합 구성 요소) 등, 당신은 생성 된 HTML 출력을 보면 쉽게 인식하고, 자신의 ID는 모든 하위 구성 요소의 생성 된 클라이언트 ID 앞에 추가됩니다. 고정 ID가 없으면 JSF는 자동 생성 ID를 j_idXXX형식으로 사용 합니다. 당신은 그들에게 고정 ID를 부여함으로써 그것을 피해야합니다. OmniFaces는NoAutoGeneratedIdViewHandler 개발 과정이 도움이 될 수 있습니다.

UIComponent문제 의 javadoc을 찾으면 NamingContainer인터페이스를 구현하는지 여부를 확인할 수도 있습니다 . 예를 들어 HtmlForm( UIComponent<h:form>태그)는 구현 NamingContainer을 표시하지만 HtmlPanelGroup( UIComponent<h:panelGroup>태그)는이를 표시하지 않으므로 구현하지 않습니다 NamingContainer. 여기에 모든 표준 구성 요소의 자바 독입니다여기 PrimeFaces의 자바 독이다 .

문제 해결

따라서 귀하의 경우 :

<p:tabView id="tabs"><!-- This is a NamingContainer -->
    <p:tab id="search"><!-- This is NOT a NamingContainer -->
        <h:form id="insTable"><!-- This is a NamingContainer -->
            <p:dialog id="dlg"><!-- This is NOT a NamingContainer -->
                <h:panelGrid id="display">

생성 된 HTML 출력은 <h:panelGrid id="display">다음과 같습니다.

<table id="tabs:insTable:display">

정확히 id클라이언트 ID로 사용하고 다음 :에서 사용하기 위해 접두사를 사용해야합니다 update.

<p:commandLink update=":tabs:insTable:display">

include / tagfile / composite 외부 참조

이 명령 링크가 include / tagfile 내부에 있고 대상이 외부에 있으므로 현재 이름 지정 컨테이너의 이름 지정 컨테이너 상위 ID를 반드시 알 필요는없는 경우 다음 UIComponent#getNamingContainer()과 같이 동적으로 참조 할 수 있습니다 .

<p:commandLink update=":#{component.namingContainer.parent.namingContainer.clientId}:display">

또는이 명령 링크가 복합 컴포넌트 내부에 있고 대상이 외부에있는 경우 :

<p:commandLink update=":#{cc.parent.namingContainer.clientId}:display">

또는 명령 링크와 대상이 모두 동일한 복합 구성 요소 내에있는 경우 :

<p:commandLink update=":#{cc.clientId}:display">

렌더링 / 업데이트 속성에 대한 템플리트에서 상위 이름 지정 컨테이너의 ID 가져 오기도 참조하십시오.

덮개 아래에서 어떻게 작동합니까

이 모두로 지정됩니다 "표현 검색"의 javadoc :UIComponent#findComponent()

검색 식 (A)의 ID 속성에 대해 정확히 일치하는 식별자 (어느 구성 UIComponent이나 의해 연결된 같은 식별자의 일련의 UINamingContainer#getSeparatorChar문자 값. 대체 alogrithms는만큼 사용될 수 있지만 검색 알고리즘 으로서는, 다음하여 동작한다 최종 결과는 같습니다 :

  • UIComponent다음 조건 중 하나가 충족되는 즉시 중지하여 검색의 기초가 되는 것을 식별하십시오 .
    • 검색 표현식이 분리 자 문자 ( "절대"검색 표현식이라고 함)로 시작하면 밑이 UIComponent컴포넌트 트리 의 루트 가됩니다. 선행 분리 문자가 제거되고 나머지 검색 표현식은 아래 설명 된대로 "상대적"검색 표현식으로 취급됩니다.
    • 이 경우 그렇지 UIComponent않은 NamingContainer그것은 기반이 될 것입니다.
    • 그렇지 않으면이 구성 요소의 상위를 검색하십시오. a NamingContainer가 있으면 기본이됩니다.
    • 그렇지 않으면 (아니오 NamingContainer가 있을 경우 ) 루트 UIComponent가 기본이됩니다.
  • 검색 표현식 (이전 단계에서 수정되었을 수 있음)은 이제 기본 컴포넌트의 범위 내에서 일치하는 ID를 가진 컴포넌트 (있는 경우)를 찾는 데 사용되는 "상대적"검색 표현식입니다. 일치는 다음과 같이 수행됩니다.
    • 검색 표현식이 단순 식별자 인 경우이 값은 id 특성과 비교 된 후 기본의 패싯 및 하위를 통해 재귀 적으로 검색됩니다 UIComponent(자손 NamingContainer이 발견되면 자체 패싯 및 하위는 검색되지 않음).
    • 검색 표현식에 구분 문자로 구분 된 둘 이상의 식별자가 포함 된 경우 첫 번째 식별자는 NamingContainer이전 글 머리 기호의 규칙 으로 위치를 찾는 데 사용됩니다 . 그런 다음 나머지 검색 식을 전달 findComponent()하여이 메서드를 NamingContainer호출합니다.

PrimeFaces도 JSF 사양을 준수하지만 RichFaces는 "일부 추가 예외"를 사용 합니다.

"reRender"UIComponent.findComponent()알고리즘 (일부 추가 예외 포함)을 사용하여 컴포넌트 트리에서 컴포넌트를 찾습니다.

이러한 추가 예외는 자세히 설명되어 있지 않지만 상대 구성 요소 ID (예 :로 시작하지 않는 :)는 가장 가까운 상위 컨텍스트 NamingContainer뿐만 아니라 NamingContainer동일한보기의 다른 모든 구성 요소 에서도 검색되는 것으로 알려져 있습니다. 그건 그렇고 비싼 일).

절대 사용하지 마십시오 prependId="false"

그래도 문제가 해결되지 않으면를 사용하지 않는지 확인하십시오 <h:form prependId="false">. 이것은 ajax 제출 및 렌더링 처리 중에 실패합니다. 이 관련 질문을 참조하십시오 : prependId = "false"인 UIForm은 <f : ajax render>를 중단 합니다.

반복 구성 요소의 특정 반복 라운드 참조

반복 구성 요소에서 특정 반복 항목을 다음 <ui:repeat><h:dataTable>같이 참조하는 것은 오랫동안 불가능했습니다 .

<h:form id="form">
    <ui:repeat id="list" value="#{['one','two','three']}" var="item">
        <h:outputText id="item" value="#{item}" /><br/>
    </ui:repeat>

    <h:commandButton value="Update second item">
        <f:ajax render=":form:list:1:item" />
    </h:commandButton>
</h:form>

그러나 Mojarra 2.2.5부터는 <f:ajax>그것을 지원하기 시작했습니다 (단순히 유효성 검사를 중지했습니다. 따라서 더 이상 언급 된 예외에 직면하지 않을 것입니다. 나중에 다른 개선 픽스는 계획되어 있습니다).

현재 MyFaces 2.2.7 및 PrimeFaces 5.2 버전에서는 아직 작동하지 않습니다. 향후 버전에서는 지원이 제공 될 수 있습니다. 한편, 가장 좋은 방법은 반복 구성 요소 자체 또는 HTML과 같이 HTML을 렌더링하지 않는 경우 부모를 업데이트하는 것 <ui:repeat>입니다.

PrimeFaces를 사용할 때 검색 표현식 또는 선택자를 고려하십시오

PrimeFaces 검색 표현식을 사용하면 JSF 컴포넌트 트리 검색 표현식을 통해 컴포넌트를 참조 할 수 있습니다. JSF에는 몇 가지 내장 기능이 있습니다.

  • @this: 현재 구성 요소
  • @form: 부모의 UIForm
  • @all: 전체 문서
  • @none: 아무것도

PrimeFaces는 새로운 키워드 및 복합 표현식 지원을 통해이를 개선했습니다.

  • @parent: 상위 구성 요소
  • @namingcontainer: 부모의 UINamingContainer
  • @widgetVar(name): 주어진 것으로 식별 된 구성 요소 widgetVar

당신은 또한 다음과 같은 복합 표현식에서 해당 키워드를 혼합 할 수 있습니다 @form:@parent, @this:@parent:@parent

PFS (PrimeFaces Selectors)@(.someclass)사용하면 jQuery CSS 선택기 구문을 통해 구성 요소를 참조 할 수 있습니다. 예를 들어 HTML 출력에서 ​​모든 공통 스타일 클래스를 가진 컴포넌트를 참조합니다. "많은"구성 요소를 참조해야하는 경우에 특히 유용합니다. 이것은 대상 구성 요소가 HTML 출력의 모든 클라이언트 ID를 갖도록 요구합니다 (고정 또는 자동 생성, 중요하지 않음). update = "@ (. myClass)"에서와 같이 PrimeFaces 선택기어떻게 작동합니까?를 참조하십시오.


@jack : javadoc : docs.oracle.com/javaee/6/api/javax/faces/component/…를 읽으십시오. JSF 2.0부터 상수 대신에 구성 가능해졌습니다 .
BalusC

아닌가요 SEPARATOR_CHAR 되지? 예를 들어 중첩 된 컴포넌트를 호출하는 방법에 대한 예제를 제공 할 수 있습니까? context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );xhtml 코드도 포함하십시오.
jacktrades

1
감사합니다, prependId="false"내 하루 를 구한 상태에서 양식 내부의 아약스 다시 렌더링에 실패 했습니다.
Gaim

설명에 요약 된 고객 ID 의 정확한 의미는 무엇 입니까? JSF-> "이 구성 요소의 클라이언트 측 ID"에서와 동일합니까? 당신의 일에 대한 + 감사합니다.
Steve Oh

1
@ antonu17 : 답변에 명시된 바와 같이 Mojarra의 f : ajax에서만 지원됩니다.
BalusC

9

우선 : 탭보기 내에 대화 상자를 배치하는 것은 나쁜 습관입니다 ...

그리고 지금 당신의 질문에 :

죄송합니다. 정확히 구현하고 싶은 것을 얻기 위해 시간이 좀 걸렸습니다.

지금 내 웹 앱에서 직접 작동했으며 작동합니다.

p : dialog를`p : tabView 바깥에 배치하기 전에 말했듯이,

처음에 제안한대로 p : dialog를 그대로 두십시오.

<p:dialog modal="true" widgetVar="dlg">
    <h:panelGrid id="display">
        <h:outputText value="Name:" />
        <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
    </h:panelGrid>
</p:dialog>   

그리고 p : commandlink는 다음과 같아야합니다 (업데이트 속성을 변경하는 것입니다)

<p:commandLink update="display" oncomplete="dlg.show()">
    <f:setPropertyActionListener value="#{lndInstrument}" 
        target="#{instrumentBean.selectedInstrument}" />
    <h:outputText value="#{lndInstrument.name}" />
</p:commandLink>  

내 웹 응용 프로그램에서도 동일하게 작동하며 작동하지 않으면 Java Bean 코드에 문제가있는 것 같습니다 ...


나는 바인딩과 faces-config와 다른 것들로 내 대답에 쓴 다른 변화들을 시도해 볼 것을 권한다. 이것은 "정보 : 구성 요소를 찾을 수 없다 ..."
Daniel

두 번째 제안을 다시 시도했지만 여전히 작동하지 않습니다. 대화 상자가 열리지 만 선택한 항목의 데이터가 포함되어 있지 않습니다. 로그에 "보기에서 식별자가"j_idt31 "인 구성 요소를 찾을 수 없습니다"가 표시되고 이보다 더 이상 디버깅 할 수 없습니다.
perissf

5

그것은 탭이 명명 컨테이너이기도하기 때문입니다 ... 업데이트해야 update="Search:insTable:display"할 일은 대화 상자를 양식 외부에 배치하고 탭 내부에 배치하는 것입니다.update="Search:display"


0

변화 시도 update="insTable:display"에를 update="display". 양식 ID로 ID를 접두어로 사용할 수 없다고 생각합니다.


2
아주 오래되었지만 오해의 소지가있는 답변. 위의 BalusC 게시물을 참조하여 구성 요소 ID 접두어를 양식 양식 ID로 명확하게 표시하십시오. <h : form id = "form"> <p : commandLink update = ": otherform : result"> <!-OK! -> </ h : form> <h : form id = "otherform"> <h : panelGroup id = "result"/> </ h : form>
J Slick 2012

0

나는 이것이 BalusC에 의해 이미 훌륭한 대답을 가지고 있음을 알고 있지만 여기에 컨테이너가 올바른 clientId를 알려주는 데 약간의 트릭이 있습니다 .

  1. 작동하지 않는 구성 요소에서 업데이트를 제거하십시오.
  2. 업데이트하려는 구성 요소 내에 가짜 업데이트가 포함 된 임시 구성 요소를 넣습니다.
  3. 페이지를 누르면 서블릿 예외 오류가 참조해야 할 올바른 클라이언트 ID를 알려줍니다.
  4. 가짜 구성 요소를 제거하고 원래 업데이트에 올바른 clientId를 넣습니다.

내 단어가 가장 잘 설명되지 않을 수 있으므로 코드 예제는 다음과 같습니다.

<p:tabView id="tabs">
    <p:tab id="search" title="Search">                        
        <h:form id="insTable">
            <p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
                <p:column>
                    <p:commandLink id="select"

이 구성 요소 내에서 실패한 업데이트 제거

 oncomplete="dlg.show()">
                        <f:setPropertyActionListener value="#{lndInstrument}" 
                                        target="#{instrumentBean.selectedInstrument}" />
                        <h:outputText value="#{lndInstrument.name}" />
                    </p:commandLink>                                    
                </p:column>
            </p:dataTable>
            <p:dialog id="dlg" modal="true" widgetVar="dlg">
                <h:panelGrid id="display">

실패 할 업데이트를 사용하여 업데이트하려는 ID의 구성 요소 내에 구성 요소를 추가하십시오.

   <p:commandButton id="BogusButton" update="BogusUpdate"></p:commandButton>

                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

이 페이지를보고 오류를보십시오. 오류는 다음과 같습니다. javax.servlet.ServletException : tabs : insTable : BogusButton 에서 참조 된 "BogusUpdate"표현식의 컴포넌트를 찾을 수 없습니다 .

따라서 사용할 올바른 clientId는 굵은 체와 대상 컨테이너의 ID입니다 (이 경우 표시).

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