How is your experience with the OutSystems Community?
36
Views
12
Comments
Solved
How to get a list paginated with checkboxes for the user to select one or two... n
Question

Hello, I am trying for a long time, to create a list with pagination that fetch data from my database, with checkboxes in each row as well as one for all. I'd like to learn how to let the user to check one two .... n or all the check boxes, navigate between pages and maintain the rows previously selected. Could you help me teaching me to do this, please?

(Already I've add an attribute "IsSelected" to my aggregate for the list)

2026-07-23 14-34-51
Nuno Reis
 
MVP
Solution

Here is an example of what Dorine said.

I used the Sample Products database.

Notice the OnAfterFetch, the OnClick, and the Check All actions.

SelectMultiple.oml
2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

Hi @Maria Victoria de Lucas Vaquero ,

this is a nice challenge, and not too easy.  I did this last year, with the added complexity that the list had 3 levels, and that each parent checkbox influenced all children and grandchildren and vice versa, that got real nasty real fast.

You will need several things :

- an isSelected boolean coming out of your aggregate (which you already have)

- a local boolean IsAllSelected

- a local list that holds ALL items (id + isSelected) regardless of what page you are currently on.  So a separate aggregate without maxrecords.

- on each checkbox change event (both in a row as at the top) you need to execute a screen action to keep your local list up to date AND to keep your checkboxes in sync.  That will look something like

  • if top is set ON, set all in current visible subset to ON, set all in local list ON
  • if top is set OFF, set all in current visible subset OFF, set all in local list OFF
  • if line is set ON, set in local list to ON, if all in local list are now ON, set top to ON
  • if line is set OFF, set in local list to OFF, set top to OFF

Every time you go to another page, you will have to take into account the local list in displaying the retrieved page.   You probably could make that work by passing the local list into your query (e.g. an advanced sql with a case and an in clause).  Or much easier, postprocess your query result by checking the 10 retrieved records against that local list to set the isSelected correctly on them.  

I would personally do that in a data action after the aggregate, but you could also do it in the OnAfterFetch of the aggregate.

This approach will allow to paginate and to sort without forgetting what is selected, but i would stay away from combining an selectAll with filtering, as i think it makes it unclear what the All in selectAll means then.  Even without selectAll, filtering adds confusion, because there might be items selected that don't fit the filter, what to do with those ???

This is already a lot, maybe have a go at it, and then come back with your OML if you still run into problems.

Dorine

To take it a little step further, you could replace the boolean for the selectAll by a Trilean.

In that case, your logic will change slightly

  • if line is set ON, set in local list to ON, 
    • if all in local list are now ON, set top to ON, otherwise set top to Undetermined
  • if line is set OFF, set in local list to OFF, 
    • if all in local list are now OFF, set top to OFF, otherwise set top to Undetermined
2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

You will also have to figure something out for the race condition of the local list not being populated yet by the time your OnAfterFetch runs.

I'm personally not a fan of doing things in OnAfterFetch that can be done server side, so i would probably make a server action to fetch 5 questions and set the boolean correctly, and return the correct boolean from the server, so no need for an OnAfterFetch.

If you reference the list with all id's in that DataAction, if I'm not mistaken, it will wait until the list is fetch before running for the first time.

Dorine

2018-07-06 11-13-55
Nathan Hobbs
Solution

Hi Maria,

I would like to add to the excellent solution above, that this may have performance issues if their are a lot of rows as you are storing that list in the viewstate.... it's worth seeing if you can filter the data.

Also the local list needs only hold the IDs that are in the list... add if selected, remove if not selected... this will help reduce the size of the list.

Nathan

2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

Only holding the selected in the local list doesn't work with a select all checkbox at the top.

Oh and yes, performance can be a thing, but I don't think this checkbox pattern would work for a large dataset from a UX point of view anyway.

Dorine

2018-07-06 11-13-55
Nathan Hobbs
Solution


Yeah, I agree, for the select all you'd need to rerun the agg to fetch all records if you tick it, and do a list clear if you untick.

It would be interesting to see if there is a difference in performance between a list of longs and a list of records with a long and a boolean, and also the difference when you have to search the entire list when you tick it vs just appending an item.

It's probably going to be entirely down to the number of records, and, as you say, it's going to be ugly if there is a large dataset anyway.

2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

Only holding the selected in the local list doesn't work with a select all checkbox at the top.

Oh and yes, performance can be a thing, but I don't think this checkbox pattern would work for a large dataset from a UX point of view anyway.

Dorine

2018-07-06 11-13-55
Nathan Hobbs
Solution


Yeah, I agree, for the select all you'd need to rerun the agg to fetch all records if you tick it, and do a list clear if you untick.

It would be interesting to see if there is a difference in performance between a list of longs and a list of records with a long and a boolean, and also the difference when you have to search the entire list when you tick it vs just appending an item.

It's probably going to be entirely down to the number of records, and, as you say, it's going to be ugly if there is a large dataset anyway.

2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

Hi @Maria Victoria de Lucas Vaquero ,

this is a nice challenge, and not too easy.  I did this last year, with the added complexity that the list had 3 levels, and that each parent checkbox influenced all children and grandchildren and vice versa, that got real nasty real fast.

You will need several things :

- an isSelected boolean coming out of your aggregate (which you already have)

- a local boolean IsAllSelected

- a local list that holds ALL items (id + isSelected) regardless of what page you are currently on.  So a separate aggregate without maxrecords.

- on each checkbox change event (both in a row as at the top) you need to execute a screen action to keep your local list up to date AND to keep your checkboxes in sync.  That will look something like

  • if top is set ON, set all in current visible subset to ON, set all in local list ON
  • if top is set OFF, set all in current visible subset OFF, set all in local list OFF
  • if line is set ON, set in local list to ON, if all in local list are now ON, set top to ON
  • if line is set OFF, set in local list to OFF, set top to OFF

Every time you go to another page, you will have to take into account the local list in displaying the retrieved page.   You probably could make that work by passing the local list into your query (e.g. an advanced sql with a case and an in clause).  Or much easier, postprocess your query result by checking the 10 retrieved records against that local list to set the isSelected correctly on them.  

I would personally do that in a data action after the aggregate, but you could also do it in the OnAfterFetch of the aggregate.

This approach will allow to paginate and to sort without forgetting what is selected, but i would stay away from combining an selectAll with filtering, as i think it makes it unclear what the All in selectAll means then.  Even without selectAll, filtering adds confusion, because there might be items selected that don't fit the filter, what to do with those ???

This is already a lot, maybe have a go at it, and then come back with your OML if you still run into problems.

Dorine

To take it a little step further, you could replace the boolean for the selectAll by a Trilean.

In that case, your logic will change slightly

  • if line is set ON, set in local list to ON, 
    • if all in local list are now ON, set top to ON, otherwise set top to Undetermined
  • if line is set OFF, set in local list to OFF, 
    • if all in local list are now OFF, set top to OFF, otherwise set top to Undetermined
2026-07-23 14-34-51
Nuno Reis
 
MVP
Solution

Here is an example of what Dorine said.

I used the Sample Products database.

Notice the OnAfterFetch, the OnClick, and the Check All actions.

SelectMultiple.oml
2023-12-04 11-42-59
Maria Victoria de Lucas Vaquero

Thanks all of you!

I got it following Dorine's procedure and Nuno's. oml, unvaluable help for me!

There is yet something in my screen ("PruebaCheckBoxes") that does not work fully, because when user checks all, and after that uncheck one or two ... in firts page (for example), and user navigates to other page (it can be the 3rd or 4th ... is random) this page has been fully unchecked, but only these two random pages, the other 8 works coherently), that is what I share here the .oml in case somebody can have a look and reveals what I am doing wrong:

