Làm thế nào để tìm ra ID khách hàng của thành phần để cập nhật / kết xuất ajax? Không thể tìm thấy thành phần với biểu thức


140

Đoạn mã sau được lấy cảm hứng từ Hướng dẫn PrimeFaces DataGrid + DataTable và đưa vào một <p:tab>trong những nơi <p:tabView>cư trú trong <p:layoutUnit>a <p:layout>. Đây là phần bên trong của mã (bắt đầu từ p:tabthành phần); phần bên ngoài là tầm thường.

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

Khi tôi nhấp vào <p:commandLink>, mã sẽ ngừng hoạt động và đưa ra thông báo:

Không thể tìm thấy thành phần có biểu thức "insTable: display" được tham chiếu từ "tab: insTable: select".

Khi tôi thử sử dụng tương tự <f:ajax>, nó thất bại với một thông điệp khác về cơ bản nói giống nhau:

<f:ajax> chứa một id không xác định "insTable: display" không thể định vị nó trong ngữ cảnh của thành phần "tab: insTable: select"

Điều này gây ra như thế nào và làm thế nào tôi có thể giải quyết nó?

Câu trả lời:


313

Tìm trong đầu ra HTML cho ID khách hàng thực tế

Bạn cần xem xét đầu ra HTML đã tạo để tìm ID khách hàng phù hợp. Mở trang trong trình duyệt, nhấp chuột phải và Xem Nguồn . Xác định vị trí đại diện HTML của thành phần JSF quan tâm và lấy nó idlàm ID khách hàng. Bạn có thể sử dụng nó một cách tuyệt đối hoặc tương đối tùy thuộc vào vùng chứa tên hiện tại. Xem chương sau.

Lưu ý: nếu nó có chứa chỉ số lặp như :0:, :1:v.v. (vì nó nằm trong thành phần lặp), thì bạn cần nhận ra rằng việc cập nhật một vòng lặp cụ thể không phải lúc nào cũng được hỗ trợ. Xem dưới cùng của câu trả lời để biết thêm chi tiết về điều đó.

Ghi nhớ NamingContainercác thành phần và luôn cung cấp cho chúng một ID cố định

Nếu một thành phần mà bạn muốn tham chiếu theo quy trình ajax / thực thi / cập nhật / kết xuất nằm trong cùng một NamingContainercha mẹ, thì chỉ cần tham chiếu ID của chính nó.

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

Nếu nó không giống nhau NamingContainer, thì bạn cần tham chiếu nó bằng ID khách hàng tuyệt đối. ID khách hàng tuyệt đối bắt đầu bằng NamingContainerký tự phân cách, theo mặc định :.

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

NamingContainerthành phần là ví dụ <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(do đó, tất cả các thành phần composite), vv Bạn có nhận ra chúng một cách dễ dàng bằng cách nhìn vào đầu ra HTML được tạo ra, ID của họ sẽ được thêm vào phía trước ID khách hàng tạo của tất cả các thành phần con. Lưu ý rằng khi họ không có ID cố định, thì JSF sẽ sử dụng ID được tạo tự động theo j_idXXXđịnh dạng. Bạn tuyệt đối nên tránh điều đó bằng cách cung cấp cho họ một ID cố định. Các OmniFacesNoAutoGeneratedIdViewHandler có thể hữu ích trong việc này trong thời gian phát triển.

Nếu bạn biết để tìm javadoc của UIComponentcâu hỏi, thì bạn cũng có thể kiểm tra xem nó có thực hiện NamingContainergiao diện hay không. Ví dụ: HtmlForm( thẻ UIComponentphía sau <h:form>) hiển thị nó thực hiện NamingContainer, nhưng HtmlPanelGroup( thẻ UIComponentphía sau <h:panelGroup>) không hiển thị, vì vậy nó không thực hiện NamingContainer. Đây là javadoc của tất cả các thành phần tiêu chuẩnđây là javadoc của PrimeFaces .

Giải quyết vấn đề của bạn

Vì vậy, trong trường hợp của bạn về:

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

Đầu ra HTML được tạo ra <h:panelGrid id="display">trông như thế này:

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

