List Widget, Pagination, and Screen Navigation
Key Takeaways
- The List widget iterates over a source (Aggregate, Data Action output, or list variable) and renders its child widget subtree once per element — the child widgets bind to List.Current to access the current row.
- Pagination is implemented via StartIndex (the zero-based offset of the first row to fetch) and Max Records (the page size) on the source Aggregate or Data Action; ListNavigate and the pagination UI widgets drive these.
- The Navigate action transitions to a target Screen, passing Input Parameters; the target Screen's OnParametersChanged fires when navigating to the same Screen with new parameter values.
- Scaffolding a List Screen from an entity auto-generates a List widget bound to an Aggregate, pagination, and per-row Edit/Delete Links navigating to edit Screens with the entity Id as Input Parameter.
- Traditional Web uses a Preparation action (server-side, runs before render) to fetch data; Reactive replaces Preparation with Data Actions and Aggregates placed directly on the Screen — Preparation does not exist in Reactive.
Quick Answer: The List widget iterates a source (Aggregate or list) and renders its children once per row, with
List.Currentgiving access to the current element. Pagination usesStartIndex(offset) andMax Records(page size) on the source. TheNavigateaction opens a target Screen, passing Input Parameters. Scaffolding generates a full List + pagination + edit/delete CRUD Screen. Traditional Web's Preparation action does not exist in Reactive — use Data Actions and Aggregates instead.
List Widget
The List widget is the primary mechanism for displaying repeating data. It iterates a source — Aggregate, Data Action output list, or list variable — and renders its child widget subtree once per element. Inside the List, child widgets reference the current row via List.Current (for an Aggregate, GetProducts.List.Current.Product).
| Property / Concept | Purpose |
|---|---|
| Source | The list to iterate — Aggregate, Data Action output, or list variable |
| List.Current | The current row in the iteration — accessible from child widgets |
| List.Empty | Boolean — true when the source has zero rows (use for empty-state message) |
| Child widgets | Rendered once per element; typically Expression, Link, Button |
Exam trap: child widgets inside a List must reference fields via List.Current, not the Screen-level variable. If you bind an Expression's Value to the Screen-level SelectedProduct instead of GetProducts.List.Current.Product, every row will display the same value. The List provides List.Current as the per-row context.
A common pattern is to place a Link inside the List row that Navigates to a detail Screen, passing GetProducts.List.Current.Product.Id as the Input Parameter. This is how scaffolding generates the row-level Edit/Delete links.
Pagination: StartIndex and Max Records
OutSystems pagination is driven by two properties on the source Aggregate or Data Action:
- StartIndex: the zero-based index of the first row to fetch. Page 1 = 0; page 2 (Max Records 10) = 10.
- Max Records: the maximum rows per page (page size).
When the user clicks "Next", the pagination UI updates StartIndex (e.g., StartIndex += Max Records) and the source re-fetches. The OutSystems UI framework provides ListNavigate and pagination widgets (Previous/Next buttons, page number display) that manipulate these properties automatically.
| Concept | Value |
|---|---|
| Page 1, Max Records 20 | StartIndex = 0, fetches rows 0–19 |
| Page 2, Max Records 20 | StartIndex = 20, fetches rows 20–39 |
| Page 3, Max Records 20 | StartIndex = 40, fetches rows 40–59 |
Exam trap: StartIndex is zero-based, not one-based. The first row is index 0. Setting StartIndex = 1 on page 1 would skip the first row. Also, Max Records limits the fetched rows — it is not a display-only property; the Aggregate only returns Max Records rows from the database, which is why pagination is efficient.
The total row count for pagination UI ("Showing 1–20 of 147") comes from the Aggregate's Count property — the total ignoring Max Records. The Count is available because the Aggregate computes both the page list and the full count in one trip.
Navigate Action and Screen Input Parameters
The Navigate action transitions the user to a target Screen. You set the Destination Screen and bind Input Parameters. Example: Navigate to ProductDetailScreen with ProductId = GetProducts.List.Current.Product.Id.
Key behaviors:
- Input Parameters are typed and must be provided.
- When navigating to the same Screen already displayed with different parameter values,
OnParametersChangedfires (OnInitialize does NOT re-run — it runs once per Screen instance). - Navigate replaces the current Screen; the user can use the back button.
- Navigate is client-side in Reactive (no server round-trip to render the target — it fetches its own data via Data Actions).
Scaffolding-Generated CRUD Screens
When you scaffold a List Screen from an entity (right-click entity → "Screen for Entity" → List type), OutSystems generates:
- A Screen with an Aggregate bound to the entity as the List source.
- A List widget with Expressions showing key attributes per row.
- Per-row Link widgets navigating to an edit Screen, passing
Id. - Pagination (StartIndex/Max Records wired to pagination UI).
- A "New" button navigating to an empty edit Screen.
The edit Screen has the Form + Inputs + Save pattern from the previous section. Understanding what scaffolding produces is high-yield because the exam describes the generated structure and asks which widget/action/property is present.
Traditional Web Preparation Contrast
In Traditional Web apps, each Screen has a Preparation action — a server-side action that runs before the Screen renders, used to fetch data (Aggregates/SQL) and set local variables. Preparation is the Traditional equivalent of combining OnInitialize + Data Actions.
In Reactive apps, Preparation does not exist. Data fetching is done via:
- Data Actions: server-side actions placed on the Screen that fetch data and expose the result as a list; they run automatically and can be refreshed via
RefreshData. - Aggregates: placed directly on the Screen, fetch data, and expose
ListandCount. - OnInitialize: a Client Action for one-time setup (not data fetching — use Data Actions for that).
Exam trap: if a question describes a "Preparation action" in a Reactive app context, the correct answer is that Preparation is Traditional Web only and does not exist in Reactive. The Reactive equivalent is Data Actions and Aggregates placed on the Screen. This is one of the most frequently tested Traditional-vs-Reactive distinctions.
The RefreshData built-in Client Action is the Reactive way to re-fetch a Data Action's data (e.g., after a save, call RefreshData(GetCustomers) to reload the list). There is no Preparation to re-run. Aggregates bound directly to a Screen re-fetch automatically when their source parameters change (e.g., a filter variable changes).
Inside a List widget whose Source is the Aggregate GetProducts, how do you reference the Name field of the current row from a child Expression widget?
A Reactive app shows page 3 of a paginated List with Max Records = 15. What is the correct StartIndex value?
In a Reactive app, what replaces the Traditional Web Preparation action for fetching data before a Screen renders?
You are already on ProductDetailScreen (ProductId = 10) and you Navigate to ProductDetailScreen again with ProductId = 25. Which lifecycle event fires?