简体   繁体   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 (community edition); Primefaces 6.0(社区版); Mojarra: 2.2.8-18; Mojarra:2.2.8-18; Glassfish: 4.1; 玻璃鱼:4.1; Java version: 1.8.0_102; Java版本:1.8.0_102; jQuery jQuery的

[EDIT: 2016-10-05 An answer using pure Primefaces+JSF for the following general question has now been self-answered (see detailed answer below): [编辑:2016-10-05对于以下一般问题,使用纯Primefaces + JSF的答案现已自动解答(请参见下面的详细答案):

Q1: How link to and target/open ap:tab within an p:accordionPanel within ap:tab within ap:tabview ? 问题1:如何在ap:tabview的ap:tab中的p:accordionPanel中链接并定位/打开ap:tab?

The remainder of the descriptions of here below concern failed attempts at achieving the same end by using JavaScript pseudo-clicking on an outer p:tab (within ap:tabView) and then an inner p:tab (within ap:accordionPanel). 下面其余的描述涉及通过使用JavaScript伪单击外部的p:tab(在ap:tabView内)然后内部的p:tab(在ap:accordionPanel内)来达到相同目的的失败尝试。 The challenge (now basically academic) is how to synchronise these when both the p:tabView and p:accordionPanel are dynamic and non-cached.] 挑战(现在基本上是学术上的挑战)是当p:tabView和p:accordionPanel是动态的且未缓存时如何同步它们。]


Under @ViewScoped I have a inner p:tab within a dynamic , non-cached p:accordionPanel within an outer p:tab within a dynamic , non-cached p:tabView. 在@ViewScoped下,我在动态的 ,非缓存的p:tabView中有一个内部p:tab,在一个动态的 ,非缓存的p:tabView中的外部p:tab中。 I have to be able to leave that inner tab and return to it from other pages. 我必须能够离开该内部标签并从其他页面返回它。

IMPORTANT: the problem only happens for dynamic p:tabView and p:accordionPanel, and any acceptable answer must work for dynamic mode, as the real web app requires dynamic because it uses an expert system and the real content shown in any innermost tab may be impacted on by data from elsewhere 重要说明:该问题仅发生在动态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']}"/>

I need the following to open up the innermost tab: 我需要以下内容打开最里面的标签:

/faces/tabs_accordions.xhtml?tabViewId=tabview&tabId=tabOuter2&accordionId=accordion&accordionTabId=tabInner2

My JavaScript (included via changeTab.js), which imitates clicking on the outermost tab then the innermost accordion tab, does not catch the innermost accordionPanel+tab, the outermost tabView+panel has not made the targets available by the time I try to access the inner most parts. 我的JavaScript(通过changeTab.js包含)模仿了单击最外面的选项卡,然后模仿了最里面的手风琴选项卡,但没有捕获最里面的手风琴面板+选项卡,最外面的tabView + panel在我尝试访问时尚未使目标可用最里面的部分。

I show one callback-based approach below first/ I have researched dozens of suggestions here for how to force consecutive synchronous functions, including using .done() , Promises, and many other approaches (see edited examples below), but all of them seem to fail for the same reason, something to with how Primefaces is handling the first click() on the outermost tabView/tab. 我首先在下面展示一种基于回调的方法/我在这里研究了许多建议,以强制执行连续的同步功能,包括使用.done() ,Promises和许多其他方法(请参见下面的编辑示例),但是所有这些似乎都是由于相同的原因而失败,Primefaces如何处理最外面的tabView / tab上的第一个click()。

Example using basic callbacks: From changeTab.js : 使用基本回调的示例:来自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();
}

Simplified for testing (without checking for existence of query params etc.) the following is in the page with the tabView/tab/accordionPanel/tab: 为简化测试(无需检查查询参数是否存在等),以下内容位于带有tabView / tab / accordionPanel / tab的页面中:

 changeTabDynamicOuterCBwithParams(
  '#{tabViewId}',
  '#{tabId}', 
  '#{accordionId}', 
  '#{accordionTabId}',
  changeTabViewAccordionTabDynamic
 );

The JS console gives: 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)

Note how the DIV and H3 selection fail, because they are not yet available. 请注意DIV和H3选择如何失败,因为它们尚不可用。

However, if I do it in the JavaScript console of a browser, after the first click() is finished and the tabs with inner accordion/tabview have loaded properly, this selection works fine: 但是,如果我在浏览器的JavaScript控制台中执行此操作,则在完成first click()且内部手风琴/标签视图的标签已正确加载后,此选择可以正常工作:

id='tabview:accordion:tabInner2'
var div = $("[id=\'" + id + "'\]");
var h3 = div.prev();

