javascriptprimefacesdialogcountdowntimer

Countdown timer in PrimeFaces confirmDialog


In my web application I have a idle monitor which is triggered if a user is idle for 5 minutes. It will open a confirm dialog which will wait for 2 minutes; after that it will redirect to login page.

Requirement: I want to show a countdown timer with the time remaining before the user will be redirected to the login page.

baseTemplate.xhtml:

<h:form>

    <p:idleMonitor timeout="#{idleMonitorView.timeoutVal}"
        onidle="startTimer()" />

    <p:confirmDialog id="confirmDialog"
        message="You have been idle, Please click ok to remain logged in"
        header="Are you there?" severity="alert" widgetVar="idleDialog">                 
        <p:commandButton id="confirm" value="Ok" oncomplete="extendSession()" />

    </p:confirmDialog>

    <p:remoteCommand name="terminateIdleSession" actionListener="#{idleMonitorView.onIdle}" out="count" />

    <script type="text/javascript">
        var extend_session = 0;
        function startTimer() {

            extend_session = 0;
            setTimeout("timeout()", 120000);
            PF('idleDialog').show();
        }

        function timeout() {
            if (extend_session == 0) {
                terminateIdleSession();
            }
        }

        function extendSession() {
            extend_session = 1;
            PF('idleDialog').hide();
        }
    </script>
</h:form>

Problem: I don't know that how to achieve this requirement.


Solution

  • You should create a JavaScript interval which updates a timer every second (or any other interval you need). You can update your timer using a bit of jQuery. I would recommend to add a span to the dialog message which you can use as a timer:

    $("#myForm\\:confirmDialog .ui-confirm-dialog-message").append("<span id=logoffTimeout/>");
    

    You should replace myForm with the ID you've used on your form here.

    When the dialog is shown, calculate the time your logoff timer will expire and store it for example in window.logoffTime. Now you can use the following function to update the timer:

    function updateTimer(){
      var seconds = Math.ceil((window.logoffTime - new Date().getTime()) / 1000);
      $("#logoffTimeout").html(seconds);
    }
    

    On your buttons and remote command I would suggest to use process="@this". See also: Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes

    What you should know is when you start a timeout in JavaScript, an ID is returned. You can use that ID to clear your timeout.

    I ended up with this XHTML:

    <p:confirmDialog id="confirmDialog"
                     message="Please click Ok before the timer runs out: "
                     header="Are you there?"
                     severity="alert"
                     closable="false"
                     widgetVar="idleDialog">
      <p:commandButton id="confirm"
                       value="Ok"
                       process="@this"
                       onclick="clearTimeout(window.logoffTimeoutId); PF('idleDialog').hide();"/>
    </p:confirmDialog>
    
    <p:remoteCommand name="terminateIdleSession"
                     actionListener="#{idleMonitorView.onIdle}"
                     process="@this"
                     out="count"/>
    
    <p:idleMonitor timeout="#{5 * 60 * 1000}"
                   onidle="startTimer()"/>
    

    And this JavaScript:

    function startTimer() {
      clearTimeout(window.logoffUpdaterId);
      PF('idleDialog').show();
      // Set timeout to 2 minutes
      var timeout = 2 * 60 * 1000;
      // Calculate when the time runs out
      window.logoffTime = new Date().getTime() + timeout;
      // Start timer which calls remote command
      window.logoffTimeoutId = setTimeout(terminateIdleSession, timeout);
      // Update timer every second
      window.logoffUpdaterId = setInterval(updateTimer, 1000);
      // Update timer now
      updateTimer();
    }
    
    // Update the timer
    function updateTimer() {
      var seconds = Math.ceil((window.logoffTime - new Date().getTime()) / 1000);
      $("#logoffTimeout").html(seconds);
    }
    
    // Create span to contain the timer
    $(function(){
      $("#myForm\\:confirmDialog .ui-confirm-dialog-message").append("<span id=logoffTimeout/>");
    });
    

    I've noted that you are using your JavaScript embedded in your XHTML. In that case add CDATA to prevent running into XML errors:

    <script type="text/javascript">
    //<![CDATA[
    ...
    //]]>
    </script>
    

    I would suggest to load your script from a file / resource which will give you the benefit of caching.

    2024 update

    PrimeFaces Extensions now has a component to deal with session expiry.

    <pe:session onexpire="PF('sessionDialog').show()"
                onexpired="PF('sessionDialog').hide();PF('sessionExpiredDialog').show()"
                reactionPeriod="60"/>
    
    <p:dialog widgetVar="sessionDialog" header="Session Warning">
        <p>Your session is about to expire.</p>
        <h:form>
            <p:commandButton value="Keep my session alive." onsuccess="PF('sessionDialog').hide()"/>
        </h:form>
    </p:dialog>
    
    <p:dialog widgetVar="sessionExpiredDialog" header="Session Expired">
        <p>Your session has expired.</p>
    </p:dialog>
    

    See https://www.primefaces.org/showcase-ext/sections/session/basic.jsf