MVDLV_Bookings.oml
2021-09-06 15-09-53
Dorine Boudry
 
MVP

I think you made this with ODC Studio, but unfortunately, can't open your file with either service studio or odc studio.

errors 

in O11 Service Studio

in ODC Studio

2023-12-04 11-42-59
Maria Victoria de Lucas Vaquero

oh thanks for your advice!, yes I did it in ODC. Now I have replicated in Service Studio, and attach here (the screen: "PruebaCheckBoxes")

SaltotumAppReduce.oml
2021-09-06 15-09-53
Dorine Boudry
 
MVP

Oke,

this one I can open.  It is however not possible to test, because there is dependency on missing module 'SaltotumBBDD'.

I repaired it by creating an extra entity myself.

So, the reason you are seeing this, is that your local list is not a FULL LIST of ALL id's.  Because you populate it in the OnAfterFetch, only the ids of the pages you happened to visit are there, so when check all, the id's of not yet visited pages are not checked. 

Then unchecking one, will set the checkall to False, so if you then go to a not yet visited page, at that point the checkall is False, and that id is not yet in the list, so it ends up unchecked.

You really need to do a separate Aggregate without maxrecords to populate the local list with all the id's that exist.  And then in your OnAfterFetch you would not have a ListAppend anymore.

Dorine

