Bagaimana cara mengetahui ID klien komponen untuk update / render ajax? Tidak dapat menemukan komponen dengan ekspresi "foo" yang dirujuk dari "bar"

143

Kode berikut ini terinspirasi dari PrimeFaces DataGrid + Tutorial DataTable dan dimasukkan ke dalam <p:tab>file yang <p:tabView>berada di <p:layoutUnit>a <p:layout>. Berikut adalah bagian dalam kode (mulai dari p:tabkomponen); bagian luarnya sepele.

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

Ketika saya mengklik <p:commandLink>, kode berhenti bekerja dan memberikan pesan:

Tidak dapat menemukan komponen dengan ekspresi "insTable: display" direferensikan dari "tab: insTable: select".

Ketika saya mencoba penggunaan yang sama <f:ajax>, gagal dengan pesan berbeda yang pada dasarnya mengatakan hal yang sama:

<f:ajax> berisi id yang tidak diketahui "insTable: display" tidak dapat menemukannya dalam konteks komponen "tabs: insTable: select"

Bagaimana ini disebabkan dan bagaimana saya bisa mengatasinya?

perissf
sumber

Jawaban:

318

Lihat keluaran HTML untuk ID klien yang sebenarnya

Anda perlu melihat output HTML yang dihasilkan untuk mengetahui ID klien yang tepat. Buka halaman di browser, lakukan klik kanan dan View Source . Temukan representasi HTML dari komponen JSF yang diinginkan dan ambilid sebagai ID klien. Anda dapat menggunakannya dengan cara absolut atau relatif bergantung pada wadah penamaan saat ini. Lihat bab berikut.

Catatan: jika kebetulan mengandung indeks iterasi seperti :0:,, :1:dll (karena berada di dalam komponen iterasi), maka Anda perlu menyadari bahwa memperbarui putaran iterasi tertentu tidak selalu didukung. Lihat bagian bawah jawaban untuk detail lebih lanjut tentang itu.

Hafalkan NamingContainerkomponen dan selalu beri mereka ID tetap

Jika sebuah komponen yang ingin Anda referensikan dengan ajax process / execution / update / render berada di dalam NamingContainerinduk yang sama , maka cukup referensikan ID-nya sendiri.

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

Jika tidak ada di dalamnya NamingContainer, Anda perlu mereferensikannya menggunakan ID klien absolut. ID klien absolut dimulai dengan NamingContainerkarakter pemisah, yang secara default :.

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

NamingContainerkomponen misalnya <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(dengan demikian, semua komponen komposit), dll Anda mengenali mereka dengan mudah dengan melihat hasil HTML yang dihasilkan, ID mereka akan ditambahkan di ID klien yang dihasilkan dari semua komponen anak. Perhatikan bahwa jika mereka tidak memiliki ID tetap, JSF akan menggunakan ID yang dibuat secara otomatis dalam j_idXXXformat. Anda benar-benar harus menghindarinya dengan memberi mereka ID tetap. The OmniFacesNoAutoGeneratedIdViewHandler dapat membantu dalam ini selama pengembangan.

Jika Anda mengetahui untuk menemukan javadoc dari yang UIComponentdimaksud, maka Anda juga dapat memeriksa di sana apakah itu mengimplementasikan NamingContainerantarmuka atau tidak. Misalnya, HtmlForm( tag UIComponentbelakang <h:form>) menunjukkan penerapannya NamingContainer, tetapi HtmlPanelGroup( tag UIComponentbelakang <h:panelGroup>) tidak menampilkannya, jadi tidak diterapkan NamingContainer. Berikut adalah javadoc dari semua komponen standar dan ini adalah javadoc dari PrimeFaces .

Memecahkan masalah Anda

Jadi dalam kasus Anda:

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

Output HTML yang dihasilkan <h:panelGrid id="display">terlihat seperti ini:

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

Anda harus menganggap itu idsebagai ID klien dan kemudian mengawali dengan :untuk penggunaan di update:

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

Mereferensikan di luar include / tagfile / composite

Jika tautan perintah ini ada di dalam include / tagfile, dan target berada di luarnya, dan dengan demikian Anda tidak perlu mengetahui ID dari induk penamaan penampung dari penampung penamaan saat ini, Anda dapat merujuknya secara dinamis melalui UIComponent#getNamingContainer()seperti ini:

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

Atau, jika tautan perintah ini ada di dalam komponen komposit dan target berada di luarnya:

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

Atau, jika tautan perintah dan target berada di dalam komponen komposit yang sama:

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

Lihat juga Mendapatkan id wadah penamaan induk dalam template untuk atribut render / update

Bagaimana cara kerjanya di bawah selimut

Ini semua ditentukan sebagai "mencari ekspresi" di dalam UIComponent#findComponent()javadoc :

