如何在p:tabview中的p:tab中的p:accordionPanel中链接并定位/打开p:tab [英] How link to and target/open a p:tab within an p:accordionPanel within a p:tab within a p:tabview

查看:62
本文介绍了如何在p:tabview中的p:tab中的p:accordionPanel中链接并定位/打开p:tab的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Primefaces 6.0(社区版); Mojarra:2.2.8-18;玻璃鱼:4.1; Java版本:1.8.0_102; jQuery

Primefaces 6.0 (community edition); Mojarra: 2.2.8-18; Glassfish: 4.1; Java version: 1.8.0_102; jQuery

[2016-10-05使用纯Primefaces + JSF回答以下一般问题的答案现已自行解答(请参见下面的详细答案):

[ 2016-10-05 An answer using pure Primefaces+JSF for the following general question has now been self-answered (see detailed answer below):

问题1:如何在p:tabview的p:tab内的p:accordionPanel内链接和定位/打开p:tab?

下面其余的描述涉及通过使用JavaScript伪单击外部p:tab(在ap:tabView内)然后内部p:tab(在ap:accordionPanel内)来达到相同目的的失败尝试. .挑战(现在基本上是学术上的挑战)是当p:tabView和p:accordionPanel是动态的且未缓存时如何同步它们.]

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 a p:tabView) and then an inner p:tab (within a p:accordionPanel). The challenge (now basically academic) is how to synchronise these when both the p:tabView and p:accordionPanel are dynamic and non-cached.]

在@ViewScoped下,我在一个 dynamic 内有一个内部p:tab,未缓存p:accordionPanel在一个 dynamic 内的外部p:tab中,没有缓存p:tabView.我必须能够离开该内部标签并从其他页面返回它.

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. I have to be able to leave that inner tab and return to it from other pages.

重要提示:仅动态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

我的JavaScript(通过changeTab.js包含)模仿了在最外面的选项卡上单击,然后在最里面的手风琴选项卡上单击,但没有捕获到最里面的手风琴面板+选项卡,最外面的tabView + panel在我使用时尚未使目标可用尝试访问最里面的部分.

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.

我首先在下面展示了一种基于回调的方法/我在这里研究了很多建议,以介绍如何强制使用连续的同步功能,包括使用.done(),Promises和许多其他方法(请参见下面的编辑示例),但是所有它们似乎由于相同的原因而失败,这与Primefaces如何处理最外面的tabView/tab上的第一个click()有关.

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.

使用基本回调的示例:来自changeTab.js:

Example using basic callbacks: From 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的页面中:

Simplified for testing (without checking for existence of query params etc.) the following is in the page with the 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选择失败的原因,因为它们尚不可用.

Note how the DIV and H3 selection fail, because they are not yet available.

但是,如果我在浏览器的JavaScript控制台中执行此操作,则在第一个click()完成并正确加载了带有内部手风琴/tabview的选项卡之后,此选择可以正常工作:

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:

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小部件var也无法正常工作

Using PrimeFaces widget vars also does not work for the same reason:

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

如果我先将其用于外部,然后将其用于内部,则手风琴小部件var尚不可用.

If I use this for the outer part first, then the inner part, the accordionWidget var is not yet available.

第二季度:如何通过确保同步中的click()可用来定位最里面的标签?

Q2: How can I target a click() on the innermost tab by making sure it is available using synchronisation ?

此问题的可接受答案必须针对特定的"Primefaces"选项卡情况回答.请不要仅仅让我参考其他有关使用回调,延迟/完成/下一步或Promises顺序执行异步功能的stackoverflow答案,我已经阅读并尝试了许多方法.

_"> https://stackoverflow .com/questions/39717433/如何将参数传递到下游功能(完成或下一步)

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中的click()尚未完全呈现) ),尽管使用了done(.

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

使用带有then(的promise的这种变化存在相同的问题:

Same problem with this variation using a promise with 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()的效果,因此选择始终失败:


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

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();
}

无论我从上面使用哪种同步策略,都会发生这种情况.同样,内部手风琴和制表符的素面小部件Var也不可用.

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.

失败的更多尝试.以下内容是从如何在页面加载后执行JavaScript改编而成的::

More attempts that fail. The following is adapted from How to execute JavaScript after page load?:

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}')运行时仍不可用,因此选择器失败.

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.

我尝试过使用p:remoteCommand的变体,但存在相同的问题:

I tried variations on using p:remoteCommand, with the same problem:

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

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

同样的问题.

推荐答案

现已找到一种通过activeIndex使用纯Primefaces + JSF(没有任何JavaScript伪点击技巧)的有效解决方案,请参见下文.上面其余的有关未能成功尝试同步外部p:tab和内部p:tab的JavaScript单击的描述,现在被认为是学术性的(对我而言,它的优先级较低),但是仍欢迎收到有关其失败原因的任何反馈.

A working solution using pure Primefaces+JSF via activeIndex (without any JavaScript pseudo-clicking tricks) has now been found, see below. 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:tabView中使用activeIndex,然后内部p:accordionPanel可以与动态非缓存一起很好地工作.

The following using activeIndex in an outer p:tabView then the inner p:accordionPanel works fine with dynamic non-cached.

我坚持(但现在已经放弃)尝试模仿使用idwidgetVar在外部和内部选项卡上单击的原因之一是为了避免使用硬编码的activeIndex数字(因此该解决方案对于插入新选项卡将是可靠的,因为每个目标idwidgetVar是稳定的);事实证明,如果整个人都使用绑定到导航Bean的f:viewParam参数,则不必对任何activeIndex值进行硬编码.

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); 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.

在页面tabs_accordions.xhtml中,带有p:tabView/p:tab/p:accordionPanel/p:tab嵌套:

In the page tabs_accordions.xhtml with the p:tabView/p:tab/p:accordionPanel/p:tab nesting:

<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,该文件具有:

The links (from either inner tab) go to dummy_edit_viewParam.xhtml, which has:

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

有趣的是,在p:tabViewp:accordionPanel中,这不仅打开了所需的标签,而且当单击标签时,它还会在导航bean中设置相应的值(即打开一个标签,而不是初始参数定位的标签):

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 (i.e. opening a tab other than the tab targeted by the initial parameters):

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

如果您随后单击第二个外部选项卡的第二个内部选项卡,则会将#{navBean.accordionActiveIndex}设置为与该选项卡相对应的1

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 !

因此,当一个人点击此链接时(从第二个外部标签的第二个内部标签中),它将发送信息以定位正确的标签:

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>

使用这些参数最终返回到原始的tabView/accordionPanel(例如,在另一个页面中编辑值并保存后返回)之后,它现在将自动定位到第二个内部标签:

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:

/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";
}    

然后所有这些工作都无需对每个目标activeIndex进行任何硬编码.

And all of this then works without any hard-coding of each target activeIndex.

这篇关于如何在p:tabview中的p:tab中的p:accordionPanel中链接并定位/打开p:tab的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