操作不适用于在自定义控件上设置呈现 属性,包含操作按钮
Action not working with rendered property set on custom control, containing the action button
我遇到了一个奇怪的问题。情况是这样的:
我正在使用基于屏幕尺寸显示和隐藏内容的解决方案:https://xpagesandme.wordpress.com/2015/01/20/diving-into-bootstrap-and-font-awesome-part-3-displaying-hiding-content-based-on-device-type/
因此,我设置了一个自定义控件,其中包含用于移动设备的 UI。我正在使用所述自定义控件的 "rendered" 属性 根据我的 post.
中的参数值隐藏或显示它
这是一个奇怪的问题:
在自定义控件中,我有一个按钮应该设置范围变量,在模态内的面板上进行部分刷新,然后显示模态(打开模态的调用在 onComplete 事件中事件处理程序)。
模式打开,但范围变量未更新。
当我删除呈现的自定义控件 属性 时,它起作用了。如果我再次将渲染的 属性 放回去,它就不起作用了。
此外,任何简单的操作(即打开页面)都不起作用。但是,我放入事件处理程序的 onComplete 或 onStart 事件中的 CSJS 将被执行。
有什么想法吗?
P.S.: 这是一个 XPage 示例。删除任何按钮的呈现 属性,onClick 事件中的代码将起作用:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshGet( '#{id:pnlList}', {params: {'sDevice': sDevice}});
});]]></xp:this.script>
</xp:eventHandler>
<xp:panel
id="pnlList">
<xp:button
value="Button Mobile"
id="button1" rendered="#{javascript:param.sDevice == 'mobile';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Mobile clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:button
id="button2" value="Button Tablet" rendered="#{javascript:param.sDevice == 'tablet';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Tablet clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:button
value="Button Desktop"
id="button3" rendered="#{javascript:param.sDevice == 'desktop';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Desktop clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button></xp:panel>
<xp:panel id="pnlToUpdate">
<xp:text
escape="true"
id="computedField1" value="#{sessionScope.sTestValue}">
</xp:text></xp:panel></xp:view>
您必须在按钮中设置选项 "Set partial execution mode" execMode="partial"
。这将首先执行按钮的 SSJS。
否则它将在没有URL参数的情况下首先重新计算整个页面sDevice
。因为未设置 sDevice
,XPage 不会呈现当前部分(例如桌面面板),也不会执行按钮的 SSJS。稍后在客户端,它将使用 URL 参数 sDevice=desktop
执行部分刷新并呈现桌面面板,但这对于执行按钮代码来说为时已晚。
我根据您的 link 修改了您的示例。它在面板 "pnlList" 中打印当前 URL 参数 sDevice
,并在执行按钮的 SSJS 时打印 "button clicked"。该按钮将一个随机值写入 viewScope 变量并刷新 "computedField1"。通过这种方式,您可以在单击按钮、刷新页面或调整浏览器大小时查看服务器控制台上发生的情况 window。
正如您所测试的,按钮在渲染条件下工作(但在没有 execMode="partial"
的情况下将无法工作)。如果需要,您仍然可以执行完整更新而不是示例中的部分刷新。
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshPost( '#{id:pnlList}', {params: {'sDevice': sDevice}} );
});]]></xp:this.script>
</xp:eventHandler>
<xp:panel
id="pnlList"
rendered="#{javascript:print('param.sDevice: ' + param.sDevice); return true}">
<xp:label
value="I am going to be displayed if I am on a mobile device"
id="label1"
rendered="#{javascript:param.sDevice == 'mobile';}">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete"></xp:eventHandler></xp:label>
<xp:br></xp:br>
<xp:label
value="I am going to be displayed if I am on a tablet device"
id="label2"
rendered="#{javascript:param.sDevice == 'tablet'}">
</xp:label>
<xp:br></xp:br>
<xp:label
value="I am going to be displayed if I am on a desktop device"
id="label3"
rendered="#{javascript:param.sDevice == 'desktop'}">
<xp:button
value="Label"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial" execMode="partial" refreshId="computedField1">
<xp:this.action><![CDATA[#{javascript:
print("button clicked");
viewScope.currentItem = Math.random();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:label>
</xp:panel>
<br />
<xp:text
escape="true"
id="computedField1"
value="#{viewScope.currentItem}">
</xp:text>
</xp:view>
更新
不执行按钮 SSJS 代码是该方法本身的一个症状。默认情况下,面板 "pnlList" 呈现为空。只有在客户端加载或调整大小后,事件面板才会部分刷新并填充设备相关的内容。不幸的是,渲染标准很脆弱,因为它只是部分刷新的一部分 URL。该按钮可能与选项 execMode="partial"
一起使用,但您可能 运行 使用此方法会遇到其他问题。
更好的方法是让客户端告诉服务器它是什么类型的设备,服务器将保存此信息在 viewScope 变量和使用 viewScope 的呈现条件中变量而已。这样服务器就已经为当前设备呈现面板,并且只会在加载和调整大小事件时更改它。
将参数 sDevice 保存到范围变量面板的 "pnlList" 呈现 属性(return 始终为真)并在设备特定面板的呈现条件下使用此变量:
<xp:panel
id="pnlList"
rendered="#{javascript:
if (param.sDevice) {
viewScope.device = param.sDevice;
}
return true}">
<xp:button
value="Button Mobile"
id="button1"
rendered="#{javascript:viewScope.device == 'mobile';}">
...
我遇到了一个奇怪的问题。情况是这样的:
我正在使用基于屏幕尺寸显示和隐藏内容的解决方案:https://xpagesandme.wordpress.com/2015/01/20/diving-into-bootstrap-and-font-awesome-part-3-displaying-hiding-content-based-on-device-type/
因此,我设置了一个自定义控件,其中包含用于移动设备的 UI。我正在使用所述自定义控件的 "rendered" 属性 根据我的 post.
中的参数值隐藏或显示它这是一个奇怪的问题:
在自定义控件中,我有一个按钮应该设置范围变量,在模态内的面板上进行部分刷新,然后显示模态(打开模态的调用在 onComplete 事件中事件处理程序)。
模式打开,但范围变量未更新。
当我删除呈现的自定义控件 属性 时,它起作用了。如果我再次将渲染的 属性 放回去,它就不起作用了。
此外,任何简单的操作(即打开页面)都不起作用。但是,我放入事件处理程序的 onComplete 或 onStart 事件中的 CSJS 将被执行。
有什么想法吗?
P.S.: 这是一个 XPage 示例。删除任何按钮的呈现 属性,onClick 事件中的代码将起作用:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshGet( '#{id:pnlList}', {params: {'sDevice': sDevice}});
});]]></xp:this.script>
</xp:eventHandler>
<xp:panel
id="pnlList">
<xp:button
value="Button Mobile"
id="button1" rendered="#{javascript:param.sDevice == 'mobile';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Mobile clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:button
id="button2" value="Button Tablet" rendered="#{javascript:param.sDevice == 'tablet';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Tablet clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:button
value="Button Desktop"
id="button3" rendered="#{javascript:param.sDevice == 'desktop';}">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="pnlToUpdate"
onStart="alert('onStart')"
onComplete="alert('onComplete')">
<xp:this.action><![CDATA[#{javascript:sessionScope.put('sTestValue', 'Button Desktop clicked')}]]></xp:this.action>
</xp:eventHandler></xp:button></xp:panel>
<xp:panel id="pnlToUpdate">
<xp:text
escape="true"
id="computedField1" value="#{sessionScope.sTestValue}">
</xp:text></xp:panel></xp:view>
您必须在按钮中设置选项 "Set partial execution mode" execMode="partial"
。这将首先执行按钮的 SSJS。
否则它将在没有URL参数的情况下首先重新计算整个页面sDevice
。因为未设置 sDevice
,XPage 不会呈现当前部分(例如桌面面板),也不会执行按钮的 SSJS。稍后在客户端,它将使用 URL 参数 sDevice=desktop
执行部分刷新并呈现桌面面板,但这对于执行按钮代码来说为时已晚。
我根据您的 link 修改了您的示例。它在面板 "pnlList" 中打印当前 URL 参数 sDevice
,并在执行按钮的 SSJS 时打印 "button clicked"。该按钮将一个随机值写入 viewScope 变量并刷新 "computedField1"。通过这种方式,您可以在单击按钮、刷新页面或调整浏览器大小时查看服务器控制台上发生的情况 window。
正如您所测试的,按钮在渲染条件下工作(但在没有 execMode="partial"
的情况下将无法工作)。如果需要,您仍然可以执行完整更新而不是示例中的部分刷新。
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshPost( '#{id:pnlList}', {params: {'sDevice': sDevice}} );
});]]></xp:this.script>
</xp:eventHandler>
<xp:panel
id="pnlList"
rendered="#{javascript:print('param.sDevice: ' + param.sDevice); return true}">
<xp:label
value="I am going to be displayed if I am on a mobile device"
id="label1"
rendered="#{javascript:param.sDevice == 'mobile';}">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete"></xp:eventHandler></xp:label>
<xp:br></xp:br>
<xp:label
value="I am going to be displayed if I am on a tablet device"
id="label2"
rendered="#{javascript:param.sDevice == 'tablet'}">
</xp:label>
<xp:br></xp:br>
<xp:label
value="I am going to be displayed if I am on a desktop device"
id="label3"
rendered="#{javascript:param.sDevice == 'desktop'}">
<xp:button
value="Label"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial" execMode="partial" refreshId="computedField1">
<xp:this.action><![CDATA[#{javascript:
print("button clicked");
viewScope.currentItem = Math.random();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:label>
</xp:panel>
<br />
<xp:text
escape="true"
id="computedField1"
value="#{viewScope.currentItem}">
</xp:text>
</xp:view>
更新
不执行按钮 SSJS 代码是该方法本身的一个症状。默认情况下,面板 "pnlList" 呈现为空。只有在客户端加载或调整大小后,事件面板才会部分刷新并填充设备相关的内容。不幸的是,渲染标准很脆弱,因为它只是部分刷新的一部分 URL。该按钮可能与选项 execMode="partial"
一起使用,但您可能 运行 使用此方法会遇到其他问题。
更好的方法是让客户端告诉服务器它是什么类型的设备,服务器将保存此信息在 viewScope 变量和使用 viewScope 的呈现条件中变量而已。这样服务器就已经为当前设备呈现面板,并且只会在加载和调整大小事件时更改它。
将参数 sDevice 保存到范围变量面板的 "pnlList" 呈现 属性(return 始终为真)并在设备特定面板的呈现条件下使用此变量:
<xp:panel
id="pnlList"
rendered="#{javascript:
if (param.sDevice) {
viewScope.device = param.sDevice;
}
return true}">
<xp:button
value="Button Mobile"
id="button1"
rendered="#{javascript:viewScope.device == 'mobile';}">
...