繁体   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