Result: 结果:

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

Using PrimeFaces widget vars also does not work for the same reason: 由于相同的原因,使用PrimeFaces小部件vars也无法正常工作:

function changeAccordionTabPF(accordionWidget,index) {
 PF(accordionWidget).select(index);     
}

If I use this for the outer part first, then the inner part, the accordionWidget var is not yet available. 如果我首先将其用于外部,然后用于内部,则手风琴小部件var尚不可用。

Q2: How can I target a click() on the innermost tab by making sure it is available using synchronisation ? 问题2:如何通过确保使用同步来确保click()在最里面的选项卡上定位?

An acceptable answer to this question must answer it for this specific Primefaces tab situation; 对于这种特定的“ Primefaces”选项卡情况,必须回答该问题。 please do not just refer me to any of the other stackoverflow answers about executing asynchronous functions in order using callbacks, deferred/done/next, or Promises, I have already read and tried dozens of them. 请不要仅仅让我参考其他有关使用回调,延迟/完成/下一步或Promises顺序执行异步函数的stackoverflow答案,我已经阅读并尝试了许多方法。


[EDIT: some more examples that don't work because the targets of the selection for the innermost tab of the accordion (within a tab within a tabView) are not available when the 2nd click simulation runs. [编辑:更多示例无法使用,因为在第二次点击模拟运行时,手风琴最里面的标签(在tabView中的标签内)的选择目标不可用。 Some are adapted from: https://stackoverflow.com/questions/39717433/how-pass-parameters-to-downstream-function-with-done-or-next] 一些改编自: 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();
}

Called as: 称为:

changeTabDynamicOuterDeferred(
 '#{tabViewId}',
 '#{tabId}'
).done(
   function() {
     changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')
   }
).fail(
  function(err) {
   console.log(err)
  }
);

