Component Communication

WakaPAC components communicate through desktop-style messaging for all inter-component communication. All messages — whether between siblings, parent to child, or child to parent — flow through msgProc, providing a single, consistent entry point for every component.

Component Hierarchies

Parent-child relationships are determined entirely by DOM nesting, not by the order in which components are created. When WakaPAC initialises a component, it walks the DOM tree to check whether that element is nested inside another component's element. If it is, the outer component becomes the parent.

<div id="app">                <!-- Root component -->
    <div id="sidebar">       <!-- Child of app -->
        <div id="menu">      <!-- Child of sidebar (grandchild of app) -->
        </div>
    </div>
    <div id="content">       <!-- Child of app -->
    </div>
</div>

<script>
    // Creation order is irrelevant — hierarchy is resolved from DOM structure
    wakaPAC('#menu',    { /* ... */ });
    wakaPAC('#app',     { /* ... */ });
    wakaPAC('#sidebar', { /* ... */ });
    wakaPAC('#content', { /* ... */ });
</script>

This produces two hierarchy chains: app → sidebar → menu and app → content. Each component has at most one parent, but can have any number of children.

Desktop-Style Messaging

The following sections cover how components are addressed, how messages are defined, sent, and received — including targeted, broadcast, and hierarchy-aware delivery.

Component Identification

Every component has a pacId, resolved in this order: the data-pac-id attribute, the element's id attribute, or an auto-generated identifier. This is the address you use when sending a message:

wakaPAC('#sidebar', {
    ready() {
        console.log(this.pacId); // "sidebar"
    }
});

Defining Custom Message Types

All custom message IDs must start at wakaPAC.MSG_USER (0x1000) to avoid collisions with built-in messages. Define them as named constants — inline numbers are not self-documenting:

const MSG_ITEM_SELECTED  = wakaPAC.MSG_USER + 1;
const MSG_REFRESH_DATA   = wakaPAC.MSG_USER + 2;
const MSG_THEME_CHANGED  = wakaPAC.MSG_USER + 3;

Sending Messages

postMessage — Asynchronous, Single Target (Preferred)

The message is queued through the DOM event system. Calling code continues immediately; the target's msgProc runs later in the event loop.

wakaPAC.postMessage('sidebar', MSG_REFRESH_DATA, 0, 0);
Parameter Type Description
targetId string Target component's pacId
messageId number Message type constant (must be ≥ MSG_USER)
wParam number First integer parameter — flags, IDs, small values
lParam number Second integer parameter — flags, IDs, small values
extended object Optional. Arbitrary data, available as event.detail in the receiver

sendMessage — Synchronous, Single Target

Calls the target's msgProc directly, before the next line of calling code executes. Uses the same parameters as postMessage above. Use only when you genuinely need the target to have processed the message before you continue.

// Target's msgProc has already run by this point
wakaPAC.sendMessage('data-store', MSG_REFRESH_DATA, 0, 0);

broadcastMessage — Synchronous, All Components

Delivers to every registered component in the application. Omits targetId; otherwise identical to postMessage. Use for genuinely global state changes.

wakaPAC.broadcastMessage(MSG_THEME_CHANGED, isDarkMode ? 1 : 0, 0);

Receiving Messages

msgProc receives all messages delivered to a component — including built-in events like mouse moves and key presses. Components that only handle custom messages should guard at the top to avoid unnecessary processing:

wakaPAC('#sidebar', {
    darkMode: false,

    msgProc(event) {
        // Skip all built-in toolkit messages
        if (event.message < wakaPAC.MSG_USER) {
            return;
        }

        switch (event.message) {
            case MSG_REFRESH_DATA:
                this.loadData();
                break;

            case MSG_THEME_CHANGED:
                this.darkMode = event.wParam === 1;
                break;
        }
    }
});

Return false to cancel default toolkit handling for cancellable events (clicks, key events, form submission). For custom messages this has no effect.

sendMessageToParent / postMessageToParent — Child to Parent

Delivers a message directly to the parent component without needing to know its pacId. If the component has no parent, the message is silently dropped. Typically called from inside msgProc to forward an event up the hierarchy:

msgProc(event) {
    if (event.message === MSG_ITEM_ADDED) {
        wakaPAC.sendMessageToParent(this.pacId, MSG_ITEM_ADDED, 0, 0, { item: event.detail.item });
    }
}

The signature is wakaPAC.sendMessageToParent(pacId, messageId, wParam, lParam, extended), where pacId is the child sending the message. All other parameters match postMessage. Use postMessageToParent for the asynchronous equivalent — same parameters, queued delivery.

broadcastToChildren — Parent to All Descendants

Sends a message to all descendants of a given component. The root component itself does not receive the message — only its children, grandchildren, and so on. Traversal is depth-first.

toggleTheme() {
    this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light';
    wakaPAC.broadcastToChildren(this.pacId, MSG_THEME_CHANGED, 0, 0, {
        theme: this.currentTheme
    });
}

The signature is wakaPAC.broadcastToChildren(pacId, messageId, wParam, lParam, extended), where pacId is the root whose full subtree receives the message. All other parameters match postMessage.

The same event object is reused across all dispatches. If a child's msgProc mutates event.detail, subsequent siblings will see the mutation.

Message Event Structure

The event object received in msgProc contains message, wParam, lParam, detail, and timestamp as documented here, plus additional properties (target, originalEvent, pacId) relevant to DOM-originated events. See the msgProc page for the full event structure reference.

Best Practices

  • Guard against built-in messages. Add if (event.message < wakaPAC.MSG_USER) return; at the top of any msgProc that only handles custom messages. Without it, every mouse move and keypress calls your handler unnecessarily.
  • Use named constants. Define const MSG_ITEM_SELECTED = wakaPAC.MSG_USER + 1 at the top of your script. Inline numbers are not self-documenting and break when you insert a new message between existing ones.
  • Include senderPacId in the payload. When sending from a child to a parent, include { senderPacId: this.pacId, ... } in the extended data. The parent can then respond directly by pacId without coupling to any specific child implementation.
  • Send only what's needed. Avoid passing entire component state through a message. Narrow payloads make contracts easier to understand and debug.
  • Broadcast sparingly. broadcastMessage hits every component. If only two or three components care, target them directly.
  • Watch for loops. A sends to B, B sends back to A — this is the easiest way to create an infinite message cycle. Add a guard or a flag to prevent re-entrancy.