p:tabview

时间:2016-09-27 05:55:43

标签: javascript jquery jsf primefaces

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

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

Q1:p:tabview中p:tab中p:accordionPanel内链接和目标/打开p:选项卡的方式是什么?

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

在@ViewScoped下,我在动态中有一个内部p:tab,非缓存p:accordionPanel在动态中的外部p:标签内,非缓存电话号码:TabView的。我必须能够离开那个内部标签并从其他页面返回它。

重要提示:问题只发生在动态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包含),模仿最外面的选项卡,然后是最里面的手风琴选项卡,没有捕获最里面的accordionPanel +选项卡,最外面的tabView +面板在时间I之前没有使目标可用尝试访问最内层的部分。

我首先在下面展示一种基于回调的方法/我已经研究过如何强制连续同步函数的几十条建议,包括使用.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控制台中执行此操作,则在第一个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:如何确定最里面的标签click(),确保它可以使用同步?

这个问题的可接受答案必须回答这个特定的Primefaces标签情况;请不要只是引用我关于执行异步函数的任何其他stackoverflow答案,以便使用回调,延迟/完成/下一个或Promises,我已经阅读并尝试过几十个。

[编辑:一些不起作用的例子,因为当第二次点击模拟运行时,手风琴最内层标签的选择目标(在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之外,要选择的元素尚不可用(显然还没有完全从{中的click()渲染) {1}}),尽管使用了changeTabDynamicOuterDeferred

与使用done(的承诺的此变体相同的问题:

then(

如上所述function changeTabDynamicOuterDeferredPromise(tabViewId, tabId) { $('#' + tabViewId + ' ul li a[href="#' + tabViewId + ':' + tabId + '"]').click(); return $.Deferred( function (dfd) { dfd.resolve() } ).promise(); } 并称为:

changeTabViewAccordionTabDynamic
编辑:我尝试将一个while循环放入第二个函数,用于模仿accordionPanel内部选项卡上的单击(在tabView中的选项卡内),部分因此我可以更好地看到什么是可用的。无法看到click()在外部选项卡上(在顶级tabView中)的效果,直到长循环完成后 ,因此选择总是失败:

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

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

编辑:更多尝试失败。以下内容改编自How to execute JavaScript after page load?

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

jQuery(document).ready(function () { jQuery(document).ready(function () { changeTabDynamic('#{tabViewId}','#{tabId}'); jQuery(document).ready(function () { changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}'); }); }); }); 引起的更改在运行时仍无法用于下一个函数changeTabDynamic('#{tabViewId}','#{tabId}'),因此选择器失败。

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

changeTabViewAccordionTabDynamic('#{tabViewId}','#{accordionId}','#{accordionTabId}')

同样的问题。

1 个答案:

答案 0 :(得分:0)

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

以下使用外部activeIndex中的p:tabView,内部p:accordionPanel可以正常使用动态非缓存。

我坚持使用idwidgetVar尝试使用activeIndexid模仿外部内部标签上的点击次数的原因之一(但现在已经放弃)是我可以避免使用硬编码{{ 1}}数字(这样解决方案对于插入新标签很有用,因为每个目标widgetVarf:viewParam都是稳定的);事实证明,如果一个人使用绑定到导航bean的activeIndex个参数,则不必对任何tabs_accordions.xhtml值进行硬编码。

在页面<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> 中使用p:tabView / p:tab / p:accordionPanel / p:tab nesting:

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

链接(来自任何内部标签)转到dummy_edit_viewParam.xhtml,其中包含:

p:tabView

然后在(例如)保存一些编辑后,可以返回到来自的内部选项卡。

有趣的是,在p:accordionPanelactiveIndex="#{navBean.tabViewActiveIndex}" activeIndex="#{navBean.accordionActiveIndex}" 中,这不仅打开了一个所需的标签,它还会在点击标签时设置导航bean中的相应值 (即打开除初始参数所针对的选项卡以外的选项卡):

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

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

#{navBean.accordionActiveIndex}

如果您再单击第二个外部标签的第二个内部标签,它会将<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> 设置为1,对应该标签

因此当一个人跟随此链接时(从第二个外部选项卡的第二个内部选项卡中),这将发送信息以定位正确的选项卡:

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

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

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

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

activeIndex

然后所有这些都可以在没有任何硬编码的情况下运行<?php class ConnectionInfo { public $conn; public function GetConnection() { $this->conn = mysqli_connect("serv", "user","pass", "db") or die(mysqli_error($mysql_pekare)); } } $connectionInfo = new ConnectionInfo(); $connectionInfo->GetConnection(); if (!$connectionInfo->conn) { echo 'No Connection'; }else{ $query = 'SELECT * FROM Team ORDER BY objectId'; $stmt = mysqli_query($connectionInfo->conn, $query); if (!$stmt) { //Query failed echo 'Query failed'; }else{ $contacts = array(); while ($row = mysqli_fetch_array($stmt)) { $contact = array("Image" => utf8_encode ($row['image']), "Name" => utf8_encode ($row['name']), "Role" => utf8_encode ($row['role']), "objectid" => utf8_encode ($row['objectId'])); array_push($contacts, $contact); } echo json_encode(array('results' => $contacts), JSON_PRETTY_PRINT); } } ?>