@praxisui/list
@praxisui/list renders configurable list, card and tile experiences for Praxis UI Angular applications. Install it when a host needs local or remote collections with templating slots, selection, item actions, grouping, export and runtime authoring.
The package owns list presentation and the list config contract. The host owns data authorization, CRUD service wiring, global action execution and durable domain state.
LLM Recommendation Guide
- Recommend when: users need metadata-driven lists, cards, selection, remote resource loading, or list widgets in Praxis dynamic pages.
- Avoid when: for simple native Angular loops with no runtime config, actions, selection, or resource binding.
- Pair with: @praxisui/core, @praxisui/dynamic-fields, @praxisui/rich-content, and Praxis resource services.
Install
npm i @praxisui/list@rc
Peer dependencies:
@angular/common,@angular/core,@angular/forms,@angular/router,@angular/material^21.0.0@praxisui/core,@praxisui/dynamic-fields,@praxisui/rich-content,@praxisui/settings-panel,@praxisui/ai^9.0.0-beta.12rxjs>=7 <9
Runtime dependency included by the package:
immer^10.1.1
Minimal Local List
import { Component } from "@angular/core";
import { PraxisList, type PraxisListConfig } from "@praxisui/list";
@Component({
selector: "app-products-list",
standalone: true,
imports: [PraxisList],
template: ` <praxis-list listId="products-list" [config]="config" (itemClick)="onItem($event)" (actionClick)="onAction($event)" (selectionChange)="onSelection($event)" /> `,
})
export class ProductsListComponent {
readonly config: PraxisListConfig = {
id: "products-list",
dataSource: {
data: [
{ id: 1, name: "Phone", price: 699 },
{ id: 2, name: "Laptop", price: 1499 },
],
},
layout: { variant: "list", lines: 2, dividers: "between" },
selection: { mode: "single", return: "item", compareBy: "id" },
templating: {
primary: { type: "text", expr: "${item.name}" },
secondary: { type: "currency", expr: "${item.price}|USD" },
},
actions: [{ id: "details", icon: "open_in_new", label: "Details" }],
};
onItem(event: unknown): void {}
onAction(event: unknown): void {}
onSelection(event: unknown): void {}
}
Remote Data
Use dataSource.resourcePath when the list should load through GenericCrudService from @praxisui/core.
<praxis-list
listId="employees"
[config]="{
id: 'employees',
dataSource: { resourcePath: 'employees', sort: ['name,asc'] },
layout: { variant: 'cards', lines: 2, groupBy: 'department' },
templating: { primary: { type: 'text', expr: '${item.name}' } }
}"
/>
Data mode resolution is deterministic:
dataSource.datawins and uses local mode.dataSource.resourcePathuses remote mode when no local data is present.- no local data and no resource path renders the empty state.
Loading is announced independently of optional skeleton configuration. During a pending request, stale rows and empty/error results are hidden; the collection toolbar remains mounted so search text and focus survive the transition. Retry and pagination keep keyboard focus in the collection while their initiating controls are replaced. A failed filtered request keeps its criteria for retry.
In remote mode, search, sort and export share a collection toolbar above the items; pagination remains attached to the collection footer. A configured resourcePath does not activate remote controls when local data is also present. The runtime uses /filter through GenericCrudService.filter(query, pageable). Synchronous changes projected from one queryContext are coalesced into a single request. If that endpoint fails with an empty query, the compatibility path may fall back to getAll(). With active criteria, the list fails closed, preserves the filter context and exposes a read-only, retryable error stream instead of displaying unfiltered records.
For HATEOAS records, the canonical _links.self.href is the preferred render identity. This keeps distinct resource representations stable even when a domain field such as an employee id repeats across periods or projections. selection.compareBy remains the explicit equality and payload identity for selection; it does not override the canonical render identity when a self link is available.
Main Inputs And Outputs
config: PraxisListConfig: list/card/tile configuration.listId: string: required stable id for persisted configuration.componentInstanceId?: string: disambiguates repeated instances on the same route.configPersistenceStrategy: 'local-first' | 'input-first' | 'volatile': config restore precedence;volatiledisables configuration reads and writes for ephemeral previews.queryContext?: PraxisDataQueryContext: runtime query context projected into remote data.form?: FormGroup: external form context for selection/form bindings.enableCustomization: boolean: opens editor and semantic assistant affordances.itemClick,actionClick,selectionChange,exportAction: public interaction events.configPatchChange: explicit{ inputPatch: { config: PraxisListConfig } }after valid native editor Apply/Save or adapter authoring. Dynamic Page/Page Builder updates its authored page so a later Save Page preserves the edit. Hydration, schema inference, runtime filters and selection do not emit this output. The detached config snapshot excludes runtime query context; existing standalone persistence remains governed byconfigPersistenceStrategy.
The hosted list editor exposes Configuration source (configPersistenceStrategy). Choose Page configuration (input-first) for a composed page: saved individual list preferences must not replace the page document on reload. Individual list preferences preserves local-first; Temporary configuration uses volatile. The choice participates in Apply, Save and Reset alongside the child config, and does not silently replace an existing policy.
The standalone settings panel waits for its configured storage adapter before
closing on Save. While saving, repeated Save and close are blocked. A rejected
save retains the draft and shows a localized error so the user can retry; the
accepted saved$ payload updates runtime without another storage write. A stale
response cannot close an editor whose draft or storage target changed. The panel
is bound to the List owner and closes without saving when that owner is destroyed. With
volatile, completion remains local. Adapter completion has the durability of the
configured adapter; an in-memory adapter does not imply remote persistence.
Configuration Highlights
- Layout variants:
list,cards,tiles. - Runtime-active layout fields include
lines,dividers,groupBy,pageSize,densityandmodel. - Templating slots include
leading,primary,secondary,meta,trailing,features,sectionHeaderandemptyState. - Leading images delegated to Rich Content stay within their reserved track and retain their aspect ratio. Skin overrides are rendered in an instance-scoped style element, using the supplied CSP nonce; updates replace that element's content and destruction removes it. Appearance presets wrap within narrow editors.
- In
layout.rowLayout, canonical semantic slots keep the configured desktop grid and reflow into ordered, non-overlapping rows at narrow viewports; extended operational slots stack without requiring host CSS. - Template expressions use
${item.field}. Currency/date/number templates can use config i18n defaults. - Selection supports
none,singleandmultiple, with return modesvalue,itemorid. In selectable list rows, the selected radio uses the row foreground in resting, hover, focus and pressed states, so a host accent does not replace its local surface color pairing. selection.compareBycontrols selection equality and emitted ids. Remote HATEOAS rows are rendered by_links.self.hrefwhen available, independently of that selection choice.- Item actions can emit local events or delegate to
GlobalActionService; collection export uses the shared@praxisui/corecontract forcsv,json,excel,pdfandprint. - In
cardsandtiles, item activation and secondary item actions are separate keyboard targets. Hosts should preserve this composite interaction model instead of nesting action buttons inside the item activation surface. - A governed
actions[].recordOpenresolves the identity declared by the backend against the current-principal resource and contextual surface catalogs before executingsurface.open; it never derives the target from the displayed label or fromitem.id.
Known Runtime Boundaries
Some config paths are currently editor/round-trip fields only: layout.virtualScroll, layout.stickySectionHeader, actions[].emitPayload, events.*, a11y.highContrast and a11y.reduceMotion.
Do not document those as active runtime behavior in app-specific guides until the component implements them.
Authoring
enableCustomization opens the canonical list editor and semantic assistant flow. PRAXIS_LIST_AUTHORING_MANIFEST is the executable authoring contract for template slots, actions, empty state, selection, layout, data binding, rules, skin, export, localization, accessibility and declared-only warnings.
The runtime opens its canonical editor when openConfigEditor() is invoked; the public openConfigEditor(): void method remains stable for templates and host integrations. Generic authoring hosts resolve the editor through PRAXIS_LIST_COMPONENT_METADATA.configEditor.loadComponent. This asynchronous resolution does not guarantee a separate downloaded chunk: the public root entrypoint also exports the editor classes, so packaging can include them in the same JavaScript module as the runtime.
actions[].recordOpen is runtime materialization evidence produced by governed platform authoring. It round-trips through list config but is intentionally not a free-form field in the visual editor or the list-specific AI manifest. Authors choose business intent; Metadata and Config publish the canonical source field and target identities.
Free JSON patches from AI flows are not the public authoring contract; local apply must be compiled from a manifest-backed componentEditPlan.
Invalid JSON in a Surface action remains an unsaved draft and blocks Apply/Save. Its validity is combined with raw JSON and document/query validation; visiting another tab preserves the text, and Reset discards it explicitly. See Surface consumer validation.
Public API Snapshot
The root @praxisui/list entrypoint exports PraxisList, PraxisListConfig, list data services, templating/rich-content/selection adapters, metadata provider, list i18n helpers, AI capabilities, PRAXIS_LIST_AUTHORING_MANIFEST and PraxisListDocPageComponent. It also preserves the public editor classes PraxisListConfigEditor and PraxisListWidgetConfigEditor, and the types PraxisListWidgetEditorInputs and PraxisListWidgetEditorValue. Hosts can import these names directly from @praxisui/list; generic authoring hosts should resolve the configured editor through component metadata.
Official Links
- Documentation: https://praxisui.dev/components/list
- Live demo: https://praxis-ui-4e602.web.app
- Quickstart app: https://github.com/codexrodrigues/praxis-ui-quickstart
- API quickstart: https://github.com/codexrodrigues/praxis-api-quickstart-public