动作不适用于自定义控件上设置的呈现属性,包含动作按钮 [英] Action not working with rendered property set on custom control, containing the action button

查看:22
本文介绍了动作不适用于自定义控件上设置的呈现属性,包含动作按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I came across a weird issue. Here is the situation:

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

I am using my solution regarding displaying and hiding content based on the screen size: https://xpagesandme.wordpress.com/2015/01/20/diving-into-bootstrap-and-font-awesome-part-3-displaying-hiding-content-based-on-device-type/

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

So, I set up a custom control, containing the UI for a mobile device. I am using the "rendered" property of said custom control to hide or display it, based on the param value from my post.

这是一个奇怪的问题:

在自定义控件内,我有一个按钮,它应该设置一个范围变量,在模态内的面板上进行部分刷新,然后显示模态(打开模态的调用在事件处理程序).

Inside the custom control, I have a button that is supposed to set a scoped variable, do a partial refresh on a panel inside a modal and then display the modal (the call to open the modal is inside the onComplete event of the event handler).

模式打开,但作用域变量没有更新.

The modal opens, but the scoped variable was not updated.

当我删除自定义控件的渲染属性时,它可以工作.如果我再次将渲染的属性放回去,它就不起作用了.

When I remove the rendered property of the custom control, it works. If I put the rendered property back in again, it doesn't work.

此外,任何简单的操作(即打开页面)都不起作用.但是,我放入事件处理程序的 onComplete 或 onStart 事件中的 CSJS 将被执行.

Also, any simple action (i.e. opening a page) won't work. However, CSJS that I put into the onComplete or onStart event of the event handler, will be executed.

有什么想法吗?

P.S.:这是一个 XPage 示例.删除任何按钮的渲染属性,onClick 事件中的代码将起作用:

P.S.: Here is an example XPage. Remove the rendered property of any of the buttons and the code in the onClick event will work:

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

推荐答案

您必须在按钮中设置选项设置部分执行模式"execMode="partial".这肯定会首先执行按钮的 SSJS.

You have to set option "Set partial execution mode" execMode="partial" in your button. This will execute button's SSJS first and for sure.

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

Otherwise it will recalculate the whole page first without an URL parameter sDevice. Because sDevice is not set the XPage won't render the current part (e.g. desktop-panel) and won't execute button's SSJS. Later on client side, it will execute the partial refresh with URL parameter sDevice=desktop and render the desktop-panel but that is too late for executing button's code.

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

I modified your example from your link. It prints the current URL parameter sDevice in panel "pnlList" and prints "button clicked" when button's SSJS gets executed. The button writes a random value to viewScope variable and refreshes the "computedField1". This way you can see what happens on server console when you click the button, refresh page or resize browser window.

正如您可以测试的那样,按钮在呈现条件下工作(但如果没有 execMode="partial",则无法工作).如果需要,您仍然可以执行完整更新而不是示例中的部分刷新.

As you can test, button works with rendered condition (but won't work without execMode="partial"). You can still do a full update instead of the partial refresh in example if you need to.

<?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" 一起使用,但您可能会遇到这种方法的其他问题.

Not executing buttons SSJS code is a symptom of the approach itself. The panel "pnlList" gets rendered by default empty. Only after client's load or resize event panel will be partially refreshed and be filled with a device depended content. Unfortunately, the criteria for rendering is fragile as it is only part of partial refresh URL. The button might be work with option execMode="partial" but you might run into other issues with this approach.

更好的方法是让客户端告诉服务器它是什么类型的设备,服务器将保存这个信息在一个 viewScope 变量中,并且渲染条件只使用 viewScope 变量.通过这种方式,服务器已经为当前设备呈现了面板,并且只会在加载和调整大小事件时对其进行更改.

The better approach is to let the client tell the server what kind of device it is and the server will save this information in an viewScope variable and the rendered conditions using the viewScope variable only. This way the server already renders the panel for the current device and will just change it on load and resize event.

将参数 sDevice 保存到范围变量面板的pnlList"渲染属性(始终返回 true)并在设备特定面板的渲染条件下使用此变量:

Save the parameter sDevice to a scope variable panel's "pnlList" rendered property (return always true) and use this variable in rendered condition in device specific panels:

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

这篇关于动作不适用于自定义控件上设置的呈现属性,包含动作按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