Sebuah ekspresi pencarian terdiri dari baik identifier (yang cocok persis terhadap properti id dari UIComponent, atau serangkaian pengenal tersebut terkait dengan UINamingContainer#getSeparatorCharnilai karakter. Algoritma pencarian harus beroperasi sebagai berikut, meskipun alogrithms alternatif dapat digunakan selama hasil akhirnya sama:

  • Identifikasi UIComponentyang akan menjadi basis pencarian, dengan berhenti segera setelah salah satu kondisi berikut terpenuhi:
    • Jika ekspresi pencarian dimulai dengan karakter pemisah (disebut ekspresi pencarian "absolut"), basisnya akan menjadi akar UIComponentdari pohon komponen. Karakter pemisah utama akan dihilangkan, dan sisa ekspresi penelusuran akan diperlakukan sebagai ekspresi penelusuran "relatif" seperti yang dijelaskan di bawah ini.
    • Kalau tidak, jika ini UIComponentadalah NamingContaineritu akan menjadi dasar.
    • Jika tidak, cari orang tua komponen ini. Jika NamingContainerditemui, itu akan menjadi basis.
    • Jika tidak (jika tidak NamingContainerditemukan) root UIComponentakan menjadi basis.
  • Ekspresi pencarian (mungkin dimodifikasi pada langkah sebelumnya) sekarang menjadi ekspresi pencarian "relatif" yang akan digunakan untuk menemukan komponen (jika ada) yang memiliki id yang cocok, dalam lingkup komponen dasar. Pertandingan dilakukan sebagai berikut:
    • Jika ekspresi penelusuran adalah pengenal sederhana, nilai ini dibandingkan dengan properti id, lalu secara rekursif melalui faset dan UIComponentturunan basis (kecuali jika turunan NamingContainerditemukan, faset dan turunannya sendiri tidak akan dicari).
    • Jika ekspresi penelusuran menyertakan lebih dari satu pengenal yang dipisahkan oleh karakter pemisah, pengenal pertama digunakan untuk menemukan a NamingContainermenurut aturan di poin sebelumnya. Kemudian, findComponent()metode ini NamingContainerakan dipanggil, meneruskan sisa ekspresi pencarian.

Perhatikan bahwa PrimeFaces juga mengikuti spesifikasi JSF, tetapi RichFaces menggunakan "beberapa pengecualian tambahan" .

"reRender" menggunakan UIComponent.findComponent()algoritme (dengan beberapa pengecualian tambahan) untuk menemukan komponen di pohon komponen.

Pengecualian tambahan tersebut tidak dijelaskan secara detail, tetapi diketahui bahwa ID komponen relatif (yaitu yang tidak dimulai dengan :) tidak hanya dicari dalam konteks induk terdekat NamingContainer, tetapi juga di semua NamingContainerkomponen lain dalam tampilan yang sama (yang relatif omong-omong, pekerjaan mahal).

Jangan pernah gunakan prependId="false"

Jika ini semua masih tidak berhasil, verifikasi jika Anda tidak menggunakan <h:form prependId="false">. Ini akan gagal selama pemrosesan ajax submit dan render. Lihat juga pertanyaan terkait ini: UIForm dengan prependId = "false" break <f: ajax render> .

Mengacu pada putaran iterasi spesifik dari komponen iterasi

Sudah lama tidak mungkin untuk mereferensikan item iterasi tertentu dalam komponen iterasi seperti <ui:repeat>dan <h:dataTable>seperti ini:

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

Namun, sejak Mojarra 2.2.5 <f:ajax>mulai mendukungnya (itu hanya berhenti memvalidasinya; sehingga Anda tidak akan pernah menghadapi pengecualian yang disebutkan dalam pertanyaan lagi; perbaikan peningkatan lain direncanakan untuk itu nanti).

Ini hanya tidak berfungsi di versi MyFaces 2.2.7 dan PrimeFaces 5.2 saat ini. Dukungan mungkin datang dalam versi yang akan datang. Sementara itu, taruhan terbaik Anda adalah memperbarui komponen iterasi itu sendiri, atau induk jika tidak merender HTML, seperti <ui:repeat>.

Saat menggunakan PrimeFaces, pertimbangkan Ekspresi atau Pemilih Pencarian

Ekspresi Pencarian PrimeFaces memungkinkan Anda mereferensikan komponen melalui ekspresi pencarian pohon komponen JSF. JSF memiliki beberapa bawaan:

  • @this: komponen saat ini
  • @form: orang tua UIForm
  • @all: seluruh dokumen
  • @none: tidak ada

PrimeFaces telah menyempurnakan ini dengan kata kunci baru dan dukungan ekspresi komposit:

  • @parent: komponen induk
  • @namingcontainer: orang tua UINamingContainer
  • @widgetVar(name): komponen seperti yang diidentifikasi oleh yang diberikan widgetVar

Anda juga dapat mencampur kata kunci tersebut dalam ekspresi komposit seperti @form:@parent, @this:@parent:@parent, dll

PrimeFaces Selectors (PFS) seperti di @(.someclass)memungkinkan Anda untuk mereferensikan komponen melalui sintaks pemilih CSS jQuery. Misalnya mereferensikan komponen yang memiliki semua kelas gaya umum dalam keluaran HTML. Ini sangat membantu jika Anda perlu merujuk "banyak" komponen. Ini hanya prasyarat bahwa komponen target memiliki semua ID klien dalam keluaran HTML (tetap atau dibuat otomatis, tidak masalah). Lihat juga Bagaimana PrimeFaces Selectors seperti di update = "@ (. MyClass)" bekerja?

BalusC
sumber
@jack: Baca saja javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… Sejak JSF 2.0, JSF 2.0 menjadi dapat dikonfigurasi alih-alih konstanta.
BalusC
Bukankah SEPARATOR_CHAR sudah usang? Dapatkah Anda memberi contoh tentang cara memanggil komponen bersarang, misalnya: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Sertakan juga kode xhtml.
jacktrades
1
Terima kasih, catatan tentang kegagalan ajax rerender di dalam formulir dengan prependId="false"menyelamatkan hari saya.
Gaim
apa arti sebenarnya dari ID klien seperti yang diuraikan dalam penjelasan Anda? Apakah sama seperti di JSF -> "Pengidentifikasi sisi klien untuk komponen ini". salam + terima kasih atas pekerjaan Anda.
Steve Oh
1
@ antonu17: Seperti yang dinyatakan dalam jawaban, itu hanya didukung di f: ajax Mojarra.
BalusC
9

pertama-tama: sejauh yang saya tahu menempatkan dialog di dalam tabview adalah praktik yang buruk ... lebih baik Anda keluarkan ...

dan sekarang untuk pertanyaan Anda:

maaf, luangkan waktu untuk mendapatkan apa yang sebenarnya ingin Anda terapkan,

lakukan di aplikasi web saya sendiri sekarang, dan berhasil

seperti yang saya katakan sebelumnya, tempatkan p: dialog di luar `p: tabView,

tinggalkan dialog p: seperti yang Anda sarankan pada awalnya:

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

dan tautan perintah p: akan terlihat seperti ini (yang saya lakukan hanyalah mengubah atribut pembaruan)

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

hal yang sama berfungsi di aplikasi web saya, dan jika tidak berhasil untuk Anda, maka saya kira ada yang salah dalam kode kacang java Anda ...

Daniel
sumber
Saya sarankan Anda untuk mencoba perubahan lain yang saya tulis dalam jawaban saya (dengan binding dan face-config dan lainnya ...) ini seharusnya untuk memecahkan "INFO: Tidak dapat menemukan compone ..."
Daniel
Saya telah mencoba lagi untuk menerapkan saran kedua Anda, tetapi masih tidak berhasil. Dialog terbuka tetapi tidak berisi data item yang dipilih. Log menampilkan "Tidak dapat menemukan komponen dengan pengenal" j_idt31 "dalam tampilan", dan saya tidak dapat men-debug lebih dari ini.
perissf
5

Itu karena tab juga merupakan wadah penamaan ... pembaruan Anda harus update="Search:insTable:display"Apa yang dapat Anda lakukan juga adalah hanya menempatkan dialog Anda di luar formulir dan masih di dalam tab maka itu akan menjadi:update="Search:display"

Lyrion
sumber
1

Saya tahu ini sudah memiliki jawaban yang bagus oleh BalusC tetapi berikut adalah sedikit trik yang saya gunakan untuk mendapatkan wadah untuk memberi tahu saya clientId yang benar .

  1. Hapus pembaruan pada komponen Anda yang tidak berfungsi
  2. Letakkan komponen sementara dengan pembaruan palsu di dalam komponen yang Anda coba perbarui
  3. masuk ke halaman, kesalahan pengecualian servlet akan memberi tahu Anda ID klien yang benar yang perlu Anda rujuk.
  4. Hapus komponen palsu dan letakkan clientId yang benar di pembaruan asli

Berikut adalah contoh kode karena kata-kata saya mungkin tidak menggambarkannya dengan baik.

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

Hapus pembaruan yang gagal dalam komponen ini

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

Tambahkan komponen di dalam komponen id yang Anda coba perbarui menggunakan pembaruan yang akan gagal

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

Buka halaman ini dan lihat kesalahannya. Kesalahannya adalah: javax.servlet.ServletException: Tidak dapat menemukan komponen untuk ekspresi "BogusUpdate" yang dirujuk dari tab: insTable: BogusButton

Jadi clientId yang benar untuk digunakan akan menjadi huruf tebal ditambah id dari wadah target (ditampilkan dalam kasus ini)

tabs:insTable:display
jeff
sumber
0

Coba ganti update="insTable:display"ke update="display". Saya yakin Anda tidak dapat mengawali id ​​dengan ID formulir seperti itu.

Tuan J4mes
sumber
2
Jawaban yang sangat tua tapi menyesatkan. Lihat posting BalusC di atas, yang dengan jelas menunjukkan awalan ID komponen dengan ID formulir yang dilampirkan: <h: form id = "form"> <p: commandLink update = ": otherform: result"> <! - OK! -> </ h: form> <h: form id = "otherform"> <h: panelGroup id = "result" /> </ h: form>
J Slick