Filter to Query to Select Data Flow Guide
This guide explains the list-page data path from filter UI into query state, then into ModelResource.select(query), and finally into table and pager state in Zova.
Use this page when you want to understand:
- how filter UI changes become list-page query state
- how paging state joins that query
- how the final query reaches
ModelResource.select(query) - how the resulting paged/list data is consumed by table and pager
- where permission-driven table-meta refresh sits relative to that data flow
Why this page exists
Several existing docs already explain nearby pieces:
- Resource List Page Deep Dive explains the broader list-page runtime assembly
- Rest Resource Under the Hood explains the module-level bridge
- Table + Resource CRUD Cookbook explains practical authoring for CRUD list pages
What those pages do not isolate directly is the one compact data-flow path from filter UI into the owner-side query execution boundary.
That is the gap this page fills.
The shortest accurate mental model
A practical mental model is:
blockFilternormalizes user-entered filter valuesblockPagestores those values inqueryFilterDatablockPagealso owns paging state inqueryPagedblockPagecomputes one mergedqueryModelResource.select(query)becomes the owner-side query execution boundary- the select result provides both
listand paged metadata blockTableconsumes the list datablockPagerconsumes the paged metadata
That means the list-page fetch path is not scattered across table, pager, and filter. It is centralized in the page runtime and then handed to the owner.
Source-confirmed reading path
When reading this topic, use this order:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsxzova/src/suite/cabloy-basic/modules/basic-page/src/component/blockFilter/controller.tsxzova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPager/controller.tsxzova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts
That order moves from the page-owned state source, to filter and pager inputs, to the table bridge, and finally to the owner-side query execution path.
Runtime path by layer
1. Filter UI becomes queryFilterData
The filter bridge lives in:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockFilter/controller.tsxThis block renders ZForm with:
data={$$page.queryFilterData}schema={$$page.schemaFilter}schemaScene="filter"
Its submit and reset flow both pass through _onFilter(dataOld).
The important source-confirmed behavior is:
- nil and empty-string values are removed
- the normalized object is passed to
$$page.onFilter(dataNew)
This is the first key rule:
blockFilterdoes not fetch directly- it normalizes filter values and pushes them into page-owned state
2. Paging state becomes queryPaged
The page runtime owner lives in:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsxInside __init__(), it initializes:
queryFilterData = {}queryPaged = { pageNo: 1, pageSize: this.$props.pageSize }
It also owns:
gotoPage(pageNo)setPageSize(pageSize)
That means paging is page-owned runtime state, not something the pager block owns independently.
3. query is the canonical merged state
The same blockPage controller computes:
this.query = this.$computed(() => {
return Object.assign({}, this.queryFilterData, this.queryPaged);
});This is the canonical query object for the list page.
A practical reading rule is:
- filter state and paging state stay separate while they are being edited
queryis the merged runtime shape passed into the owner
4. ModelResource.select(query) is the owner-side query boundary
The owner-side boundary lives in:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.tsblockPage uses:
get queryData() {
return this.$$modelResource.select(this.query);
}Inside the owner:
select(query?)delegates toselectGeneral(undefined, query)selectGeneral(...)creates a query key shaped like:['select', actionPath ?? '', hashkey(query)]
- it fetches the list result from the resolved
resourceApi
This is the second key rule:
- pages assemble query state
ModelResourceowns query execution, query-key identity, and fetch semantics
5. The select result becomes both data and paged
Back in blockPage, two important derived surfaces are exposed:
data = queryData.data?.listpaged = queryData.data
This matters because the select result is not only a list.
It is the paged result surface that both table and pager depend on.
6. blockTable consumes list data
The table bridge lives in:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsxIt consumes the shared page runtime and passes into ZTable:
data={$$page.data}schema={$$page.schemaRow}tableScope={$$page.jsxCelScope}
That means blockTable is not the owner of the fetch path.
It is the bridge from page-owned list result into the table runtime.
7. blockPager consumes paged metadata
The pager block lives in:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPager/controller.tsxIt reads:
$$page.paged
and delegates navigation back into page-owned state with:
$$page.gotoPage(...)
This is the third key rule:
- the pager does not fetch directly
- it mutates page-owned paging state and lets the central query path react
Permission-driven table-meta refresh is adjacent, not the data path itself
blockPage also watches:
permissions
and refreshes table meta when permission state changes.
That behavior is important, but it is not the same thing as the filter/query/select data path.
A practical rule is:
- filter/paging changes affect query identity and data fetches
- permission changes affect table metadata and visible action/column behavior
Those are related, but distinct, flows.
A compact end-to-end trace
The shortest end-to-end trace is:
- filter UI normalizes user input
queryFilterDatachangesqueryPagedchanges when navigation changes pagequery = { ...queryFilterData, ...queryPaged }queryData = $$modelResource.select(query)data = queryData.data?.listpaged = queryData.data- table consumes
data - pager consumes
paged
That is the source-confirmed filter -> query -> select data path in the current Basic list-page runtime.
Debugging checklist
If list data looks wrong, ask:
- did
blockFilternormalize away a field unexpectedly? - did
queryFilterDataactually change? - did
queryPagedchange as expected? - does the merged
querycontain the expected fields? - did
ModelResource.select(query)create the expected query-key identity? - is
queryData.data?.listpopulated as expected? - is
pagedpresent and consistent with the list result? - is the issue really data flow, or is it permission-driven table-meta refresh instead?
Where to read next
Use these next steps depending on your question:
- if you want the broader list-page runtime assembly, read Resource List Page Deep Dive
- if you want the owner internals behind
select(query), read ModelResource Internals Deep Dive - if you want practical CRUD authoring guidance, read Table + Resource CRUD Cookbook
- if you want row/bulk action visibility, read Table Action Visibility and Permission Flow Guide
Final takeaway
The most accurate way to read the current Basic list-page data path is:
blockFilterowns filter normalizationblockPageowns query state assemblyModelResource.select(query)owns owner-side query executiondataandpagedare the shared outputs- table and pager consume those outputs instead of owning the fetch logic
That is the source-confirmed filter -> query -> select runtime path in the current Cabloy Basic frontend architecture.