Bạn cần lấy chính xác đó idlà ID khách hàng và sau đó là tiền tố :để sử dụng trong update:

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

Tham chiếu bên ngoài bao gồm / tagfile / composite

Nếu liên kết lệnh này nằm trong tệp bao gồm / tagfile và mục tiêu nằm ngoài nó, và do đó bạn không nhất thiết phải biết ID của bộ chứa tên đặt tên của bộ chứa đặt tên hiện tại, thì bạn có thể tự động tham chiếu nó qua UIComponent#getNamingContainer()như vậy:

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

Hoặc, nếu liên kết lệnh này nằm trong một thành phần hỗn hợp và mục tiêu nằm ngoài nó:

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

Hoặc, nếu cả liên kết lệnh và đích nằm trong cùng một thành phần hỗn hợp:

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

Xem thêm Nhận id của thùng chứa đặt tên cha mẹ trong mẫu cho thuộc tính kết xuất / cập nhật

Làm thế nào nó hoạt động dưới vỏ bọc

Điều này tất cả được xác định là "tìm kiếm cụm từ" trong các UIComponent#findComponent()javadoc :

Một biểu hiện tìm kiếm bao gồm hoặc là một định danh (mà là lần xuất hiện chính xác đối với các tài sản id của một UIComponent, hoặc một loạt các mã nhận dạng đó được liên kết bởi các UINamingContainer#getSeparatorChargiá trị nhân vật. Các thuật toán tìm kiếm nên hoạt động như sau, mặc dù alogrithms thay thế có thể được sử dụng miễn là kết quả cuối cùng là như nhau:

  • Xác định UIComponentđó sẽ là cơ sở để tìm kiếm, bằng cách dừng ngay khi một trong các điều kiện sau được đáp ứng:
    • Nếu biểu thức tìm kiếm bắt đầu bằng ký tự dấu tách (được gọi là biểu thức tìm kiếm "tuyệt đối"), cơ sở sẽ là gốc UIComponentcủa cây thành phần. Ký tự phân cách hàng đầu sẽ bị loại bỏ và phần còn lại của biểu thức tìm kiếm sẽ được coi là biểu thức tìm kiếm "tương đối" như được mô tả bên dưới.
    • Mặt khác, nếu đây UIComponentlà một NamingContainernó sẽ phục vụ như là cơ sở.
    • Nếu không, tìm kiếm cha mẹ của thành phần này. Nếu a NamingContainergặp phải, nó sẽ là cơ sở.
    • Mặt khác (nếu không NamingContainergặp phải) gốc UIComponentsẽ là cơ sở.
  • Biểu thức tìm kiếm (có thể được sửa đổi trong bước trước) bây giờ là biểu thức tìm kiếm "tương đối" sẽ được sử dụng để định vị thành phần (nếu có) có id khớp với, trong phạm vi của thành phần cơ sở. Trận đấu được thực hiện như sau:
    • Nếu biểu thức tìm kiếm là một định danh đơn giản, giá trị này được so sánh với thuộc tính id và sau đó đệ quy thông qua các khía cạnh và con của cơ sở UIComponent(ngoại trừ nếu NamingContainertìm thấy hậu duệ , các khía cạnh và con riêng của nó không được tìm kiếm).
    • Nếu biểu thức tìm kiếm bao gồm nhiều hơn một mã định danh được phân tách bằng ký tự phân tách, mã định danh đầu tiên được sử dụng để xác định vị trí NamingContainertheo quy tắc trong điểm đầu dòng trước đó. Sau đó, findComponent()phương thức này NamingContainersẽ được gọi, chuyển phần còn lại của biểu thức tìm kiếm.

Lưu ý rằng PrimeFaces cũng tuân thủ thông số JSF, nhưng RichFaces sử dụng "một số ngoại lệ bổ sung" .

"reRender" sử dụng UIComponent.findComponent()thuật toán (với một số ngoại lệ bổ sung) để tìm thành phần trong cây thành phần.

Các ngoại lệ bổ sung đó không được mô tả chi tiết, nhưng được biết rằng ID thành phần tương đối (nghĩa là các ID không bắt đầu :) không chỉ được tìm kiếm trong ngữ cảnh của cha mẹ gần nhất NamingContainer, mà còn trong tất cả các NamingContainerthành phần khác trong cùng một chế độ xem (tương đối Nhân tiện công việc đắt tiền).

Không bao giờ sử dụng prependId="false"

Nếu tất cả điều này vẫn không hoạt động, sau đó xác minh nếu bạn không sử dụng <h:form prependId="false">. Điều này sẽ thất bại trong quá trình xử lý ajax gửi và kết xuất. Xem thêm câu hỏi liên quan này: UIForm với preendId = "false" break <f: ajax render> .

Tham khảo vòng lặp cụ thể của các thành phần lặp

Đã từ lâu không thể tham chiếu một mục lặp cụ thể trong các thành phần lặp như thế <ui:repeat><h:dataTable>như vậy:

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

Tuy nhiên, vì Mojarra 2.2.5 đã <f:ajax>bắt đầu hỗ trợ nó (đơn giản là nó đã dừng xác nhận nó; do đó bạn sẽ không bao giờ phải đối mặt với ngoại lệ trong câu hỏi được đề cập nữa; một sửa chữa nâng cao khác được lên kế hoạch cho lần sau).

Điều này chỉ không hoạt động trong các phiên bản MyFaces 2.2.7 và PrimeFaces 5.2 hiện tại. Sự hỗ trợ có thể đến trong các phiên bản trong tương lai. Trong khi đó, cách tốt nhất của bạn là cập nhật chính thành phần lặp hoặc cha mẹ trong trường hợp nó không hiển thị HTML, như thế nào <ui:repeat>.

Khi sử dụng PrimeFaces, hãy xem xét Biểu thức tìm kiếm hoặc Bộ chọn

Biểu thức tìm kiếm PrimeFaces cho phép bạn tham chiếu các thành phần thông qua các biểu thức tìm kiếm cây thành phần JSF. JSF có một số nội dung:

  • @this: thành phần hiện tại
  • @form: cha mẹ UIForm
  • @all: toàn bộ tài liệu
  • @none: không có gì

PrimeFaces đã tăng cường điều này với các từ khóa mới và hỗ trợ biểu thức tổng hợp:

  • @parent: thành phần cha mẹ
  • @namingcontainer: cha mẹ UINamingContainer
  • @widgetVar(name): thành phần như được xác định bởi đã cho widgetVar

Bạn cũng có thể kết hợp các từ khóa trong các biểu thức tổng hợp như @form:@parent, @this:@parent:@parentvv

PrimeFaces Selector (PFS) như trong @(.someclass)cho phép bạn tham chiếu các thành phần thông qua cú pháp bộ chọn CSS jQuery. Ví dụ: các thành phần tham chiếu có tất cả một lớp kiểu chung trong đầu ra HTML. Điều này đặc biệt hữu ích trong trường hợp bạn cần tham khảo "rất nhiều" thành phần. Điều này chỉ yêu cầu các thành phần đích có tất cả ID khách trong đầu ra HTML (cố định hoặc tự động, không thành vấn đề). Xem thêm Làm thế nào để các bộ chọn PrimeFaces như trong update = "@ (. MyClass)" hoạt động?


@jack: Chỉ cần đọc javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/ mẹo Kể từ khi JSF 2.0, nó đã trở thành cấu hình thay vì hằng số.
BalusC

Không phải SEPARATOR_CHAR không được chấp nhận? Bạn có thể đưa ra một ví dụ về cách gọi một thành phần lồng nhau, ví dụ: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Ngoài ra, vui lòng bao gồm mã xhtml.
jacktrades

1
Cảm ơn bạn, lưu ý về việc thất bại trong việc đăng ký lại ajax trong mẫu prependId="false"đã lưu lại ngày của tôi.
Gaim

ý nghĩa chính xác của ID khách hàng như được nêu trong phần giải thích của bạn là gì? Có giống như trong JSF -> "Mã định danh phía máy khách cho thành phần này". Trân trọng + cảm ơn cho công việc của bạn.
Steve Oh

1
@ antonu17: Như đã nêu trong câu trả lời, nó chỉ được hỗ trợ trong f: ajax của Mojarra.
BalusC

9

trước hết: theo như tôi biết, việc đặt hộp thoại bên trong tabview là một cách làm tồi tệ ... tốt nhất bạn nên lấy nó ra ...

và bây giờ cho câu hỏi của bạn:

xin lỗi, tôi đã mất một thời gian để có được chính xác những gì bạn muốn thực hiện,

đã làm tại ứng dụng web của tôi ngay bây giờ, và nó hoạt động

như tôi đã nói trước khi đặt hộp thoại p: ra bên cạnh `p: tabView,

để lại hộp thoại p: như bạn đề xuất ban đầu:

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

và p: Commandlink sẽ trông như thế này (tất cả những gì tôi đã làm là thay đổi thuộc tính cập nhật)

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

ứng dụng tương tự trong ứng dụng web của tôi và nếu nó không hoạt động với bạn, thì tôi đoán có lỗi gì đó trong mã đậu java của bạn ...


Tôi khuyên bạn nên thử các thay đổi khác mà tôi đã viết trong câu trả lời của mình (với ràng buộc và khuôn mặt-cấu hình và khác ...) giả sử này để giải quyết "INFO: Không thể tìm thấy compone ..."
Daniel

Tôi đã cố gắng thực hiện đề xuất thứ 2 của bạn, nhưng nó vẫn không hoạt động. Hộp thoại mở nhưng không chứa dữ liệu của mục đã chọn. Nhật ký hiển thị "Không thể tìm thấy thành phần có mã định danh" j_idt31 "trong chế độ xem" và tôi không thể gỡ lỗi nhiều hơn thế này.
perissf

5

Đó là vì tab là một thùng chứa đặt tên cũng như ... bản cập nhật của bạn phải là update="Search:insTable:display"những gì bạn có thể làm cũng chỉ là đặt hộp thoại của bạn bên ngoài biểu mẫu và vẫn bên trong tab thì nó sẽ là:update="Search:display"


0

Hãy thử thay đổi update="insTable:display"thành update="display". Tôi tin rằng bạn không thể thêm tiền tố vào id với ID mẫu như thế.


2
Rất cũ nhưng câu trả lời sai lệch. Tham khảo bài đăng của BalusC ở trên, hiển thị rõ ràng tiền tố của ID của thành phần với ID của biểu mẫu kèm theo: <h: form id = "form"> <p: commandLink update = ": otherform: result"> <! - OK! -> </ h: form> <h: form id = "otherform"> <h: panelgroup id = "result" /> </ h: form>
J Slick

0

Tôi biết điều này đã có một câu trả lời tuyệt vời của BalusC nhưng đây là một mẹo nhỏ tôi sử dụng để lấy container để cho tôi biết clientId chính xác .

  1. Xóa bản cập nhật trên thành phần của bạn không hoạt động
  2. Đặt một thành phần tạm thời với bản cập nhật không có thật trong thành phần bạn đang cố cập nhật
  3. nhấn trang, lỗi ngoại lệ servlet sẽ cho bạn biết Id khách hàng chính xác mà bạn cần tham khảo.
  4. Xóa thành phần không có thật và đặt clientId chính xác trong bản cập nhật gốc

Đây là ví dụ mã vì những từ của tôi có thể không mô tả nó tốt nhất.

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

Xóa bản cập nhật không thành công trong thành phần này

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

Thêm một thành phần trong thành phần của id mà bạn đang cố cập nhật bằng một bản cập nhật sẽ thất bại

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

Nhấn trang này và xem lỗi. Lỗi là: javax.servlet.ServletException: Không thể tìm thấy thành phần cho biểu thức "BogusUpdate" được tham chiếu từ các tab: insTable: BogusButton

Vì vậy, clientId chính xác để sử dụng sau đó sẽ được in đậm cộng với id của vùng chứa đích (hiển thị trong trường hợp này)

tabs:insTable:display
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.