【发布时间】:2017-02-04 15:10:11
【问题描述】:
Primefaces 6.0(社区版);莫哈拉:2.2.8-18;玻璃鱼:4.1; Java版本:1.8.0_102; jQuery
[编辑:2016-10-05 使用纯 Primefaces+JSF 回答以下一般问题的答案现已得到自我回答(请参阅下面的详细答案):
Q1:如何在 p:tabview 中的 p:tab 中链接并定位/打开 p:accordionPanel 中的 p:tab?
下面的其余描述涉及通过使用 JavaScript 伪单击外部 p:tab(在 ap:tabView 内)和内部 p:tab(在 ap:accordionPanel 内)来实现相同目的的失败尝试.挑战(现在基本上是学术上的)是当 p:tabView 和 p:accordionPanel 都是动态且非缓存时如何同步这些。]
在 @ViewScoped 下,我在 dynamic 内有一个内部 p:tab,非缓存 p:accordionPanel 在 dynamic 内的外部 p:tab 内有一个非缓存 p:accordionPanel p:tabView。我必须能够离开那个内部标签并从其他页面返回。
重要提示:该问题仅发生在动态 p:tabView 和 p:accordionPanel 上,任何可接受的答案都必须适用于动态模式,因为真正的网络应用程序需要动态,因为它使用专家系统和显示的真实内容任何最里面的标签都可能受到来自其他地方的数据的影响
<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 包含),它模仿点击最外面的选项卡然后最里面的手风琴选项卡,没有捕捉到最里面的手风琴Panel+tab,最外面的 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 控制台中执行此操作,则在第一个 click() 完成并且带有内部手风琴/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);
}
如果我先将其用于外部,然后再用于内部,则手风琴Widget var 尚不可用。
Q2:如何通过同步来确保最内层标签上的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 中的 click() 呈现),尽管使用了done(。
使用带有then( 的promise 的这种变体也存在同样的问题:
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 循环,以模仿对手风琴面板内的内部选项卡的单击(在 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();
}
无论我从上面使用哪种同步策略,都会发生这种情况。同样,内部手风琴和选项卡的 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"/>
同样的问题。
【问题讨论】:
-
离题:永远不要使用
<h:form id="form" prependId="false"> : http://stackoverflow.com/questions/7415230/uiform-with-prependid-false-breaks-fajax-render (if I remember correctly, it breaksp:ajax`来` -
试过没有
dynamic="true"? -
@Kukeltje 感谢您的建议(以及您在此论坛上的其他常见帮助)。回复
prependId="false"。我意识到了这个弱点,但需要迁移大型 Web 应用程序;为未来计划。尝试了“dynamic=false”,是的,简单的回调链接方法可以完美运行,但这在任何情况下都不是我的现实世界网络应用程序的选择,它使用专家系统技术并且必须 100% 准确地更新以反映来自系统其他地方的其他依赖项。因此缓存也是顺便说一句。 (另请参阅问题编辑。) -
我会尝试从设置开始此操作时需要设置的选项卡的信号量和 ID 开始。我还将一些 ajax 标签添加到动态标签中,并在这些标签的 oncomplete 中,调用一个 javascript 函数来检查全局设置的值并调用嵌套选项卡的 widgetvars 或模拟点击它们。 'oncompletes' 是你可以用来检查某事是否完成的回调。
-
对我的评论有意见吗?您可以多次编辑您的问题,但如果问题保持不变,我之前对我将尝试的内容的评论仍然有效。请对此发表评论。
标签: javascript jquery jsf primefaces