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答案,我已经阅读并尝试过几十个 .
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 回答
使用纯Primefaces JSF通过
activeIndex
(没有任何JavaScript伪点击技巧)的工作解决方案现在已经发现,见下文 . 上面的其余描述涉及同步JavaScript外部p:tab的点击失败尝试然后内部p:tab现在被认为是学术性的(现在对我来说是低优先级),但是仍然欢迎任何关于他们失败原因的反馈 .以下使用
activeIndex
在外部p:tabView
然后内部p:accordionPanel
与动态非缓存一起正常工作 .我坚持使用的原因之一(但现在已经放弃了)尝试使用
id
或_2595665来模仿外部内部选项卡上的点击,这样我就可以避免使用硬编码的activeIndex
数字(这样解决方案就可以很好地插入新的标签,因为每个目标id
或widgetVar
是稳定的);事实证明,如果一个人使用绑定到导航bean的f:viewParam
参数,则不必对任何activeIndex
值进行硬编码 .在页面
tabs_accordions.xhtml
中,使用p:tabView / p:tab / p:accordionPanel / p:tab嵌套:链接(来自任何内部选项卡)转到dummy_edit_viewParam.xhtml,其中包含:
然后在(例如)保存一些编辑后,可以返回到来自的任何内部选项卡 .
有趣的是,在
p:tabView
和p:accordionPanel
中,这不仅打开了一个所需的选项卡,(即打开除初始参数所针对的选项卡之外的选项卡):例如,如果您使用此进入视图循环,它将打开第二个外部选项卡的第一个内部选项卡:
如果您然后单击第二个外部选项卡的第二个内部选项卡 it will set # to 1 corresponding to that tab !
因此当一个人跟随此链接时(从第二个外部选项卡的第二个内部选项卡中),这将发送信息以定位正确的选项卡:
当这些参数最终返回到原始的tabView / accordionPanel时(例如在另一个页面中编辑值并在保存时返回),它现在将自动定位到第二个内部选项卡:
例如,要返回的页面的保存按钮的操作可能是:
然后所有这些都可以在没有任何硬编码的情况下工作
activeIndex
.