簡體   English   中英

如何在ap:tabview的ap:tab中的p:accordionPanel中鏈接並定位/打開ap:tab

[英]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使用動態非緩存可以正常工作。

我堅持使用(但現在已經放棄)嘗試使用idwidgetVar模仿外部標簽和內部標簽的點擊的widgetVar是,這樣我就可以避免使用硬編碼的activeIndex數字(這樣,該解決方案就不會插入新的activeIndex標簽,因為每個目標idwidgetVar都是穩定的); 事實證明,如果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:tabViewp: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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM