8

I'm executing the following jQuery function on <p:dataTable> filter (whose id is id) that allows users to enter only digits in the filter component.

$(document).ready(function() {
    $("#form\\:dataTable\\:id\\:filter").keydown(function(event) {
        //Allow: tab, escape, and enter
        if(event.keyCode===9||event.keyCode===27||event.keyCode===13||
            //Allow: Ctrl+A, Ctrl+C
            (event.keyCode===65&&event.ctrlKey===true)||(event.keyCode===67&&event.ctrlKey===true)||
            //Allow: home, end, left, right
            (event.keyCode>=35&&event.keyCode<=39)){
            //let it happen, don't do anything
            event.preventCapture();
            return;
        }//backspace, delete
        else if(event.keyCode===46||event.keyCode===8)
        {
            return;
        }
        else{//Ensure that it is a number and stop the keypress
            if (event.shiftKey||(event.keyCode<48||event.keyCode>57)&&(event.keyCode< 96||event.keyCode>105)){
                //event.preventDefault();
                event.preventCapture();
            }
        }
    });
});

This function is placed under context/resources/default/js/digit_only_textfield.js. Therefore, it can be used on XHTML pages like,

<h:outputScript library="default" name="js/digit_only_textfield.js"/>

The XHTML page looks like the following.

<h:outputScript library="default" name="js/digit_only_textfield.js"/>

<h:form id="form" prependId="true">

    <!--PrimeFaces extension <pe:blockUI>-->         

    <p:remoteCommand name="updateTable" update="dataTable"/>

    <p:panel id="panel">
        <h:panelGrid id="panelGrid" columns="3" cellpadding="5">

            <!--Some UIInput components-->

            <p:commandButton id="btnSubmit" 
                             update="panel" 
                             onstart="PF('blockUIWidget').block();" 
                             oncomplete="if(!args.validationFailed) {updateTable();}PF('blockUIWidget').unblock();" 
                             actionListener="#{bean.insert}" 
                             value="Save"/>
        </h:panelGrid>
    </p:panel>

    <p:dataTable id="dataTable" 
                 var="row" 
                 value="#{bean}"
                 filterEvent="keydown"

                 ...
                 ...  >

                 ...
                 ...
    <p:dataTable>
<h:form>

This jQuery works fine for the filter whose id is is but when this <p:dataTable> is updated by pressing the given <p:commandButton>, it stops functioning.

How to make this function work after <p:dataTable> is updated by AJAX?


A new problem domain introduced:

This question and corresponding replies on the PrimeFaces Community Forum still do not lead to a workaround/solution to the following problem domain.

if a wrong key is hit (i.e a non-digit key, except backspace, delete etc) then, the data table is updated unnecessarily that causes some costly queries to be fired upon the database which is completely unnecessary and the data table must be prevented from being updated.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Tiny
  • 27,221
  • 105
  • 339
  • 599
  • Have you tried running the jQuery `keydown` function again after the table is updated? If the table is rendered from scratch by PrimeFaces upon update, then the jQuery on event trigger would be erased. – patstuart Nov 26 '13 at 22:12
  • It is just a filter event in PrimeFaces data table - an attribute of ``, `filterEvent="keydown"`. – Tiny Nov 26 '13 at 22:16
  • I don't understand the bounty and the apparent new problem. – BalusC Dec 09 '13 at 00:51
  • I could have asked a separate question but mostly it would have been the same scenario except the text in the block quote in the question. – Tiny Dec 09 '13 at 09:32
  • Isn't that already the whole intent of your custom jQuery function? Or didn't that part work? – BalusC Dec 09 '13 at 16:46
  • @BalusC : This question has a sufficient answer. There is no doubt at all about it. There is however, just one thing that came after the answer (and therefore, it was not covered in the question). The filter should accept only digits. All other characters (except backspace, delete, home etc) should be disallowed and it should function after the data table is updated. That's done. The new thing that I can't figure out is that when non-digit characters are attempted, the data table is unnecessarily updated that shouldn't happen. Shouldn't it? (it causes the model to be updated unnecessarily). – Tiny Dec 09 '13 at 17:17

1 Answers1

9

The flow is as follows:

  • Browser retrieves HTML output.
  • Browser populates HTML DOM tree based on HTML markup.
  • When finished, browser triggers HTML DOM ready event.
  • jQuery's $(document).ready() function handlers are all invoked.
  • Yours finds an element in DOM by ID and attaches a keydown listener to it.
  • During user interaction, an ajax request is fired and the HTML DOM tree is updated with new HTML elements delivered by the ajax response.
  • Among others, exactly that element having the keydown listener is removed from HTML DOM tree and replaced by a fresh new element without any keydown listener. The document ready event is not fired during ajax requests. Your ready handler is never re-invoked. The keydown listener is never re-attached. To the enduser, it then indeed seemingly "stops functioning".

The solution on this particular case should now be obvious: explicitly re-attach the keydown listener on complete of ajax call. Most straightforward would be to extract the job of attaching the keydown listener into a reusable function and fire it as follows:

function applyKeydownOnTableFilter() { 
    // ...
}

$(document).ready(applyKeydownOnTableFilter);

So that you can just do a:

<p:commandButton ... oncomplete="applyKeydownOnTableFilter()" />

But this is quite tedious to repeat for every single ajax command/listener and not very maintenance friendly. Better is to approach it differently: use jQuery's $.on() instead. Replace

$(document).ready(function() {
    $("#form\\:dataTable\\:id\\:filter").keydown(function(event) {
        // ...
    });
});

by

$(document).on("keydown", "#form\\:dataTable\\:id\\:filter", function(event) {
    // ...
});

This way the keydown listener isn't actually attached to the element of interest. Instead, thanks to the event bubbling feature of JavaScript, the keydown event will ultimately reach the $(document) — which is always present and usually not changed during JSF ajax requests. Once reached, the $(document).on() is triggered, which will then determine the source of the event and check it if matches the given selector and if so, then invoke the function. This all without the need to attach the keydown listener to the physical element and thus not sensitive to whether the element is removed/replaced in the HTML DOM tree.

See also:


By the way, do you also see how much similarities there are between HTML DOM tree and JSF component tree?

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks as always. The approach with `$.on()` works as intended. There a small thing, however that I cannot figure out. While using `$.on()`, if a wrong key is hit (i.e any non-digit key) then, the data table is updated unnecessarily that causes some costly queries to be executed on the database which is completely unnecessary. Is there any workaround that can prevent the data table from being updated, when a wrong key is hit. This should be done alone by `event.preventDefault();` but it does not. While using `$(document).ready(function() {});` as in the question, this did not happen. – Tiny Nov 27 '13 at 14:41
  • Oh right :( PrimeFaces doesn't use directly the `onkeydown` attribute for this but also attaches an event handler by jQuery. This is fired *before* your event handler. So, basically, you're too late ... Sorry, you've to switch back to `ready` event so that yours is attached first. To minimize the `oncomplete` mess, you may want to perform the job in a page-wide manner by `` or perhaps `$.ajaxStop()`. – BalusC Nov 27 '13 at 16:20
  • As to the concrete requirements of this question, [PrimeFaces 5.0](http://primefaces.org/downloads) (currently available as a release candidate - RC1) comes with handy data table filters There is a [major enhancement](http://blog.primefaces.org/?p=3084) in [data table filters](http://www.primefaces.org/showcase-labs/ui/datatableFiltering.jsf). Therefore, such client side validations on data table filters can completely be eliminated :) – Tiny Apr 21 '14 at 00:36