
Show how much work is waiting
Derive a navigation badge from the records it represents. Wildo counts the matching data for the person or organization and refreshes the count when the source changes.
This suits quantities such as unfinished assignments, whose truth already lives in business records.
Example — Count my unfinished tasks
The task badge counts assignments that are neither completed nor cancelled. Finishing an assignment changes the source data and therefore the count.
For engineers
Declare the query and its scope
Wonder Todos owns this badge in the tasks-manager module:
Selected from notification-badges.tasks-manager.ts; surrounding module configuration is omitted.
export const tasksManagerNotificationBadgeDefinitions: NotificationBadgeDefinition[] = [
{
identifier: USER_SELF_INCOMPLETE_TODOS_BADGE_IDENTIFIER,
scope: ResourcePrimaryScope.USER_SELF,
source: {
kind: NotificationBadgeSourceKind.DERIVED_QUERY,
resourceType: TasksManager_ResourceType.TODOS,
filter: { status: { $nin: [Todos_Status.COMPLETED, Todos_Status.CANCELLED] } },
scopeField: 'assignedToUserId',
},
display: {
mode: NotificationBadgeDisplayMode.COUNT,
tone: IndicatorVariant.INFO,
pulse: NotificationBadgePulse.ON_INCREASE,
},
},
];
Register the definition through the module’s notificationBadgeDefinitions; the shared registry aggregates it for backend and frontend. The navigation item names its key through notificationBadgeRef. The key combines scope and identifier, keeping user and organization badges distinct.
Let the data own the count
resourceType identifies the counted collection, filter states which records qualify and scopeField binds the count to the current badge owner. The backend recomputes on source mutations and connection initialization. For explicit multi-ID reassignment through the shared core path, Wildo captures prior owners before mutation and recomputes both prior and current owners after commit. Unassignment also refreshes the owner losing those records. The captured values belong to the successful transaction attempt; they do not change the object passed to single-row hooks. Arbitrary filter mutations and custom paths that bypass the core are outside that capture contract.
Use the shared mutation path so those hooks run. Direct database writes do not carry the operation’s notification behavior. For a quantity that is genuinely a stored event counter, such as unread events acknowledged separately from their source records, choose EVENT_COUNTER instead.