aster.UI / Docs
Aster products
Showcase ↗

Aster UI / Guides

Understand the shared data model

A grid, a form and a chart often show different parts of the same records. As soon as users filter, edit or refresh those records, each view needs to agree about what changed. Aster gives those views a shared data context and keeps unsaved edits separate until they are saved.

If you have not run a page yet, start with Get started. This guide explains the pieces you will use when adding a second view or an editing workflow.

Connect once for the views that share data

A DataContext connects a transport to an OData service. It loads the service's metadata and owns the schema, record store and synchronization for that context. Use the same context for views that should share records.

import { DataContext, fetchTransport } from "./lib/aster-ui.js";

const ctx = await DataContext.connect(
  fetchTransport("https://your-service.example/odata/")
);

The service metadata describes entity sets, property types, keys, relationships and annotations such as labels, allowed values and validation constraints. Controls use that information to choose editors and filter operators. The server must still enforce permissions and validation; client metadata is not an access control boundary.

For local development, the examples use an optional WebAssembly service with the same transport interface. Replace its transport with your service connection when ready. See installation and distribution for the files involved.

A collection is a query and its loaded pages

The record store holds shared records. A Collection holds a query and the keys at positions in that query's result. It loads pages as views need them; it does not have to hold the entire result before a user can scroll or filter it.

const devices = ctx.collection(
  { set: "Devices", orderBy: [{ field: "name" }] },
  { pageSize: 40, keepPages: 4 }
);

Bind both a Grid and a TileGrid to devices to share the same result. Changes to the query apply to the service result, including records on pages that have not loaded. A known update reaches views through the record store. If an update's position in a partially loaded result is uncertain, the collection refreshes the relevant result rather than guessing its order.

Query results are not a snapshot transaction. Concurrent service changes may require a refresh, and applications should handle errors while retaining useful confirmed content. See filtering, grouping and export and stable refresh feedback.

Selection belongs to the data workflow

A Selection tracks record keys independently of the mounted rows or tiles. Sharing one selection lets a user switch presentation without losing their picks.

import { Selection } from "./lib/aster-ui.js";

const selection = new Selection(ctx.store, devices, {
  multiple: true, ordered: false, fallback: false,
});
table.ctx = tiles.ctx = ctx;
table.collection = tiles.collection = devices;
table.selection = tiles.selection = selection;

Here table and tiles are existing Grid and TileGrid elements. The controls present selection; the application chooses its policy. The shared collection example demonstrates single, multiple and ordered selection, including an optional ordered-selection limit.

Edit a draft, then save it

An EditModel holds a record's uncommitted values, validation state and baseline. A form writes into that draft. Saving sends changes to the service; a successful response updates the shared record store, so the grid and other views follow.

import { EditModel } from "./lib/aster-ui.js";

// key is a record key obtained from the collection or shared selection.
const edit = new EditModel(ctx, "Devices", key);
form.model = edit;
await edit.save();

This illustrates the ownership boundary; a real Save action handles validation, request errors and conflicts, and keeps the draft available when saving fails. The edit model tracks concurrency information when the service supplies it. Resolve a conflict explicitly rather than discarding someone's input.

Decide what happens when selection changes with a dirty draft: refuse the move, ask for confirmation, or retain an editor per record. The connected grid and form introduces the flow; retained sessions covers longer-lived editors. Dispose edit models when their owner releases them.

Share state without wiring controls together

Use a PageScope to publish named state such as the active selection or editor. PageRules can read that state to explain why an action is unavailable. For example, Save can depend on a dirty editor that is not already saving.

Use SharedFilters when charts, lists and summary widgets intentionally contribute criteria to the same workspace. Participation is explicit: a view chooses which contributions it consumes. The dashboard guide demonstrates that pattern.

JS or YAML composition can declare these relationships, while registered JS functions own requests, commands and cleanup. Choose it when several controls and their lifecycles form a reusable workspace; ordinary JS property assignment remains available for small pages.

Release resources with their owner

A screen that creates a collection owns releasing it with ctx.release(devices). A control borrowing that collection does not become its owner. The screen also stops its effects and disposes edit models that it created. Selection has no dispose method; release references to it when the owning workflow ends. Composition models can register cleanup with their lifetime so the same rule holds when a workspace is removed or recreated.

Avoid recreating the context or collection on every render. Keep stable record keys in custom templates, and let signals update the affected values. See composition ownership for activation and cleanup patterns.

Service and realtime boundaries

The connected views in this guide synchronize changes known to their shared context. Learning about writes made by another client also requires a service notification or refresh mechanism.

The current remote demo adapter does not subscribe to a production realtime channel. The agreed transport direction is WebTransport with SSE fallback; client integration awaits the server event and reconnect contract. Examples that simulate another user's change are demonstrations, not a deployed realtime service. Authentication is similarly application-configured; see authentication and session recovery.

For detailed APIs use DataContext, Collection, Selection and EditModel. The data core internals explain implementation details when you need them.

Continue with an editable screen

Next, connect a grid and form to apply the context, selection and draft ownership described here. Then compose a workspace when you want reusable named children, commands and lazy views around that model.