2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

You will also have to figure something out for the race condition of the local list not being populated yet by the time your OnAfterFetch runs.

I'm personally not a fan of doing things in OnAfterFetch that can be done server side, so i would probably make a server action to fetch 5 questions and set the boolean correctly, and return the correct boolean from the server, so no need for an OnAfterFetch.

If you reference the list with all id's in that DataAction, if I'm not mistaken, it will wait until the list is fetch before running for the first time.

Dorine

2018-07-06 11-13-55
Nathan Hobbs
Solution

Hi Maria,

I would like to add to the excellent solution above, that this may have performance issues if their are a lot of rows as you are storing that list in the viewstate.... it's worth seeing if you can filter the data.

Also the local list needs only hold the IDs that are in the list... add if selected, remove if not selected... this will help reduce the size of the list.

Nathan

2021-09-06 15-09-53
Dorine Boudry
 
MVP
Solution

Only holding the selected in the local list doesn't work with a select all checkbox at the top.

Oh and yes, performance can be a thing, but I don't think this checkbox pattern would work for a large dataset from a UX point of view anyway.

Dorine

2018-07-06 11-13-55
Nathan Hobbs
Solution


Yeah, I agree, for the select all you'd need to rerun the agg to fetch all records if you tick it, and do a list clear if you untick.

It would be interesting to see if there is a difference in performance between a list of longs and a list of records with a long and a boolean, and also the difference when you have to search the entire list when you tick it vs just appending an item.

It's probably going to be entirely down to the number of records, and, as you say, it's going to be ugly if there is a large dataset anyway.

2023-12-04 11-42-59
Maria Victoria de Lucas Vaquero

Hi all

Thanks your unvaluable explanations and tips I have reached the solution and get my task finished. 

Please, let me share here the steps taken from the beginning:

1º.- I've replicated the job of Nuno Reis: SelectMultiple.oml

2º.- I report my error and Dorine Boudry brings to my attention the key point: 

"(...) your local list is not a FULL LIST of ALL id's. Because you populate it in the OnAfterFetch, only the ids of the pages you happened to visit are there, so when check all, the id's of not yet visited pages are not checked. 

Then unchecking one, will set the checkall to False, so if you then go to a not yet visited page, at that point the checkall is False, and that id is not yet in the list, so it ends up unchecked (...) You really need to do a separate Aggregate without maxrecords to populate the local list with all the id's that exist (…)”.

3º.- Samuel Espinoza helps me with this issue of the local list filling up on each page  : 

We introduce a DataAction which takes all the questions from the server (as inside the DataAction you can use an aggregate without maxrecods) so that returns an output parameter that is the full list (I name it List3 (which as  Nathan Hobbs suggests, is a list of records with a long (QuestionId) and a boolean (IsSelected))

Now that the list is complete, the CheckAll can select the entire list and pagination can work. 

I share here my .oml 

Thanks you all again!


MVDLV_Bookings.oml
2026-03-13 15-51-13
Samuel Espinoza

Great Job @Maria Victoria de Lucas Vaquero, I’m really glad I could help. 

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.