2 Replies Latest reply on Sep 9, 2010 11:34 AM by Adrian Moos

    dataScroller: concurrent clicks with stateful tables

    Adrian Moos Newbie

      I have a rich:dataTable containing input components (in our case, comboboxes) in each row, and an attached rich:dataScroller. Since we want to submit changes made to the comboboxes, the dataScroller's ajaxSingle is false.

       

      This works fine - until the users click the the dataScroller faster than the server can answer the AJAX request. Then, the second request fails with a validation error. Here's a slightly sanitized output of a4j:log:

       

      debug[14:40:48,272]: Have Event [object Object] with properties: target: [object HTMLTableCellElement], srcElement: undefined, type: dataavailable
      debug[14:40:48,272]: Query preparation for form 'ourForm requested'
      debug[14:40:48,272]: Append  hidden control ...
      (more controls)
      debug[14:40:48,276]: Look up queue with default name
      debug[14:40:48,276]: Creating  new transient queue  'dataScroller'  with default settings
      debug[14:40:48,276]: Adding queue 'dataScroller' to queues registry
      debug[14:40:48,277]: Queue is empty now
      debug[14:40:48,277]: New  request added to queue  'dataScroller'.  Queue similarityGroupingId changed to  dataScroller
      debug[14:40:48,277]: Queue will wait 0ms before submit
      debug[14:40:48,277]: Queue 'dataScroller' will submit request NOW
      debug[14:40:48,277]: NEW AJAX REQUEST !!! with form: ourForm
      debug[14:40:48,277]: Start XmlHttpRequest
      debug[14:40:48,278]: Reqest state : 1
      debug[14:40:48,278]: QueryString: ...
      debug[14:40:48,278]: Reqest state : 1
      debug[14:40:48,464]: Have Event [object Object] with properties: target: [object HTMLTableCellElement], srcElement: undefined, type: dataavailable
      debug[14:40:48,464]: Query preparation for form 'ourForm requested'
      debug[14:40:48,464]: Append  hidden control ...
      (more controls)
      debug[14:40:48,467]: Look up queue with default name
      debug[14:40:48,467]: Found transient queue 'dataScroller'
      debug[14:40:48,468]: Similar request currently in queue 'dataScroller'
      debug[14:40:48,468]: Request has already beeen sent to server
      debug[14:40:48,468]: Duplicate responses ignore requested
      debug[14:40:48,468]: Response for the current request will be ignored
      debug[14:40:48,468]: New  request added to queue  'dataScroller'.  Queue similarityGroupingId changed to dataScroller
      debug[14:40:48,468]: Queue will wait 0ms before submit
      debug[14:40:48,525]: Reqest state : 2
      debug[14:40:48,542]: After request: queue 'dataScroller'
      debug[14:40:48,542]: There are 1 requests more in this queue
      debug[14:40:48,542]: Queue 'dataScroller' will submit request NOW
      debug[14:40:48,542]: NEW AJAX REQUEST !!! with form: ourForm
      debug[14:40:48,542]: Start XmlHttpRequest
      debug[14:40:48,542]: Reqest state : 1
      debug[14:40:48,543]: QueryString:   ...
      As you can see, the requests are queued and sent sequentially. They are also identical, i.e. they both send the state for the comboboxes of the first page to the server. However, processing the first page creates the combobox-components for the second page, and when the second request is received, the server is missing values for these components, causing validation to fail.
      Ideally, the dataScroller would construct the second request after the first has been processed, so it could submit the state of the newly arrived components. Is there a way I can achieve that?
      Realistically, I would be happy if too rapid clicks into the datascroller would be ignored. But it appears there is no JavaScript-API to disable the dataScroller? Must I really resort to navigating the HTML elements it renders and remove the onclick-handler?
      Any other ideas?
      PS: I am using richfaces-3.3.1.GA
        • 1. Re: dataScroller: concurrent clicks with stateful tables
          Ilya Shaikovsky Master

          you could set some request delay in order to combine the fast clicks before the first one sent. also you could for example block the screen with the popup for example using a4j:status after the request actually sent.

          1 of 1 people found this helpful
          • 2. Re: dataScroller: concurrent clicks with stateful tables
            Adrian Moos Newbie

            Thank you.

             

            Time to rerender is about 500 ms; I'd rather not set a request delay this high.

            On the other hand, showing a popup for half a second seems overkill, too.

             

            So I went with patching the onclick-handler:

             

            /**
            * Patches the onclick-handlers of the supplied rich:dataScroller to only react
            * once (until the component is rerendered). This ensures at most one scroll
            * request is pending at any given time, working around the bug that the
            * datascroller prepares the second request when the second click occurs, which,
            * if it occurs before the DOM is updated with the components from the page
            * scrolled to by the first click, submits the state of the wrong page. If the
            * pages contain no input components, this works as intended. But if not ...
            *
            * Usage example:
            *     onlyScrollOnce(#{rich:element('myDataScroller')});
            */
            function onlyScrollOnce(dataScrollerElement) {
                var tr = dataScrollerElement.childNodes[0].childNodes[0].childNodes[0];
                for (var i = 0; i < tr.childNodes.length; i++) {
                    var td = tr.childNodes[i];
                    td.originalOnClick = td.onclick;
                    td.onclick = function() {
                        this.onclick = null;
                        this.originalOnClick();
                    }
                }
            }

            1 of 1 people found this helpful