Data Service
The dataService is the central injectable service in upp-data, managing the data lifecycle, object storage, view caching, synchronization, and session state. It acts as the hub connecting all other components.
dataService
Injectable: providedIn: 'root'
Constructor Dependencies
constructor(
public injector: Injector,
public clock: clockService,
private lang: languageService,
private adhoc: adhocService,
private logs: logsService,
private state: stateService,
private view: viewService,
private sync: syncService
)
Properties
| Property | Type | Description |
|---|---|---|
store | dataStorage | Lazy-initialized two-tiered object storage |
alives | aliveItems | WeakRef-based ViewObject cache with automatic GC cleanup |
events | eventsNotifier | Event logging system for drawer, login, and guest actions |
session | Session | null | The current active session |
user | User | null | The currently authenticated user |
place | Place | null | The currently active place/business |
qrcode | QrCode | null | The currently active QR code (guest mode) |
serial | number | CRC32-based serial from place.objid + device for ticket numbering |
injector | Injector | Angular injector exposed for ViewObjects to resolve services lazily |
Observables
| Observable | Type | Description |
|---|---|---|
OnSessionChanged | Subject<void> | Emitted when the session is established or changed |
OnSessionReleased | Subject<boolean> | Emitted when the session ends; boolean indicates whether it expired |
OnReloadRequested | Subject<void> | Emitted when the server requests a full reload |
OnUserChanged | Subject<void> | Emitted when the user entity is set or changed |
OnPlaceChanged | Subject<void> | Emitted when the place entity is set or changed |
OnQrCodeChanged | Subject<void> | Emitted when the QR code entity is set or changed |
OnRefreshPercent | Observable<number> | Proxied from syncService — sync progress (0–100) |
OnRefreshCompleted | Observable<void> | Proxied from syncService — sync cycle completed |
OnCommitCompleted | Observable<void> | Proxied from syncService — commit acknowledged by server |
Session Lifecycle
Start(session, access)
Begins a data session:
- Creates a
Sessionobject in thestore. - Starts the sync service:
sync.Start(session). - Based on
accessmode:LOGIN: Waits forsync.SetUser(user), then callsSetUser().GUEST: Waits forsync.SetQrCode(qrcode), then callsSetQrCode().
- Emits
OnSessionChanged.
Stop(expired)
Ends the data session:
- Releases the current session.
- Stops the sync service:
sync.Stop(). - Clears the object storage:
store.Clear(). - Emits
OnSessionReleased(expired).
Ready(): Promise<void>
Waits until the sync service signals readiness (sync.IsReady), using firstValueFrom with take(1).
Entity Management
Each setter follows the same pattern:
async SetUser(user: User): Promise<void>
async SetPlace(place: Place): Promise<void>
async SetQrCode(qrcode: QrCode): Promise<void>
The flow:
- Wait for the entity to be fully loaded (its
OnInitializeevent). - Update the internal reference (
this._user,this._place,this._qrcode). - Add the entity to the store.
- Reset the sync stage:
sync.ResetStage(). - Emit the corresponding change observable.
- SetPlace additionally: logs a
LOGINevent, preloads the place's language pack.
Commit Flow
The commit flow is the path data changes take from a DataObject modification to the server:
Commit(changes: CommitChange[])
Validates and queues changes for submission:
- For each
CommitChange, checks that allrequires(dependencies) are safe:- The dependency has an
objid(already resolved), or - The dependency is already in the
ToCommitmap, or - The dependency is in the store and currently
InCommit.
- The dependency has an
- Adds validated changes to the internal
_tocommitmap (keyed bytable@objidortable@uuid).
Flush(force: boolean): Promise<boolean>
Sends accumulated changes to the server:
- Collects all entries from the
_tocommitmap. - Delegates to
sync.Flush(changes, force). - Returns
trueon success.
FetchByObjid(table, objid): Promise<BaseObject>
Ad-hoc server request to fetch a single object by table and objid, independent of the sync cycle.
Transaction Support
get transaction(): dataTransaction
Creates a new dataTransaction for batching multiple changes atomically.
dataStorage
A two-tiered object store that separates objects by resolution state:
Internal Structure
type StorageMap = { [table: string]: Map<string, BaseObject> };
private _objects: {
resolved: StorageMap; // objects with numeric objid from server
awaiting: StorageMap; // objects with temporary _uuid (client-created)
};
Methods
| Method | Signature | Description |
|---|---|---|
GetByRef | (table, objid, uuid) → BaseObject | null | Searches by uuid in awaiting first, then by objid in resolved |
GetObject | (dataobject) → BaseObject | null | Searches by objref in both tiers |
AddObject | (dataobject) → boolean | Routes to resolved (if numeric objid) or awaiting (otherwise). Skips copies (CopyOf is set) |
DelObject | (dataobject) → void | Removes from the appropriate tier based on objid |
SetObject | (dataobject) → void | Delete + Add (update in place) |
Resolve | (dataobject) → void | Moves an object from awaiting to resolved after the server assigns an objid. Calls OnResolve() on any displaced object |
Replace | (dataobject, uuid) → void | Replaces an existing entry in resolved with a new version. Handles uuid-based lookups in awaiting |
Clear | () → void | Empties both maps completely |
Resolution Flow
When a client-created object (e.g., a new Ticket) is committed to the server:
- The object starts in awaiting with a temporary
_uuid. - The server responds with a permanent
objid. Resolve()is called:- Removes the object from awaiting.
- Sets
objidfrom the server response. - If an object with the same
objidalready exists in resolved, it is removed and itsOnResolve()is called to update any references. - The object is added to resolved.
dataTransaction
Manages atomic groups of changes to be committed together:
class dataTransaction {
private _trstatus: 'OPEN' | 'CLOSED' = 'OPEN';
private _tocommit = new Map<string, CommitChange>();
}
Methods
push(item: BaseObject, force?: boolean): Promise<boolean>
Adds an object and its dependent changes to the transaction:
- If
forceis true, callsitem.ForceUpdate()to mark it for update. - Collects all
CommitChangeobjects fromitem.Commit(includes children and dependencies). - Adds each change to the transaction's
_tocommitmap. - Validates that all dependencies are safe:
- The dependency has an
objid, or - The dependency is in this transaction's
_tocommit, or - The dependency is in
dataService.ToCommit, or - The dependency is in the store and
InCommit.
- The dependency has an
- Throws if any dependency is unresolved.
flush(force?: boolean): Promise<boolean>
Commits all accumulated changes:
- Merges the transaction's
_tocommitintodataService.ToCommit. - Calls
dataService.Flush(force). - Returns
trueon success.
Usage Example
const tx = this.data.transaction;
const ticket = new Ticket(null, this.data);
ticket.place = this.data.place;
ticket.status = 'AC';
await tx.push(ticket);
const product = new TicketProduct(null, this.data);
product.ticket = ticket;
product.product = someProduct;
product.amount = 2;
await tx.push(product);
await tx.flush();
aliveItems
Manages ViewObject instances using weak references to allow garbage collection when views are no longer needed by the UI.
Internal Structure
class aliveItems {
private _alivemap = new WeakMap<DataObject, WeakRef<ViewObject<DataObject>>>();
private finalizationRegistry = new FinalizationRegistry<DataObject>(...);
}
Methods
| Method | Description |
|---|---|
get(object) | Returns the existing ViewObject if still alive (via WeakRef.deref()), or creates a new one by calling object.View, stores it as a WeakRef, registers it with FinalizationRegistry, and calls doRegister(). Returns null if object is null. |
has(object) | Returns true if a WeakRef exists and the referenced ViewObject is still alive. Logs a warning if the ref exists but the object has been GC'd. |
set(object) | (Private) Creates and stores a new ViewObject via object.View, registers it for finalization tracking. |
Lifecycle
Debug Information
get itemssize(): number // _register_count - _released_count
logstatus(): void // Logs total registered and released counts