Kuali Rice Development
  1. Kuali Rice Development
  2. KULRICE-10302

Analysis - Results not on the current page are not returned by multivalue lookup's "return selected" action

    Details

    • Type: Task Task
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.4
    • Component/s: Analysis, KNS Equivalency
    • Security Level: Public (Public: Anyone can view)
    • Labels:
      None
    • Similar issues:
      KULRICE-3219Move 'return selected' button location on multivalue lookups
      KULRICE-7223Multivalue lookup not returning results
      KULRICE-7256Multivalue lookup return not refreshing
      KULRICE-11214MultiValue Lookup - select only on this page and then deselect only on this page the return select button stays clickable.
      KULRICE-124012.4.0 CDT: Multivalue Lookup returned incident report on return selected
      KULRICE-10535Multivalue Global Hander Disable/Enable of return selected button
      KULRICE-9914Hitting certain buttons on a multivalue Lookup Results page after items have been selected results in a stack trace
      KULRICE-9938Field values for a multivalue lookup aren't returned correctly if lookup results fields are null
      KULRICE-10977Multi Value Lookup : Implement server side paging for both rich and non rich tables when data selected is not on current page
      KULRICE-3616Return Selected button on multivalue lookup has black border
    • Rice Module:
      KRAD
    • KRAD Feature Area:
      Multi-Value Lookup
    • Sprint:
      2.4.0-m2 KRAD Sprint 4
    • KAI Review Status:
      Not Required
    • KTI Review Status:
      Not Required
    • Code Review Status:
      Not Required
    • Include in Release Notes?:
      Yes

      Description

      If a user opens a multivalue lookup, selects all rows available, and some of the rows are not on the current page (for instance, a results set limit size of 10 is being used, and there are 25 results), the rows on the current page will be returned successfully, but the rows on non-displayed pages will not be returned to the original form.

      Do the requisite analysis to determine what needs to be done.

      1. DemoCollectionLookup.xml
        12 kB
        Sona Sona
      2. krad_widget_js_.patch
        1 kB
        Sona Sona

        Issue Links

          Activity

          Hide
          Jerry Neal (Inactive) added a comment -

          Hi Sona,

          Good point. We need to figure out the binding behavior here as well. The checkboxes bind to an entry in the Map property: protected Map<String, Set<String>> selectedCollectionLines;. When the return selected button is clicked, the form will post to the server. The checkbox state of all the lines present in the DOM will then update the selectedCollectionLines map. If the checkbox is selected, the value will get added (since the value is already there and it is a set it will not actually get added). Spring also keeps track of whether a checkbox was present, and if unselected will remove the value. The question is, whether spring initializes a new Set for the value before it does the population, I don’t think it does. So if the select all was chosen before, the values for all the lines will be added to this property by a server side method. Then just the checkbox values for the visible page should update when return selected is chosen. If all this is correct, it would work out. The value for the one checkbox that was unselected will be removed, and the other values will remain. If this is not the case, I agree going with the client side solution would be best.

          Thanks,
          Jerry

          Show
          Jerry Neal (Inactive) added a comment - Hi Sona, Good point. We need to figure out the binding behavior here as well. The checkboxes bind to an entry in the Map property: protected Map<String, Set<String>> selectedCollectionLines;. When the return selected button is clicked, the form will post to the server. The checkbox state of all the lines present in the DOM will then update the selectedCollectionLines map. If the checkbox is selected, the value will get added (since the value is already there and it is a set it will not actually get added). Spring also keeps track of whether a checkbox was present, and if unselected will remove the value. The question is, whether spring initializes a new Set for the value before it does the population, I don’t think it does. So if the select all was chosen before, the values for all the lines will be added to this property by a server side method. Then just the checkbox values for the visible page should update when return selected is chosen. If all this is correct, it would work out. The value for the one checkbox that was unselected will be removed, and the other values will remain. If this is not the case, I agree going with the client side solution would be best. Thanks, Jerry
          Hide
          Sona Sona (Inactive) added a comment -

          Hi Jerry,

          I thought more about it also and found a flaw in our design. On a 5 page result with server side paging "ON", if the user clicks on "Select All", we would make a note on the server side as you suggested and update the client to select all the check boxes on the visible page. Now if the user deselects a single item from that page and hits return the current solution will break as that action would cause the "Select All" to be reset on the server side but really the user expects all but one item to be selected. Since the user has not visited any other page, the client does not know of those values. The more I think about this the more I realize that we need either a full client side solution or a full server side solution.

          In a full client side solution when the select all is checked, instead of noting server side of that choice, we will pull all the values from the server and add it to the data attribute you suggested. After this everything will work as noted by your email for a client side solution. In this there is a one time performance hit when the user clicks on select all. The impact could be quite big based on the result set size, but only if the user decides to click on select all. To reduce the impact I was thinking using JSON response with only values of the select control.

          In the full server side solution every click will have to be recorded server side and then a return selected is processed from values stored server side. This will have a higher performance impact for every click by making round trip server requests for select and deselect and potentially impact the user experience as they click through the check boxes.

          Please let me know how you would like me to proceed? As you said it is a tricky issue .

          Thanks

          Show
          Sona Sona (Inactive) added a comment - Hi Jerry, I thought more about it also and found a flaw in our design. On a 5 page result with server side paging "ON", if the user clicks on "Select All", we would make a note on the server side as you suggested and update the client to select all the check boxes on the visible page. Now if the user deselects a single item from that page and hits return the current solution will break as that action would cause the "Select All" to be reset on the server side but really the user expects all but one item to be selected. Since the user has not visited any other page, the client does not know of those values. The more I think about this the more I realize that we need either a full client side solution or a full server side solution. In a full client side solution when the select all is checked, instead of noting server side of that choice, we will pull all the values from the server and add it to the data attribute you suggested. After this everything will work as noted by your email for a client side solution. In this there is a one time performance hit when the user clicks on select all. The impact could be quite big based on the result set size, but only if the user decides to click on select all. To reduce the impact I was thinking using JSON response with only values of the select control. In the full server side solution every click will have to be recorded server side and then a return selected is processed from values stored server side. This will have a higher performance impact for every click by making round trip server requests for select and deselect and potentially impact the user experience as they click through the check boxes. Please let me know how you would like me to proceed? As you said it is a tricky issue . Thanks
          Hide
          Sona Sona (Inactive) added a comment -

          Jerry's thoughts:

          I gave this some more thought as well. Let me first take the problem of getting all the records selected for ‘select all’. Since you don’t have all the records on the client, I think there needs to be a server call made. The server call would get all the results for collection, add the keys to the selected keys map. This would only be done when they choose ‘select all’ from the header (we can refresh the collection group so the page checkboxes become selected).

          For enabling/disabling the return selected button, I was thinking something similar. You can keep an array of selected checkbox ids in the data for the document (jQuery data). So on the checkbox handler, if the value is checked add the id to the data array, if unchecked remove the id. Then have a check to see if there are any items in the data array and enable or disable the button accordingly. The data array will stay around as long as the page is not fully refreshed. Then you can also use this array to repopulate checkboxes when a page occurs. An alternative approach is look at how we might pass additional data to the paging call, so the selected items for the current page get passed and populated client side, then they will be correct when paged back.

          There is a still a slight issue I see, full page refreshes. All the checkbox state will be posted so they selected items will appear correctly on refresh, but the array will be lost and the button disabled as a consequence. One way to solve this will be on load to iterate through the checkboxes and initialize the array.

          This is a tricky issue!

          Show
          Sona Sona (Inactive) added a comment - Jerry's thoughts: I gave this some more thought as well. Let me first take the problem of getting all the records selected for ‘select all’. Since you don’t have all the records on the client, I think there needs to be a server call made. The server call would get all the results for collection, add the keys to the selected keys map. This would only be done when they choose ‘select all’ from the header (we can refresh the collection group so the page checkboxes become selected). For enabling/disabling the return selected button, I was thinking something similar. You can keep an array of selected checkbox ids in the data for the document (jQuery data). So on the checkbox handler, if the value is checked add the id to the data array, if unchecked remove the id. Then have a check to see if there are any items in the data array and enable or disable the button accordingly. The data array will stay around as long as the page is not fully refreshed. Then you can also use this array to repopulate checkboxes when a page occurs. An alternative approach is look at how we might pass additional data to the paging call, so the selected items for the current page get passed and populated client side, then they will be correct when paged back. There is a still a slight issue I see, full page refreshes. All the checkbox state will be posted so they selected items will appear correctly on refresh, but the array will be lost and the button disabled as a consequence. One way to solve this will be on load to iterate through the checkboxes and initialize the array. This is a tricky issue!
          Hide
          Sona Sona (Inactive) added a comment -

          There are three issues that I am looking at which are intertwined and related to multivalue lookup.

          1. With pagination on the lookup results, only selections from the visible page are returned
          2. When server side paging is enabled, selection from the non visible pages are not retained as you move through the pages.
          3. With server side paging and non dataTable rendering "Return Selected" button does not get enabled when clicking on a checkbox.

          Currently in multi value lookup, the results are displayed client side (no server paging) using dataTables. All the javascript functions to enable return selected, process result submission assume that there is a dataTable in the DOM.

          I have setup examples to enable server side paging both with and without dataTable by providing a custom viewName to the quickFinder. I have attached the XML for reference.

          For item 1. The pagination causes the dataTable to remove all rows from the DOM except the ones on the visible page. So, dataTables need to be handled differently in javascript, so that it can use the plugin api to extract the selected records from non visible page. This is also why we can't use the jQuery class based selector for retrieving selected checkbox input fields.

          For item 2. Server side paging with or without dataTable causes only one page worth of content to be present in the DOM. Selection made client side are not being saved anywhere to be accessed later as the user pages through the results.

          For item 3. onChange event listener is written only for dataTable based rendering of results.

          I can think of two solutions

          1. Use a client side JSON object to store the user selections. The JSON object won't be replaced as part of component refresh in case of server side paging. We can then use the JSON object to calculate if 'Return Selected' button should be enabled and also to populate hidden variables on submit. In this approach, once a page is retrieved from the server, we would have to reset any selections that were present on the page from the JSON object. This solution will work for both serverPaging and default paging provided by dataTable.

          2. In the other solution, for every select/deselect event a call will be made to a controller method that will record the event server side. This will fix the issue of preserving selections as the user moves through different pages. To solve the problem of retaining the right state for "Return Selected" button we would need to use a counter logic instead of boolean that is there right now. With every selection we increment the counter and with every deselection we decrement the counter. If the counter is at 0, we disable the button else the button stays enabled. This way even if the user moves to a new page the button would know what state it is in. In this solution on submit, instead of reading the selections from request, the collection stored on the server needs to be read to identify which line items need to be processed.

          Also for solution 2, the JS function 'handleCheckboxLabelClick' needs to be updated so that it sends a request to the server if server paging is enabled.

          Solution 1, has lower performance impact as it would not require a server call for every select/deselect. It is also a single solution that can be applied to all use cases (client side dataTable only, with server paging and dataTable and with server paging without dataTable)

          Show
          Sona Sona (Inactive) added a comment - There are three issues that I am looking at which are intertwined and related to multivalue lookup. 1. With pagination on the lookup results, only selections from the visible page are returned 2. When server side paging is enabled, selection from the non visible pages are not retained as you move through the pages. 3. With server side paging and non dataTable rendering "Return Selected" button does not get enabled when clicking on a checkbox. Currently in multi value lookup, the results are displayed client side (no server paging) using dataTables. All the javascript functions to enable return selected, process result submission assume that there is a dataTable in the DOM. I have setup examples to enable server side paging both with and without dataTable by providing a custom viewName to the quickFinder. I have attached the XML for reference. For item 1. The pagination causes the dataTable to remove all rows from the DOM except the ones on the visible page. So, dataTables need to be handled differently in javascript, so that it can use the plugin api to extract the selected records from non visible page. This is also why we can't use the jQuery class based selector for retrieving selected checkbox input fields. For item 2. Server side paging with or without dataTable causes only one page worth of content to be present in the DOM. Selection made client side are not being saved anywhere to be accessed later as the user pages through the results. For item 3. onChange event listener is written only for dataTable based rendering of results. I can think of two solutions 1. Use a client side JSON object to store the user selections. The JSON object won't be replaced as part of component refresh in case of server side paging. We can then use the JSON object to calculate if 'Return Selected' button should be enabled and also to populate hidden variables on submit. In this approach, once a page is retrieved from the server, we would have to reset any selections that were present on the page from the JSON object. This solution will work for both serverPaging and default paging provided by dataTable. 2. In the other solution, for every select/deselect event a call will be made to a controller method that will record the event server side. This will fix the issue of preserving selections as the user moves through different pages. To solve the problem of retaining the right state for "Return Selected" button we would need to use a counter logic instead of boolean that is there right now. With every selection we increment the counter and with every deselection we decrement the counter. If the counter is at 0, we disable the button else the button stays enabled. This way even if the user moves to a new page the button would know what state it is in. In this solution on submit, instead of reading the selections from request, the collection stored on the server needs to be read to identify which line items need to be processed. Also for solution 2, the JS function 'handleCheckboxLabelClick' needs to be updated so that it sends a request to the server if server paging is enabled. Solution 1, has lower performance impact as it would not require a server call for every select/deselect. It is also a single solution that can be applied to all use cases (client side dataTable only, with server paging and dataTable and with server paging without dataTable)
          Hide
          Sona Sona (Inactive) added a comment - - edited

          Attached is a patch file with the above code. Created a review for the same https://fisheye.kuali.org/cru/rice-300

          Show
          Sona Sona (Inactive) added a comment - - edited Attached is a patch file with the above code. Created a review for the same https://fisheye.kuali.org/cru/rice-300
          Hide
          Sona Sona (Inactive) added a comment - - edited

          Data table only retains elements on the visible page within the DOM. To be able to preserve selections from hidden pages, we need to extract those elements from the datatable and re-insert them back into the form
          http://datatables.net/forums/discussion/185/submitting-forms-with-fields-on-hidden-pages/p1

          A solution has been proposed at this link
          http://datatables.net/examples/api/form.html

          Based on the above solution I have added this logic to the setupMultiValueReturn() in krad.widgets.js. This is a presubmit call to the return selected action. Here is the code:

            // Find all the input type: hidden elements in the data table
              var oTable = jQuery('.dataTable').dataTable();
              var sData = jQuery('input:hidden', oTable.fnGetNodes()).serializeArray();
          
              // For each hidden element insert it back to the form
              jQuery.each(sData, function(i, field){
          
                  jQuery('<input>').attr({
                      type: 'hidden',
                      id: field.id,
                      name: field.name,
                      value: field.value
                  }).appendTo('#'+kradVariables.KUALI_FORM);    });
          
          

          I tested it locally and the solution works for MVL returns from multiple pages.

          Show
          Sona Sona (Inactive) added a comment - - edited Data table only retains elements on the visible page within the DOM. To be able to preserve selections from hidden pages, we need to extract those elements from the datatable and re-insert them back into the form http://datatables.net/forums/discussion/185/submitting-forms-with-fields-on-hidden-pages/p1 A solution has been proposed at this link http://datatables.net/examples/api/form.html Based on the above solution I have added this logic to the setupMultiValueReturn() in krad.widgets.js. This is a presubmit call to the return selected action. Here is the code: // Find all the input type: hidden elements in the data table var oTable = jQuery('.dataTable').dataTable(); var sData = jQuery('input:hidden', oTable.fnGetNodes()).serializeArray(); // For each hidden element insert it back to the form jQuery.each(sData, function(i, field){ jQuery('<input>').attr({ type: 'hidden', id: field.id, name: field.name, value: field.value }).appendTo('#'+kradVariables.KUALI_FORM); }); I tested it locally and the solution works for MVL returns from multiple pages.

            People

            • Assignee:
              Sona Sona (Inactive)
              Reporter:
              Steve Manning (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 1 day, 4 hours Original Estimate - 1 day, 4 hours
                1d 4h
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 2 days
                2d

                  Agile

                    Structure Helper Panel