0
votes

I have a panel within an xPage set to partial refresh on a timer. As indicated by the Time fields the page outside the partial refresh panel does not refresh, however, a Managed Bean in a Repeat Control outside of the refresh panel initialises on each timer event. If I replace the Repeat Control with a ComputedField the Bean does not run. This is creating unnecessary load on the server as a lot of data is collected by the Managed Bean. The problem exist with a button event in addition to the timer.

The following is my test page. Any help is appreciated.

<xp:this.beforeRenderResponse><![CDATA[#{javascript:var time = new Date(); 
 var timeString=     ("0" + time.getHours()).slice(-2)   + ":" + 
("0" + time.getMinutes()).slice(-2) + ":" + 
("0" + time.getSeconds()).slice(-2);
viewScope.put("time",timeString)}]]></xp:this.beforeRenderResponse>

<xp:scriptBlock
id="scriptBlockRefresh">
<xp:this.value>
        <![CDATA[
            setInterval(function() 
            {XSP.partialRefreshGet("#{id:refreshPanel}", {})},
             10 * 1000);]]>
</xp:this.value>
</xp:scriptBlock>

<xp:panel  id="refreshPanel">
    <xp:text escape="true" value="#{classABean.classAText}"></xp:text>
    <xp:text escape="true" value="#{viewScope.time}"></xp:text>
</xp:panel>

<xp:panel id="restOfPagePanel">
<xp:text escape="true" id="computedField4" value="#{classBBean.classBText}"></xp:text>
<xp:repeat id="repeat1" rows="30" value="#{classBBean.classBText}" var="rowData">
    <xp:text escape="true"  value="#{rowData}"></xp:text>
</xp:repeat>
<xp:text escape="true" value="#{viewScope.time}"></xp:text>
</xp:panel>

Test Managed Beans

import java.io.Serializable;
public class ClassA implements Serializable{
private static final long serialVersionUID = 1L;
private String classAText;
public String getClassAText() {
    System.out.println("Running Class A");
    classAText = "I am Class A";
    return classAText;}

Same again for ClassB

FacesConfig

<managed-bean>
<managed-bean-name>classABean</managed-bean-name>
<managed-bean-class>uk.networkconnect.wallboardutils.ClassA</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>

 <managed-bean>
<managed-bean-name>classBBean</managed-bean-name>
<managed-bean-class>uk.networkconnect.wallboardutils.ClassB</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>

Button code used for testing.

 <xp:button value="Refresh Panel A" id="button1">
<xp:eventHandler event="onclick" submit="false" >
    <xp:this.script><![CDATA[XSP.partialRefreshGet("#{id:refreshPanel}",{})
            ]]></xp:this.script>
</xp:eventHandler>
</xp:button>
2
Do you have the problem when the button is present or with the regular code? What's the button source code? - shillem
Hi - the button was added to test only I need the timer. I have added the button code above. Thanks - Mark Maden

2 Answers

1
votes

Your code should work but there's a catch - I can't say if you have already taken care of it or not. The fact is that with JSF you must make sure you load the data only the first time and be defensive about the subsequent calls. In fact, the engine might invoke the same method twice during the life cycle.

By taking as example the mockup you provided the code should look like this:

public class ClassA implements Serializable {

    private static final long serialVersionUID = 1L;

    private String text;

    public String getClassText() {
        if (text == null) {
            String name = getClass().getName();

            System.out.println("Running class " + name);

            text = "I am class " + name;
        }

        return text;
    }
}

Basically you set text only when it evaluates to null. Once the variable is set the code won't go inside the block anymore, no matter how many calls the engine makes to the method.

The XPage doesn't need modification.

<xp:scriptBlock
    value="
        setInterval(function() {
            XSP.partialRefreshGet('#{id:refreshPanel}');
        }, 10 * 1000)" />

<xp:panel id="refreshPanel" style="background-color: red">
    <xp:text escape="true" value="#{a.classText}" />
    <xp:text escape="true" value="#{viewScope.time}" />
</xp:panel>

<xp:panel id="restOfPagePanel" style="background-color: grey">
    <xp:text escape="true" id="computedField4" value="#{b.classText}" />

    <xp:repeat id="repeat1" rows="30" value="#{b.classText}"
        var="rowData">
        <xp:text escape="true" value="#{rowData}" />
    </xp:repeat>

    <xp:text escape="true" value="#{viewScope.time}" />
</xp:panel>

<xp:button value="Label" id="button1">
    <xp:eventHandler event="onclick" submit="true"
        execMode="partial" refreshMode="partial" refreshId="refreshPanel" />
</xp:button>
1
votes

There is a difference between what's refreshed to the browser and what's processed on the server. refreshId covers what area of the XPage should have HTML passed back to the browser. But the HTML there may vary depending on updates made by the user elsewhere on the page. This is the purpose of the execId, which determines the area the server should process. The value property of components in the exec area still needs to get calculated during the lifecycle. If your bean's getter is just running code each time, as Shillem says, lazy loading (if null, set the value, else use the value) is a good approach. It's a significant benefit over SSJS approaches and standard in Java.

Another alternative, if the value in the bean never changes, is to set the value property of the repeat control to computed on page load - ${b.classText} instead of #{b.classText}.

There are additional settings on a repeat control that can also be set to handle non-changing content. repeatControls="true" sets the value of the repeat at page load and creates a copy of row's components in the component tree. By default, there will be one abstract row of components which will get iterated every refresh. removeRepeat="true" will remove the repeat control once all sets of components have been loaded into the component tree.