Data Manager listings
Introduction
Preside listings have been rebuilt around DataTables 3. The old search box and filter strip are replaced by a single everything bar. Users can show, hide and reorder columns, filter from column headings, and save named views (filters plus columns).
This page is the developer guide for that change: what broke, what is on by default, and what you should tweak per object or per embedded table.
The annotation and objectDataTable() reference still lives in Data Manager Basics and Customizing Data Manager. Customization points are listed at the end.
What changed (react to this)
Check these first when upgrading an application or extension:
- Custom DataTables JavaScript still using
fnServerParams,sAjaxSource,aoColumnsor$table.dataTable({ ... })with Hungarian options. Core listings go through a compatibility adapter, but new tables should use DataTables 3 options (ajax,columns,serverSide, …). PreferPresideDatatables.hungarianAjax()if you still POST the oldsEcho/iDisplayStartpayload. - Search UI. There is no standalone
.data-table-search/dataTables_filteron core listings. Search is the everything bar. If you injected extra buttons next to the old search input, move them to getEverythingBarActions or the listing top-right buttons. - Default columns in the picker. Objects that omit
@datamanagerColumnPickerFieldsnow useauto, not “grid fields only”. Users will see more columns than before unless you tighten the pool (see Available columns). - No columns are locked unless you set
@datamanagerLockedGridFields. The label column is not fixed by default; users can hide it. - Saved views are on for non-compact listings that have a column picker. Turn them off per object or per table if that does not make sense (see Saved views).
- Listing footers that return a string still work (one cell). If users can hide columns, prefer a mapped footer keyed by field name so totals stay under the right column (see Footers).
- Compact / related-record tables (
compact=true, including view-record related listings) keep the old, simpler table: no column picker, no heading filters, no saved views.
Available columns
@datamanagerGridFields is still the default visible set (what a user sees before they change anything). The picker pool is a larger list: grid fields, hidden grid fields, then @datamanagerColumnPickerFields (or the application default).
Object annotations
/**
* @datamanagerEnabled true
* @datamanagerGridFields label,status,category,datecreated
* @datamanagerHiddenGridFields notes
* @datamanagerColumnPickerFields auto,!internal_notes
* @datamanagerLockedGridFields
* @datamanagerAllowSavedViews true
*/
component {
property name="status" type="string" dbtype="varchar" maxlength=20;
property name="notes" type="string" dbtype="varchar" maxlength=200;
property name="internal_notes" type="string" dbtype="text" excludeDataExport=true;
}
@datamanagerGridFields— default columns on the table.@datamanagerHiddenGridFields— in the picker, off until the user turns them on.@datamanagerColumnPickerFields— extra pool. Acceptsauto,*wildcards and!exclusions (auto,!internal_notes,*,!sensitive_*).@datamanagerLockedGridFields— always visible, always first, cannot be hidden or reordered. Empty by default.
Grid fields, hidden grid fields and search fields stay in the pool even when auto would skip them.
What auto includes
auto is the default when the object has no @datamanagerColumnPickerFields and you have not changed settings.dataManager.defaults.columnPickerFields. It skips:
- The ID field (
idor the object'sgetIdField()) - Properties whose name starts with
_ - Unbounded text (
text,longtext, …) and binary / blobdbtypes, andtype="text"/type="binary" excludeDataExport=trueandautofilter=false(unless you opt the property back in)one-to-many,many-to-manyandselect-data-viewrelationships- Secret, encrypted, password and
renderer=none/adminRenderer=nonefields
Many-to-one relationships and formula fields are included in auto (formula fields still do not get a heading filter).
Property flags
property name="notes" type="string" dbtype="longtext" datamanagerUserColumn=true;
property name="internal_code" type="string" dbtype="varchar" datamanagerUserColumn=false;
datamanagerUserColumn=true— put the field in the picker even whenautowould skip it.datamanagerUserColumn=false— keep it out of the picker even if a wildcard would include it.
Application default
// /application/config/Config.cfc
settings.dataManager.defaults.columnPickerFields = "auto"; // core default
settings.dataManager.defaults.columnPickerFields = ""; // picker = grid + hidden grid fields only
settings.dataManager.defaults.columnPickerFields = "*"; // every listable field
Per-object @datamanagerColumnPickerFields always wins.
Replacing the pool in a handler
// /application/handlers/admin/datamanager/blog_post.cfc
component {
private array function getAvailableListingColumns( event, rc, prc, args={} ) {
return [ "title", "status", "published", "datemodified" ];
}
private array function getDefaultListingColumns( event, rc, prc, args={} ) {
return [ "title", "status", "published" ];
}
}
getAvailableListingColumns— replace the picker pool. ExplicitgridFields/hiddenGridFieldspassed into that table are still merged in.getDefaultListingColumns— replace the default visible columns (args.defaultFieldsis the annotated grid field list).
Saved views
A view is a named snapshot of filters + columns. It does not store free-text search, sort order or page length.
Default on when the table has a column picker and is not compact. Default off for compact tables.
/**
* @datamanagerAllowSavedViews false
*/
Or for one embedded table only:
objectDataTable( objectName="blog_post", args={
allowSavedViews = false
} );
Passing allowSavedViews=true on a compact table has no effect.
Permissions
Anyone who can use the listing can save a personal view. Sharing globally or with a group needs CMS permission datamanager.sharelistingviews (included in datamanager.*).
Behaviour to know about
- Named views lock the filters they own until the user chooses Edit view. Extra search and extra everything-bar filters can still be added on top.
- Columns can be shown, hidden or reordered on a locked view; those column changes are not written to the view unless the user is editing it.
- The implicit Default view is annotated grid fields and no saved filters.
Listing context
Column layout and the last selected view are stored per user, listing and context.
If you list the same object in more than one place with different meaning (subscriptions for a product, corporate vs individual, …), set a labelled context on that table. The save form then asks whether the view applies to this context or all listings of the object.
// /application/handlers/admin/datamanager/crm_subscription.cfc
component {
private string function listingViewlet( event, rc, prc, args={} ) {
args.listingContextKey = prc.recordId ?: "all";
args.listingContextLabel = prc.recordLabel ?: "";
return renderViewlet( event="admin.datamanager._objectListingViewlet", args=args );
}
}
listingContextKey— stable machine id (max 100 characters; longer keys are hashed).listingContextLabel— shown in the save form. If it contains:, it is treated as an i18n URI.
If you omit the key, Preside derives context from the listing ajax URL query string (cache-busters stripped). Extra parameters from getAdditionalQueryStringForBuildAjaxListingLink are included, so two listings that already differ by query string get separate prefs without extra work.
listingPreferenceKey only separates named views when the same object appears twice on one screen. It does not replace listing context.
Column heading filters
Heading filters follow the picker pool and also need allowFilter (rules engine listing filters). They are off for compact tables.
They are not created for formula fields, autofilter=false, many-to-many, one-to-many, or renderer=none. Many-to-one columns get an object picker plus a saved-filter picker for the related object. Enums get the enum options.
Footers
renderFooterForGridListing still accepts a string (shown in the first footer cell). That layout does not track hidden or reordered columns.
When the column picker is on, return a struct (or array of row structs) keyed by field name. Core places each cell under the matching visible column and drops cells whose column is hidden.
private any function renderFooterForGridListing( event, rc, prc, args={} ) {
return {
labelField = "label"
, label = "Totals"
, cells = {
status = NumberFormat( args.records.recordCount )
, amount = { html=NumberFormat( 1234.5, "9,999.99" ), className="text-right" }
}
};
}
label/labelField— put the label in that column when it is visible; otherwise the first data column.cells.{field}— string HTML, or{ html, className }.- Multiple rows:
{ rows=[ { label, cells, labelField }, ... ] }or a raw array of those structs.
args still includes records and getRecordsArgs (current search and filters).
Everything bar extras
To add an action that uses the typed query (for example “Ask AI” that returns a rules-engine expression), implement getEverythingBarActions. Extra chips stay removable on a locked view; when the user saves a view, extra expressions are folded into that view’s advancedFilter.
Do not write extra filters into [name=filter] from your ajax handler.
Embedded tables
objectDataTable( objectName="blog_post", args={
compact = false
, allowColumnPicker = true
, allowColumnFilter = true
, allowSavedViews = true
, listingPreferenceKey = "blog_post_editorial"
, listingContextKey = "corporate"
, listingContextLabel = "crm.subscription:listing.context.corporate"
, hiddenGridFields = [ "notes" ]
} );
| Arg | Default | Notes | | --- | --- | --- | | compact | false on main listings; true on related-record tables | Forces picker, heading filters and views off | | allowColumnPicker | on unless compact | Show / hide / reorder | | allowColumnFilter | on unless compact | Also requires allowFilter | | allowSavedViews | follows picker unless @datamanagerAllowSavedViews is set | Compact always off | | listingContextKey / listingContextLabel | derived from ajax query string | Labelled identity for prefs and views | | listingPreferenceKey | object name | Isolates named views on the same screen |
Override listingViewlet and pass the same args through to admin.datamanager._objectListingViewlet if you only need to set context or flags, rather than replacing the whole listing.
Custom DataTables (extensions)
Admin $().dataTable({ bServerSide, sAjaxSource, fnServerParams, ... }) is mapped to DataTables 3 automatically. New code should call .DataTable() with camelCase options.
If you still talk to a Preside ajax listing that expects Hungarian POST fields:
ajax : PresideDatatables.hungarianAjax( datasourceUrl, function( params, dtRequest ) {
params.sSearch = $( ".my-search" ).val();
} )
Do not use DataTables 1.x fnServerParams on a table that is already using ajax / PresideDatatables.hungarianAjax — that combination throws at runtime.
Related documentation
- Data Manager Basics — object annotations
- Customizing Data Manager —
objectDataTable()args and customization index - listingViewlet
- renderFooterForGridListing
- getEverythingBarActions
- getAdditionalQueryStringForBuildAjaxListingLink
- preFetchRecordsForGridListing