【问题标题】:Action not working with rendered property set on custom control, containing the action button操作不适用于自定义控件上设置的呈现属性,包含操作按钮
【发布时间】:2015-02-04 18:13:31
【问题描述】:

我遇到了一个奇怪的问题。情况如下:

我正在使用我的解决方案来根据屏幕大小显示和隐藏内容:https://xpagesandme.wordpress.com/2015/01/20/diving-into-bootstrap-and-font-awesome-part-3-displaying-hiding-content-based-on-device-type/

因此,我设置了一个自定义控件,其中包含移动设备的 UI。我正在使用所述自定义控件的“渲染”属性来隐藏或显示它,基于我帖子中的参数值。

这是一个奇怪的问题:

在自定义控件中,我有一个按钮,它应该设置一个作用域变量,在模态框内的面板上进行部分刷新,然后显示模态框(打开模态框的调用在 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>

【问题讨论】:

  • 我认为您在部分刷新尝试在其中运行脚本时隐藏了按钮。如果可能,坚持 CSJS 隐藏。
  • 非常感谢您的回复 Frantisek。在 Chrome 中,我可以看到,当我单击它时,唯一触发的部分刷新是按钮的部分刷新。此外,按钮事件处理程序的“onStart”和“onComplete”事件会触发(我放入了警报语句)。只是服务器端代码或简单操作没有被执行。
  • 我建议您阅读 XPages 中的“JSF 阶段”。 @Knutt 的建议和我的评论是关于当您单击按钮时服务器会更新模型的事实(实际上是 POST 请求,也就是部分刷新)。

标签: javascript xpages


【解决方案1】:

您必须在按钮中设置选项“设置部分执行模式”execMode="partial"。这将首先执行按钮的 SSJS。

否则它将首先重新计算整个页面而没有 URL 参数sDevice。因为没有设置sDevice,XPage 不会呈现当前部分(例如桌面面板)并且不会执行按钮的 SSJS。稍后在客户端,它将使用 URL 参数sDevice=desktop 执行部分刷新并呈现桌面面板,但这对于执行按钮的代码来说为时已晚。

我从您的链接修改了您的示例。它在面板“pnlList”中打印当前 URL 参数sDevice,并在按钮的 SSJS 被执行时打印“button clicked”。该按钮将随机值写入 viewScope 变量并刷新“computedField1”。这样,当您单击按钮、刷新页面或调整浏览器窗口大小时,您可以看到服务器控制台上发生的情况。

正如您可以测试的那样,按钮在渲染条件下工作(但如果没有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”渲染属性(始终返回真)并在设备特定面板的渲染条件下使用此变量:

<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';}">
    ...

【讨论】:

  • 非常感谢您对 Knut 的回复。在我发布的示例中,我尝试在桌面按钮上使用“设置部分执行模式”,但这不起作用。除非,我将按钮放在标签内,否则它会起作用。有任何想法吗?另外,我想因为按钮已经被渲染,它背后的功能也会被渲染,还是我不正确?
  • 只有在渲染条件在按钮的父/祖先元素上时才有效。我会选择 因为它没有伤害。我还不明白为什么如果渲染条件在按钮本身上它不起作用。
  • 您好 Knut,我找到了一个不需要“设置部分执行模式”的不同解决方案,它的灵感来自您在渲染属性中的 print 语句:我只是在其中设置了一个 sessionScope 变量并添加了检查 param.sDevice 和 sessionScope.get(''),如果其中一个设置为“桌面”,我会显示按钮。
  • 我更新了我的答案。这是一个更强大的解决方案,您以后不会再遇到类似的问题。
  • 您好 Knut,非常感谢您的回答。这正是我在之前的评论中提到的解决方案。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-15
  • 2019-12-13
  • 2013-02-23
相关资源
最近更新 更多