Everything seems to work (and the values for the targeted id pass fine) EXCEPT that by the time changeTabViewAccordionTabDynamic the elements to be selected are not yet available (apparently not yet completely rendered from the click() in changeTabDynamicOuterDeferred ), despite the usage of done( . 一切似乎工作(和目标ID值精细遍)之外,通过时间changeTabViewAccordionTabDynamic要选择的元素尚未公布(显然还没有完全从点击()在渲染changeTabDynamicOuterDeferred )尽管使用done(

Same problem with this variation using a promise with then( : 使用then(

function changeTabDynamicOuterDeferredPromise(tabViewId, tabId)
{
  $('#' + tabViewId + ' ul li a[href="#' + tabViewId + ':' + tabId + '"]').click();

  return $.Deferred(
        function (dfd) {
            dfd.resolve()
        }
  ).promise();
}

With changeTabViewAccordionTabDynamic as above and called as: 使用上面的changeTabViewAccordionTabDynamic并称为:

changeTabDynamicOuterDeferredPromise(
 '#{tabViewId}',
 '#{tabId}'
).then(
  function () {
    changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')
  }
).fail(
  function(err) {
    console.log(err)
  }
);

EDIT: I tried putting a while loop into the 2nd function for imitating a click on the inner tab within the accordionPanel (within a tab within a tabView), in part so I can see better what is available when. 编辑:我试图将一个while循环放到第二个函数中,以模仿在AccordionPanel(在tabView的一个选项卡内)的内部选项卡上的单击,部分使我能更好地看到什么时候可用。 The effect of the click() on the outer tab (within the top-level tabView) can't be seen until after the long loop completes, and thus the selection always fails: 在长循环完成才能看到外部选项卡(在顶级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();
}

This occurs no matter which synchronisation strategy I use from above. 无论我从上面使用哪种同步策略,都会发生这种情况。 And similarly, the primefaces widgetVar of the inner accordion and tab are not available. 并且类似地,内部手风琴和制表符的素面widgetVar不可用。


EDIT: More attempts that fail. 编辑:失败的更多尝试。 The following is adapted from How to execute JavaScript after page load? 以下内容改编自页面加载后如何执行JavaScript? :

jQuery(document).ready(function () {
  jQuery(document).ready(function () {
    changeTabDynamic('#{tabViewId}','#{tabId}');        
    jQuery(document).ready(function () {
      changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}');  
    });        
  });
});    

The changes due to changeTabDynamic('#{tabViewId}','#{tabId}') are still not available to the next function changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}') by the time it runs, so the selectors fail. 由于changeTabDynamic('#{tabViewId}','#{tabId}')引起的更改仍无法用于下一个功能changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')运行时,选择器将失败。

I tried variations on using p:remoteCommand, with the same problem: 我尝试过使用p:remoteCommand的变体,但存在相同的问题:

<p:remoteCommand oncomplete="changeTabDynamic('#{tabViewId}','#{tabId}')" autoRun="true" async="false"/>

<p:remoteCommand oncomplete="changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')" autoRun="true" async="false"/>

Same problem. 同样的问题。

A working solution using pure Primefaces+JSF via activeIndex (without any JavaScript pseudo-clicking tricks) has now been found, see below. 现在已经找到了通过activeIndex使用纯Primefaces + JSF的activeIndex解决方案(没有任何JavaScript伪单击技巧),请参见下文。 The rest of the descriptions above concerning failed attempts at synchronising JavaScript clicking of an outer p:tab then an inner p:tab are now considered academic (and are now low priority for me), but any feedback on why they failed is still welcome. 上面其余的有关未能成功尝试同步外部p:tab和内部p:tab的JavaScript单击的描述,现在被认为是学术性的(对我而言,它的优先级较低),但是仍然欢迎收到有关其失败原因的任何反馈。


The following using activeIndex in an outer p:tabView then the inner p:accordionPanel works fine with dynamic non-cached. 以下在外部p:tabView使用activeIndex ,然后在内部p:accordionPanel使用动态非缓存可以正常工作。

One of the reasons I was persisting with (but have for now abandoned) attempts to imitate clicks on outer then inner tabs using id or widgetVar was so that I could avoid hard-coded activeIndex numbers (so that the solution would be robust against inserting new tabs, because each target id or widgetVar is stable); 我坚持使用(但现在已经放弃)尝试使用idwidgetVar模仿外部标签和内部标签的点击的widgetVar是,这样我就可以避免使用硬编码的activeIndex数字(这样,该解决方案就不会插入新的activeIndex标签,因为每个目标idwidgetVar都是稳定的); it turns out if one uses f:viewParam parameters bound to a navigation bean throughout one does not have to hard-code any activeIndex values. 事实证明,如果f:viewParam使用绑定到导航Bean的f:viewParam参数,则不必对任何activeIndex值进行硬编码。

In the page tabs_accordions.xhtml with the p:tabView/p:tab/p:accordionPanel/p:tab nesting: 在页面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>

The links (from either inner tab) go to dummy_edit_viewParam.xhtml, which has: 链接(从任一内部选项卡进入)都指向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>

Then on (for example) saving after some edits, one can return to whatever inner tab one came from. 然后(例如)在进行一些编辑后保存,就可以返回到其中一个内部标签页。

The interesting bit is that in p:tabView and p:accordionPanel this not only opens a desired tab, it also sets the corresponding value in the navigation bean when a tab is clicked (ie opening a tab other than the tab targeted by the initial parameters): 有趣的是,在p:tabViewp:accordionPanel这不仅会打开所需的选项卡, 而且还会在单击选项卡时 (即打开除初始参数所针对的选项卡之外的其他选项卡)在导航bean中设置相应的值。 ):

activeIndex="#{navBean.tabViewActiveIndex}" 

activeIndex="#{navBean.accordionActiveIndex}" 

For example, if you enter the view cycle with this it opens the 1st inner tab of the 2nd outer tab: 例如,如果您以此输入视图循环,则会打开第二个外部标签的第一个内部标签:

/faces/tabs_accordions.xhtml?tabViewActiveIndex=1&accordionActiveIndex=0

If you then click on the 2nd inner tab of the 2nd outer tab it will set #{navBean.accordionActiveIndex} to 1 corresponding to that tab ! 如果然后单击第二个外部选项卡的第二个内部选项卡,则会将与该选项卡相对应的#{navBean.accordionActiveIndex}设置为1

So when one follows this link (from within the 2nd inner tab of the 2nd outer tab) this will send info to target the correct 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>

When these params are used to eventually return to the original tabView/accordionPanel (such as after editing values in another page and returning on save) it will now target that 2nd inner tab automatically: 当使用这些参数最终返回到原始的tabView / accordionPanel(例如,在另一个页面中编辑值并返回保存后)时,它将现在自动定位到第二个内部标签:

/faces/tabs_accordions.xhtml?tabViewActiveIndex=1&accordionActiveIndex=1

For example, the action of a save button for a page to return from might be: 例如,页面返回的保存按钮的动作可能是:

public String saveReturnToTabViewAccordionUseParams() {    
  return "tabs_accordions?faces-redirect=true&includeViewParams=true";
}    

And all of this then works without any hard-coding of each target activeIndex . 然后,所有这些工作都无需对每个目标activeIndex进行任何硬编码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM