Indexes and Ordering with Data Aware cWebLists
Related topics: Multi-line Row Support in Lists and Grids · Responsive Applications
When cWebLists are used in select-style drilldown views, the use of indexes and column ordering of lists can be confusing. This document explains how the framework chooses indexes and how you can control ordering for data-aware cWebList objects.
This discussion applies primarily to lists where the list loads the data for you (pbDataAware is true, peDbGridType is gtAutomatic or gtAllData).
Working with drilldown views — guidelines
When working with drilldown views follow these guidelines:
- Use
piSortColumnto control index order. - Always set an initial
piSortColumnvalue. - If you need to further control index order, augment the
IndexOrderfunction. - Do not set these properties:
cWebListpiOrderingcWebColumnpiDefaultIndex- DataDictionary Ordering
piOrdering
piOrdering sets a design-time index order for a list. This is not a web-property. Once set it should not be changed.
- Default value:
-1(usually the best choice). - If the list is relates-to constrained,
piOrderingis not used; instead-1is used which tells the runtime to pick the most efficient index.
Avoid setting this property. Instead, set piSortColumn and augment IndexOrder if necessary.
piSortColumn
Set piSortColumn (see piSortColumn) to the column you want. It will find records by whatever index is deemed best for that column and display the data in that column order.
At application startup each cWebList column calculates the best index for sorting for that column and sets that index into piDefaultIndex. This value never changes; do not set or change piDefaultIndex yourself.
The logic for determining piDefaultIndex:
- If the column’s data binding (
file.field) has an index associated with it, that index is used. - Otherwise
piDefaultIndexis set to-1. - It does not take into account relates-to constraints or other filters, so the index selected may not be efficient for constrained finds.
If a relates-to (parent) constraint is active, piSortColumn will not use the piDefaultIndex because that is often not an efficient index for constrained finds. Instead it returns -1 which instructs the runtime to pick the best index. This usually gives better performance, but it means the ordering you see for a column may not be what you expect (a single best index is used for all columns — the framework errs on the side of performance).
With drilldown views the same view may be used with no filter or with a relates-to constraint. For example, the same select view might be accessed with no parent constraint, a customer parent constraint, or a sales-person parent constraint. When there is no parent constraint you usually want the column’s sort order (piDefaultIndex). When there are parent constraints you want an efficient finding index (-1). This behavior is dynamic and is how the framework behaves by default.
When a mobile/touch view is navigated to it uses the design-time value piSortColumn as the initial value. This property is set in your object declaration using Set and can be queried using WebGet and changed using WebSet.
IndexOrder is the function that takes all of the above information and determines the best index to use for a specific cWebList instance.
IndexOrder
IndexOrder is called for every find and specifies the index order to be used. It examines view state, list configuration, and the sort column to determine which index will be used. The index can be a specific index number or -1, which lets the runtime pick the best index.
IndexOrder can be used for debugging and it can be augmented.
Debugging IndexOrder
Place a breakpoint in IndexOrder (it’s in cWebList) to see exactly which index is being used. If the index is not behaving as expected (e.g., order seems wrong or it’s too slow), the first step is to find out which index is being used. If you are ordering via piSortColumn you can use this function to verify that the proper sort column is being used.
Keep in mind IndexOrder is dynamic: a different index order will be returned based on how a view is being used (user’s choice of sort column and the navigation path into the view, which determines relates-to constraints). This is why choosing an index cannot be controlled with a single property per column.
Augmenting IndexOrder
If you can apply a better index order for a specific condition, augment IndexOrder and return the index of your choosing. Typically you will:
- Read the current sort column with
WebGet piSortColumn. - Check whether a relates-to constraint is in effect (read
Constrain_Fileof the view's DDO).
Augmenting IndexOrder gives you a single place to centralize index selection rules.
Example: return index 12 for column 0 and index 10 for column 1:
Function IndexOrder Returns Integer
Integer iCol
WebGet piSortColumn to iCol
If (iCol = 0) Begin
Function_Return 12
End
Else If (iCol = 1) Begin
Function_Return 10
End
End_Function
Note: piSortColumn is a web-property and must be accessed using WebGet.
In most cases, automatic assignment of indexes works fine when there is no parent constraint. You might augment IndexOrder primarily when applying DDO constraints or when you know an efficient index exists for the filters applied.
When your list is constrained to a parent, the default IndexOrder behavior is to return -1 so the runtime can choose the most efficient index. Depending on your indexes there may be other viable choices that are also fast, or speed may not be an issue for small tables. In such cases you might provide a specific index that balances ordering and performance.
You can detect parent constraints inside IndexOrder by checking the DDO’s Constrain_File property: if zero there is no parent constraint; if non-zero the list is constrained to that parent.
Example: provide special indexes when constrained by a particular parent (for example Customer):
Function IndexOrder Returns Integer
Integer iCol iConstrain
Handle hoDD
Get Server of oList to hoDD
Get Constrain_File of hoDD to iConstrain
// If no parent constraint, use normal logic.
If (iConstrain = 0) Begin
Forward Get IndexOrder to iCol
Function_Return iCol
End
// If constrained, supply specific indexes.
Else Begin
// If constrained by Customer we have special indexes we can use
If (iConstrain = Customer.File_Number) Begin
WebGet piSortColumn to iCol
If (iCol = 0) Begin
Function_Return 8
End
Else If (iCol = 1) Begin
Function_Return 11
End
End
// Some other parent constraint — tell the runtime to pick the best index
Else Begin
Function_Return -1
End
End
End_Function
DDO Ordering
As with all DDOs, you can set Ordering directly in the DDO. If you do so it overrides all finds and uses that index for every find. Its default value, -1, allows the framework and developer to specify the index dynamically.
Avoid setting Ordering unless you have a clear reason. This advice applies to all DataFlex application styles.
Using gtAllData to load all data and allow client sorting
An alternate approach is to use a single constrained index that reads all relevant data at once. If you do that, the client can control sorting locally and you can sort by any column. This only makes sense if your record set is small enough (for example, when drilling into orders for a single customer).
Set the list to read all data:
Set peDbGridType to gtAllData
If your view may or may not have a parent constraint, make this setting dynamic. During forward navigation, test for a parent constraint; if present, read all records for that parent at once, otherwise use normal dynamic record loading:
Procedure OnNavigateForward tWebNavigateData NavigateData Handle hoInvokingView Handle hoInvokingObject
Integer iConstrain
Handle hoDDO
Get Server of oList to hoDDO
Get Constrain_File of hoDDO to iConstrain
If (iConstrain <> 0) Begin
WebSet peDbGridType of oList to gtAllData
End
Else Begin
WebSet peDbGridType of oList to gtAutomatic
End
End_Procedure
Remember to use WebSet when changing peDbGridType dynamically. If you plan to change a list’s peDbGridType dynamically, ensure the initial design-time value is gtAutomatic (the default).
The sample WebOrderMobile demonstrates a similar approach (it uses a different mechanism to detect if the select view is constrained).
Note: When using gtAllData, piSortColumn no longer determines the record finding order (finding still uses index -1, letting the runtime choose the best index for the filters). piSortColumn still determines the display order on the client, since sorting will occur on the client. If you want a different finding index when using gtAllData, augment IndexOrder.
Dynamic parent constraints
Select views assign parent constraints dynamically as the view is forward-navigated to. This allows a single select view to be used under different navigation conditions. This dynamic behavior can be disabled.
The pbDrillDownDynamicConstraints property controls this behavior. By default it is true (dynamic parent constraints are applied). When set to false, relates-to constraints are not applied dynamically and whatever design-time values you set in your view will be used (similar to desktop-style views). Use caution when disabling dynamic constraints.
Parent super-finding (parent column searches)
The framework (both Windows and Web) can search lists based on a parent column if:
- The parent column is related to the child, and
- There is a child index whose first index segment is the parent key.
This parent-super-finding works automatically with prompt lists (cDbCJPromptList and cWebPromptList). For cWebList (used in mobile/touch select views), set the cWebList peRequestFindMode to rfmFindMainSuper:
Set peRequestFindMode to rfmFindMainSuper
This parent-find lookup should only be used when the parent is not also constraining the list. Searching a constrained parent makes no sense.
Design guidance and navigation paths
In the examples and the WebOrderMobile sample we assume a mobile view may be accessed via multiple navigation paths to demonstrate flexibility. This is not always the best choice for production apps. Unlike desktop style frameworks, mobile/touch apps let you define navigation paths completely. Use that power wisely.
Recommended approach:
- Determine exactly what you want your end users to accomplish.
- Look for a single navigation path that makes this possible.
- Avoid providing multiple ways to do the same or similar things.
- Avoid optional settings — find the best way and use it.
- When there is a trade-off between a clear single interface and flexibility — choose clarity.
The mobile interface is more limited visually and for input than desktop; favor clarity and simplicity.
If you follow these guidelines, views tend to be single-purpose. That reduces dynamic complexity and makes index/order logic simpler. Although the WebOrderMobile sample demonstrates many features and multiple navigation paths, for production apps consider simplifying navigation.
View strategies — multiple views vs. single dynamic view
Two conceptual strategies:
- Create distinct views for each navigation path (simpler per-view logic, easier to control behavior, more duplication).
- Create as few views as possible and make them context-aware (less duplication, more complex logic).
Trade-offs:
- Avoid unnecessary duplication, but if customizing a view for multiple uses becomes too complex, multiple similar views may be the better option.
- This is relevant to
cWebListordering because handling constrained and non-constrained cases can add complexity. If you find manyifstatements, property toggling, or lots of code to change view behavior, consider creating a new view.
One of the biggest differences between desktop and mobile/touch styles is that relates-to parent constraints are applied dynamically. This can lead to more code to handle both constrained and non-constrained conditions — creating multiple views can simplify behavior and align more closely with desktop-style expectations.
Previous Topic: Multi-line Row Support in Lists and Grids
Next Topic: Responsive Applications