首页 文章

如何在p:tabview中p:tab中的p:accordionPanel内链接和定位/打开p:选项卡

提问于
浏览
0

Primefaces 6.0(社区版); Mojarra:2.2.8-18; Glassfish:4.1; Java版本:1.8.0_102; jQuery的

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

Q1: How link to and target/open a p:tab within an p:accordionPanel within a p:tab within a p:tabview ?

下面的其余描述涉及通过在外部p:tab(在p:tabView中)和内部p:tab(在p:accordionPanel内)使用JavaScript伪点击来实现相同目的的失败尝试 . 挑战(现在基本上是学术性的)是当p:tabView和p:accordionPanel都是动态和非缓存时如何同步它们 .


在@ViewScoped下,我在_2595628中有一个内部p:选项卡,非缓存的p:accordionPanel位于 dynamic ,非缓存的p:tabView中的外部p:选项卡中 . 我必须能够离开那个内部标签并从其他页面返回到它 .

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

<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包含),它模仿最外面的选项卡然后最里面的手风琴选项卡,没有捕获最里面的accordionPanel选项卡,最外面的tabView面板在我尝试访问内部时没有使目标可用大部分 .

我首先在下面展示一种基于回调的方法/我已经研究了如何强制连续同步函数的几十条建议,包括使用 .done() ,Promises和许多其他方法(参见下面的编辑示例),但所有这些方法似乎都失败了同样的原因,与Primefaces如何处理最外面的tabView /选项卡上的第一次单击()有关 .

使用基本回调的示例:来自 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控制台中执行此操作,则在完成第一个 click() 并且内部accordion / tabview的选项卡已正确加载后,此选择可正常工作:

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小部件变量也不会出于同样的原因:

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

如果我首先将它用于外部部分,那么内部部分,accordionWidget var尚不可用 .

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

对于这个特定的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 中的click()渲染),尽管使用了 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中的选项卡内),部分因此我可以更好地看到什么时候可用 . 在长循环完成之前,无法看到click()在外部选项卡上(在顶级tabView中)的效果,因此选择总是失败:

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

无论我从上面使用哪种同步策略,都会发生这种情况 . 同样,内部手风琴和标签的primefaces widgetVar也不可用 .


编辑:更多尝试失败 . 以下内容改编自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}') ,因此选择器会失败 .

我尝试使用p:remoteCommand的变体,遇到同样的问题:

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

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

同样的问题 .

1 回答

  • 0

    使用纯Primefaces JSF通过 activeIndex (没有任何JavaScript伪点击技巧)的工作解决方案现在已经发现,见下文 . 上面的其余描述涉及同步JavaScript外部p:tab的点击失败尝试然后内部p:tab现在被认为是学术性的(现在对我来说是低优先级),但是仍然欢迎任何关于他们失败原因的反馈 .


    以下使用 activeIndex 在外部 p:tabView 然后内部 p:accordionPanel 与动态非缓存一起正常工作 .

    我坚持使用的原因之一(但现在已经放弃了)尝试使用 id 或_2595665来模仿外部内部选项卡上的点击,这样我就可以避免使用硬编码的 activeIndex 数字(这样解决方案就可以很好地插入新的标签,因为每个目标 idwidgetVar 是稳定的);事实证明,如果一个人使用绑定到导航bean的 f:viewParam 参数,则不必对任何 activeIndex 值进行硬编码 .

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

    <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 中,这不仅打开了一个所需的选项卡,(即打开除初始参数所针对的选项卡之外的选项卡):

    activeIndex="#{navBean.tabViewActiveIndex}" 
    
    activeIndex="#{navBean.accordionActiveIndex}"
    

    例如,如果您使用此进入视图循环,它将打开第二个外部选项卡的第一个内部选项卡:

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

    如果您然后单击第二个外部选项卡的第二个内部选项卡 it will set # to 1 corresponding to that 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时(例如在另一个页面中编辑值并在保存时返回),它现在将自动定位到第二个内部选项卡:

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

    例如,要返回的页面的保存按钮的操作可能是:

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

    然后所有这些都可以在没有任何硬编码的情况下工作 activeIndex .

相关问题