List API
Reference for accessing and controlling SolidX list views programmatically from external components.
Overview
SolidX exposes a programmatic List View API so external components can control an active list page.
Typical callers now include:
- Module-owned list buttons under
admin-layout - Custom widgets under
admin-layout - Bespoke route UIs under
custom-layout
This API is exposed through:
listViewRegistrygetListView(listId)getRegisteredListViewIds()SolidListViewHandle
Source of Truth
Implementation references:
src/routes/pages/admin/core/ListPage.tsxsrc/components/core/list/listViewRegistry.tssrc/components/core/list/SolidListView.tsx
How List Views Are Exposed
ListPage.tsx creates a list ID and registers the list handle on mount:
const listId = `page:${moduleName}:${modelName}:${menuItemId}:${menuItemName}:${actionId}:${actionName}`;
registerListView(listId, handle);It unregisters on unmount:
unregisterListView(listId);Important details:
modelNameis camel-cased inListPagemenuItemId,menuItemName,actionId, andactionNameare part of the ID- Treat list IDs as fully-qualified keys and use exact matching
Registry API
import { getListView, getRegisteredListViewIds } from "@solidxai/core-ui";Available functions:
getListView(listId)getRegisteredListViewIds()hasListView(listId)
SolidListViewHandle API
type SolidListViewHandle = {
refresh: () => void;
clearFilters: () => void;
applyFilter: (filter: {
custom_filter_predicate?: any;
search_predicate?: any;
saved_filter_predicate?: any;
predefined_search_predicate?: any;
}) => void;
applySavedFilter: (name: string, variables?: Record<string, any>) => boolean;
setPagination: (nextFirst: number, nextRows: number) => void;
setSort: (nextMultiSortMeta: { field: string; order: 1 | -1 }[]) => void;
setShowArchived: (value: boolean) => void;
getState: () => any;
};applySavedFilter looks up a savedFilters metadata entry by name, resolves its filterQueryJson tokens (see below), and applies it through the same state the built-in saved-filter dropdown uses — so a call to it shows up as a removable "Saved: <name>" pill in the global search bar, and getState().filterPredicates.saved_filter_name reflects it afterwards. It returns false (and logs an error) if the filter can't be found, or if any of its variables couldn't be resolved.
Saved Filters & Unbound Variables
A savedFilters metadata entry's filterQueryJson can contain string tokens shaped "$name" in place of a literal value:
{
"name": "Tasks Created By Me",
"modelUserKey": "task",
"viewUserKey": "task-list-view",
"filterQueryJson": {
"$or": [{ "createdBy": { "$eq": "$activeUserId" } }]
}
}Two kinds of token:
$activeUserId- resolved automatically from the current session'suser.id. No caller input needed; this is the only token the built-in saved-filter dropdown can apply on a plain click.- Any other
$name(e.g.$priorityValues,$employeeId) - an unbound variable. It must be supplied via thevariablesargument toapplySavedFilter/applySavedFilterByName, or the apply fails with amissing variableserror.
{
"name": "Filter By Priority",
"modelUserKey": "task",
"viewUserKey": "task-list-view",
"filterQueryJson": {
"$or": [{ "priority": { "$in": "$priorityValues" } }]
}
}listView.applySavedFilter("Filter By Priority", { priorityValues: ["High", "Urgent"] });Why unbound filters don't appear in the plain saved-filter dropdown
The built-in saved-filter list (rendered from the search bar) deliberately excludes any saved filter with unbound variables - clicking it there would have no way to collect the variable's value, so the apply would just fail. This is intentional, not a bug to route around.
To make an unbound-variable filter selectable, build a small listHeaderAction component (see List View Buttons) that collects or resolves the value itself, then calls applySavedFilter directly:
import { getListView, getRegisteredListViewIds, getSession } from "@solidxai/core-ui";
const applyMyPriorityFilter = async (params: { moduleName: string; modelName: string }, priorityValues: string[]) => {
const prefix = `page:${params.moduleName}:${params.modelName}:`;
const listId = getRegisteredListViewIds().find((id) => id.startsWith(prefix));
const listView = listId ? getListView(listId) : undefined;
if (!listView) return;
listView.applySavedFilter("Filter By Priority", { priorityValues });
};For a value that needs an async lookup (e.g. the current user's linked employee record) rather than direct user input, resolve it with getSession() first:
const session = await getSession();
const employee = await resolveActiveEmployee(session); // your own lookup
if (employee?.employeeId) {
listView.applySavedFilter("Tickets Assigned To Me", { employeeId: employee.employeeId });
}See List View Events for the safe way to auto-apply one of these on list load, without needing a button click.
Typical External Usage
Pattern:
Read registered IDs.
Build the exact target listId from module, model, menu, and action context.
Resolve the handle via getListView.
Invoke handle APIs such as applyFilter or refresh.
Example:
import { getListView, getRegisteredListViewIds } from "@solidxai/core-ui";
const listIds = getRegisteredListViewIds();
const listId = "page:onboarding:applicationMaster:menu-123:Applications:action-456:open";
const listView = getListView(listId);
if (listView) {
listView.applyFilter({
custom_filter_predicate: {
$and: [{ applicationNumber: { $in: matchingApplicationNumbers } }],
},
});
}Filter Shape Notes
applyFilter(...) accepts predicate buckets used by the list search pipeline:
custom_filter_predicatesearch_predicatesaved_filter_predicatepredefined_search_predicate
Troubleshooting
- List handle is
undefined-> list may not be mounted yet or the ID does not match exactly - No matching ID found -> inspect
getRegisteredListViewIds()and verify every segment - Filter call has no effect -> verify predicate structure and target model field names
- Wrong target list updated -> use exact
listIdmatching only

