[英]How link to and target/open a p:tab within an p:accordionPanel within a p:tab within a p:tabview
Primefaces 6.0(社區版); Mojarra:2.2.8-18; 玻璃魚:4.1; Java版本:1.8.0_102; jQuery的
[編輯:2016-10-05對於以下一般問題,使用純Primefaces + JSF的答案現已自動解答(請參見下面的詳細答案):
問題1:如何在ap:tabview的ap:tab中的p:accordionPanel中鏈接並定位/打開ap:tab?
下面其余的描述涉及通過使用JavaScript偽單擊外部的p:tab(在ap:tabView內)然后內部的p:tab(在ap:accordionPanel內)來達到相同目的的失敗嘗試。 挑戰(現在基本上是學術上的挑戰)是當p:tabView和p:accordionPanel是動態的且未緩存時如何同步它們。]
在@ViewScoped下,我在動態的 ,非緩存的p:tabView中有一個內部p:tab,在一個動態的 ,非緩存的p:tabView中的外部p:tab中。 我必須能夠離開該內部標簽並從其他頁面返回它。
重要說明:該問題僅發生在動態p:tabView和p:accordionPanel上,並且任何可接受的答案都必須在動態模式下起作用,因為真正的Web應用程序要求動態,因為它使用專家系統,並且任何最里面的選項卡中顯示的實際內容都可能是動態的。受其他地方的數據影響
<h:body>
<h:outputScript name="js/changeTab.js" />
<h:form id="form" prependId="false">
<p:tabView id='tabview' dynamic="true" cache="false" widgetVar="widgetTabView">
<p:tab title="Outer Tab1" id="tabOuter1">Content of Tab1</p:tab>
<p:tab title="Outer Tab2" id="tabOuter2">
<p:accordionPanel
id="accordion"
activeIndex="null"
dynamic="true"
cache="false"
widgetVar="widgetAccordion"
>
<p:tab title="Inner Tab1" id="tabInner1">Content of inner Tab1</p:tab>
<p:tab title="Inner Tab2" id="tabInner2">Content of inner Tab2</p:tab>
</p:accordionPanel>
</p:tab>
</p:tabView>
<ui:param name="tabViewId" value="#{param['tabViewId']}"/>
<ui:param name="tabId" value="#{param['tabId']}"/>
<ui:param name="accordionId" value="#{param['accordionId']}"/>
<ui:param name="accordionTabId" value="#{param['accordionTabId']}"/>
我需要以下內容打開最里面的標簽:
/faces/tabs_accordions.xhtml?tabViewId=tabview&tabId=tabOuter2&accordionId=accordion&accordionTabId=tabInner2
我的JavaScript(通過changeTab.js包含)模仿了單擊最外面的選項卡,然后模仿了最里面的手風琴選項卡,但沒有捕獲最里面的手風琴面板+選項卡,最外面的tabView + panel在我嘗試訪問時尚未使目標可用最里面的部分。
我首先在下面展示一種基於回調的方法/我在這里研究了許多建議,以強制執行連續的同步功能,包括使用.done()
,Promises和許多其他方法(請參見下面的編輯示例),但是所有這些似乎都是由於相同的原因而失敗,Primefaces如何處理最外面的tabView / tab上的第一個click()。
使用基本回調的示例:來自changeTab.js
:
function changeTabDynamicOuterCBwithParams(tabViewId, tabId, accordionId, accordionTabId, callback)
{
// ... log params omitted ...
$('#' + tabViewId + ' ul li a[href="#' + tabViewId + ':' + tabId + '"]').click();
callback(tabViewId,accordionId,accordionTabId);
}
/**
* Simulates clicking, and thus selection, of a p:tab within a p:accordionPanel.
*
* IMPORTANT: For h:form with prepend=false !
*
* NB: There is usually a p:tabView, p:tab, p:accordionPanel, p:tab.
* but the first p:tab does not affect the ultimate client-side id.
*
* @param {type} tabViewId Identifier of an outer ancestor p:tabView
* @param {type} accordionId Identifier of an immediate parent p:accordion
* @param {type} tabId Identifier of an immediate child p:tab
* @returns {undefined}
*/
function changeTabViewAccordionTabDynamic(tabViewId,accordionId, accordionTabId)
{
var i = 'changeTabViewAccordionTabDynamic';
console.log(i+": tabViewId("+tabViewId+")");
console.log(i+": accordionId("+accordionId+")");
console.log(i+": accordionTabId("+accordionTabId+")");
var id = tabViewId + ":"+accordionId+":"+accordionTabId;
console.log(i+": id("+id+")");
var div = $("[id=\'" + id + "'\]");
console.log(div.length);
// The id is on the DIV following the H3 to click on.
console.log(i+": div.length("+div.length+")");
var h3 = div.prev();
console.log(i+": h3.length("+h3.length+")");
console.log(i+": clicking header of inner tab within accordion within outer tab within tabview");
h3.click();
}
為簡化測試(無需檢查查詢參數是否存在等),以下內容位於帶有tabView / tab / accordionPanel / tab的頁面中:
changeTabDynamicOuterCBwithParams(
'#{tabViewId}',
'#{tabId}',
'#{accordionId}',
'#{accordionTabId}',
changeTabViewAccordionTabDynamic
);
JS控制台提供:
changeTabDynamicOuterCB2: tabViewId(tabview)
changeTab.js:100 changeTabDynamicOuterCB2: tabId(tabOuter2)
changeTab.js:101 changeTabDynamicOuterCB2: accordionId(accordion)
changeTab.js:102 changeTabDynamicOuterCB2: accordionTabId(tabInner2)
changeTab.js:103 changeTabDynamicOuterCB2: clicking on outer tab within tabview THEN calling back on inner tab
changeTab.js:128 changeTabViewAccordionTabDynamic: tabViewId(tabview)
changeTab.js:129 changeTabViewAccordionTabDynamic: accordionId(accordion)
changeTab.js:130 changeTabViewAccordionTabDynamic: accordionTabId(tabInner2)
changeTab.js:133 changeTabViewAccordionTabDynamic: id(tabview:accordion:tabInner2)
changeTab.js:135 0
changeTab.js:137 changeTabViewAccordionTabDynamic: div.length(0)
changeTab.js:139 changeTabViewAccordionTabDynamic: h3.length(0)
請注意DIV和H3選擇如何失敗,因為它們尚不可用。
但是,如果我在瀏覽器的JavaScript控制台中執行此操作,則在完成first click()
且內部手風琴/標簽視圖的標簽已正確加載后,此選擇可以正常工作:
id='tabview:accordion:tabInner2'
var div = $("[id=\'" + id + "'\]");
var h3 = div.prev();
結果:
h3;
<h3 class="ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" role="tab" aria-expanded="false" aria-selected="false" tabindex="0">…</h3>
由於相同的原因,使用PrimeFaces小部件vars也無法正常工作:
function changeAccordionTabPF(accordionWidget,index) {
PF(accordionWidget).select(index);
}
如果我首先將其用於外部,然后用於內部,則手風琴小部件var尚不可用。
問題2:如何通過確保使用同步來確保click()
在最里面的選項卡上定位?
對於這種特定的“ Primefaces”選項卡情況,必須回答該問題。 請不要僅僅讓我參考其他有關使用回調,延遲/完成/下一步或Promises順序執行異步函數的stackoverflow答案,我已經閱讀並嘗試了許多方法。
[編輯:更多示例無法使用,因為在第二次點擊模擬運行時,手風琴最里面的標簽(在tabView中的標簽內)的選擇目標不可用。 一些改編自: https : //stackoverflow.com/questions/39717433/how-pass-parameters-to-downstream-function-with-done-or-next]
function changeTabDynamicOuterDeferred(tabViewId, tabId)
{
$('#' + tabViewId + ' ul li a[href="#' + tabViewId + ':' + tabId + '"]').click();
return $.Deferred().resolve();
}
function changeTabViewAccordionTabDynamic(tabViewId, accordionId, accordionTabId)
{
var id = tabViewId + ":" + accordionId + ":" + accordionTabId;
var div = $("[id=\'" + id + "'\]");
// The id is on the DIV following the H3 to click on.
var h3 = div.prev();
h3.click();
}
稱為:
changeTabDynamicOuterDeferred(
'#{tabViewId}',
'#{tabId}'
).done(
function() {
changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')
}
).fail(
function(err) {
console.log(err)
}
);
一切似乎工作(和目標ID值精細遍)之外,通過時間changeTabViewAccordionTabDynamic
要選擇的元素尚未公布(顯然還沒有完全從點擊()在渲染changeTabDynamicOuterDeferred
)盡管使用done(
。
使用then(
function changeTabDynamicOuterDeferredPromise(tabViewId, tabId)
{
$('#' + tabViewId + ' ul li a[href="#' + tabViewId + ':' + tabId + '"]').click();
return $.Deferred(
function (dfd) {
dfd.resolve()
}
).promise();
}
使用上面的changeTabViewAccordionTabDynamic
並稱為:
changeTabDynamicOuterDeferredPromise(
'#{tabViewId}',
'#{tabId}'
).then(
function () {
changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')
}
).fail(
function(err) {
console.log(err)
}
);
編輯:我試圖將一個while循環放到第二個函數中,以模仿在AccordionPanel(在tabView的一個選項卡內)的內部選項卡上的單擊,部分使我能更好地看到什么時候可用。 在長循環完成后才能看到外部選項卡(在頂級tabView內)上click()的效果,因此選擇始終失敗:
function changeTabViewAccordionTabDynamic(tabViewId, accordionId, accordionTabId)
{
var id = tabViewId + ":" + accordionId + ":" + accordionTabId;
var timer = 0;
var div = $("[id=\'" + id + "'\]");
while (div.length < 1 && timer++ < 100000) {
div = $("[id=\'" + id + "'\]"); // Proper code would make this DRY
console.log(div.length);
}
// The id is on the DIV following the H3 to click on.
var h3 = div.prev();
h3.click();
}
無論我從上面使用哪種同步策略,都會發生這種情況。 並且類似地,內部手風琴和制表符的素面widgetVar不可用。
編輯:失敗的更多嘗試。 以下內容改編自頁面加載后如何執行JavaScript? :
jQuery(document).ready(function () {
jQuery(document).ready(function () {
changeTabDynamic('#{tabViewId}','#{tabId}');
jQuery(document).ready(function () {
changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}');
});
});
});
由於changeTabDynamic('#{tabViewId}','#{tabId}')
引起的更改仍無法用於下一個功能changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')
運行時,選擇器將失敗。
我嘗試過使用p:remoteCommand的變體,但存在相同的問題:
<p:remoteCommand oncomplete="changeTabDynamic('#{tabViewId}','#{tabId}')" autoRun="true" async="false"/>
<p:remoteCommand oncomplete="changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')" autoRun="true" async="false"/>
同樣的問題。
現在已經找到了通過activeIndex
使用純Primefaces + JSF的activeIndex
解決方案(沒有任何JavaScript偽單擊技巧),請參見下文。 上面其余的有關未能成功嘗試同步外部p:tab和內部p:tab的JavaScript單擊的描述,現在被認為是學術性的(對我而言,它的優先級較低),但是仍然歡迎收到有關其失敗原因的任何反饋。
以下在外部p:tabView
使用activeIndex
,然后在內部p:accordionPanel
使用動態非緩存可以正常工作。
我堅持使用(但現在已經放棄)嘗試使用id
或widgetVar
模仿外部標簽和內部標簽的點擊的widgetVar
是,這樣我就可以避免使用硬編碼的activeIndex
數字(這樣,該解決方案就不會插入新的activeIndex
標簽,因為每個目標id
或widgetVar
都是穩定的); 事實證明,如果f:viewParam
使用綁定到導航Bean的f:viewParam
參數,則不必對任何activeIndex
值進行硬編碼。
在頁面tabs_accordions.xhtml
與p:TabView的/ P:標簽/ P:accordionPanel / P:標簽嵌套:
<f:view>
<f:metadata>
<f:viewParam name="tabViewActiveIndex" value="#{navBean.tabViewActiveIndex}" required="false"/>
<f:viewParam name="accordionActiveIndex" value="#{navBean.accordionActiveIndex}" required="false"/>
...
</f:metadata>
</f:view>
<p:tabView
id='tabview'
dynamic="true"
cache="false"
widgetVar="widgetTabView"
activeIndex="#{navBean.tabViewActiveIndex}"
>
<p:tab title="Outer Tab1" id="tabOuter1">
Content of Tab1
</p:tab>
<p:tab title="Outer Tab2" id="tabOuter2" >
<p:accordionPanel
id="accordion"
dynamic="true"
cache="false"
widgetVar="widgetAccordion"
activeIndex="#{navBean.accordionActiveIndex}"
>
<p:tab title="Inner Tab1" id="tabInner1">
<h:link
outcome="dummy_edit_viewParam"
value="Link1: Go to pretend edit page then return to this 1st inner tab">
<f:param name="stem" value="tabs_accordions"/>
<f:param name="tabViewActiveIndex" value="#{navBean.tabViewActiveIndex}"/>
<f:param name="accordionActiveIndex" value="#{navBean.accordionActiveIndex}"/>
</h:link>
</p:tab>
<p:tab title="Inner Tab2 " id="tabInner2">
<h:link
outcome="dummy_edit_viewParam"
value="Link2: Go to pretend edit page then return to this 2nd inner tab">
<f:param name="stem" value="tabs_accordions"/>
<f:param name="tabViewActiveIndex" value="#{navBean.tabViewActiveIndex}"/>
<f:param name="accordionActiveIndex" value="#{navBean.accordionActiveIndex}"/>
</h:link>
</p:tab>
</p:accordionPanel>
</p:tab>
</p:tabView>
鏈接(從任一內部選項卡進入)都指向dummy_edit_viewParam.xhtml,該鏈接具有:
<f:view>
<f:metadata>
<f:viewParam name="stem" value="#{navBean.stem}" required="true"/>
<f:viewParam name="tabViewActiveIndex" value="#{navBean.tabViewActiveIndex}" required="true"/>
<f:viewParam name="accordionActiveIndex" value="#{navBean.accordionActiveIndex}" required="true"/>
</f:metadata>
</f:view>
然后(例如)在進行一些編輯后保存,就可以返回到其中一個內部標簽頁。
有趣的是,在p:tabView
和p:accordionPanel
這不僅會打開所需的選項卡, 而且還會在單擊選項卡時 (即打開除初始參數所針對的選項卡之外的其他選項卡)在導航bean中設置相應的值。 ):
activeIndex="#{navBean.tabViewActiveIndex}"
activeIndex="#{navBean.accordionActiveIndex}"
例如,如果您以此輸入視圖循環,則會打開第二個外部標簽的第一個內部標簽:
/faces/tabs_accordions.xhtml?tabViewActiveIndex=1&accordionActiveIndex=0
如果然后單擊第二個外部選項卡的第二個內部選項卡,則會將與該選項卡相對應的#{navBean.accordionActiveIndex}
設置為1 !
因此,當一個用戶點擊此鏈接時(從第二個外部標簽的第二個內部標簽中),它將發送信息以定位正確的標簽:
<p:tab title="Inner Tab2 " id="tabInner2">
<h:link
outcome="dummy_edit_viewParam"
value="Link2: Go to pretend edit page then return to this 2nd inner tab">
<f:param name="stem" value="tabs_accordions"/>
<f:param name="tabViewActiveIndex" value="#{navBean.tabViewActiveIndex}"/>
<f:param name="accordionActiveIndex" value="#{navBean.accordionActiveIndex}"/>
</h:link>
</p:tab>
當使用這些參數最終返回到原始的tabView / accordionPanel(例如,在另一個頁面中編輯值並返回保存后)時,它將現在自動定位到第二個內部標簽:
/faces/tabs_accordions.xhtml?tabViewActiveIndex=1&accordionActiveIndex=1
例如,頁面返回的保存按鈕的動作可能是:
public String saveReturnToTabViewAccordionUseParams() {
return "tabs_accordions?faces-redirect=true&includeViewParams=true";
}
然后,所有這些工作都無需對每個目標activeIndex
進行任何硬編碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.