Component Hierarchy & Communication

WakaPAC components form hierarchies based on DOM structure. Parent and child components communicate through explicit message passing - children notify parents of events, parents send commands to children.

Why Use Component Hierarchy?

Component hierarchy lets you build complex applications from smaller, focused components while maintaining clear communication paths:

  • Encapsulation: Each component manages its own state and behavior
  • Reusability: Child components can be used in different parent contexts
  • Clear data flow: Explicit message passing instead of shared global state
  • Separation of concerns: Parent coordinates, children handle specifics

Example: A dashboard (parent) contains multiple widget components (children). Widgets notify the dashboard when they need data, and the dashboard broadcasts theme changes to all widgets.

How Hierarchy Works

Parent-child relationships are determined by DOM structure, not code order:

<div id="parent">
    <!-- Parent contains child in DOM -->
    <div id="child"></div>
</div>

<script>
    // Create in any order - hierarchy is determined by DOM nesting
    wakaPAC('#child', {
        // Child component
    });

    wakaPAC('#parent', {
        // Parent component - automatically detects #child as its child
    });
</script>

WakaPAC walks the DOM tree during initialization to determine which components contain other components. A component becomes a parent if its DOM element contains another component's DOM element.

Child to Parent Communication

Children notify their parent using notifyParent(type, data). The parent receives notifications in its receiveFromChild(type, data, childComponent) method:

<div id="parent-app">
    <h2>Shopping Cart</h2>
    <p>Items: {{itemCount}}</p>
    <p>Total: ${{totalPrice}}</p>

    <div id="product-selector">
        <h3>Products</h3>
        <button data-pac-bind="click: addWidget">Add Widget ($10)</button>
        <button data-pac-bind="click: addGadget">Add Gadget ($20)</button>
    </div>
</div>

<script>
    wakaPAC('#product-selector', {
        addWidget() {
            this.notifyParent('add-item', {
                name: 'Widget',
                price: 10
            });
        },

        addGadget() {
            this.notifyParent('add-item', {
                name: 'Gadget',
                price: 20
            });
        }
    });

    wakaPAC('#parent-app', {
        itemCount: 0,
        totalPrice: 0,

        // Called when any child sends a notification
        receiveFromChild(type, data, childComponent) {
            if (type === 'add-item') {
                this.itemCount++;
                this.totalPrice += data.price;
                console.log('Added:', data.name);
            }
        }
    });
</script>
Note: If a component doesn't define receiveFromChild, notifications from children are silently ignored. This is intentional - not all parents need to handle child events.

Parent to Child Communication

Parents send commands to children using two methods:

Broadcast to All Children

Use notifyChildren(command, data) to send a message to all child components:

wakaPAC('#parent', {
    applyTheme() {
        // All children receive this command
        this.notifyChildren('theme-change', {
            theme: 'dark',
            accentColor: '#4a90e2'
        });
    }
});

Send to Specific Child

Use notifyChild(selector, command, data) to target a specific child:

wakaPAC('#parent', {
    focusSearchBar() {
        // Only the search component receives this
        this.notifyChild('search-bar', 'focus', null);
    }
});

Complete Example

<div id="parent-app">
    <h2>Dashboard</h2>
    <button data-pac-bind="click: toggleTheme">Toggle Theme</button>
    <button data-pac-bind="click: refreshWidget1">Refresh Widget 1</button>

    <div id="widget-1" data-pac-id="widget-1">
        <h3>Widget 1 - Theme: {{theme}}</h3>
        <p>Refreshed: {{refreshCount}} times</p>
    </div>

    <div id="widget-2" data-pac-id="widget-2">
        <h3>Widget 2 - Theme: {{theme}}</h3>
        <p>Refreshed: {{refreshCount}} times</p>
    </div>
</div>

<script>
    wakaPAC('#widget-1', {
        theme: 'light',
        refreshCount: 0,

        receiveFromParent(command, data) {
            if (command === 'theme-change') {
                this.theme = data.theme;
            } else if (command === 'refresh') {
                this.refreshCount++;
            }
        }
    });

    wakaPAC('#widget-2', {
        theme: 'light',
        refreshCount: 0,

        receiveFromParent(command, data) {
            if (command === 'theme-change') {
                this.theme = data.theme;
            }
            // Note: widget-2 doesn't handle 'refresh' command
        }
    });

    wakaPAC('#parent-app', {
        currentTheme: 'light',

        toggleTheme() {
            this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light';

            // Broadcast to all children
            this.notifyChildren('theme-change', {
                theme: this.currentTheme
            });
        },

        refreshWidget1() {
            // Send only to widget-1
            this.notifyChild('widget-1', 'refresh', null);
        }
    });
</script>
Note: If a child doesn't define receiveFromParent, commands from the parent are silently ignored. Children only handle the commands they care about.

Communication API Reference

Direction Method Parameters Use Case
Child → Parent notifyParent(type, data) type: string
data: any
User actions, form submission, validation errors
Parent → All Children notifyChildren(command, data) command: string
data: any
Theme changes, global state updates, broadcasts
Parent → Specific Child notifyChild(selector, command, data) selector: string (data-pac-id value)
command: string
data: any
Focus management, targeted component commands

Handler Methods

Method Signature Called When
receiveFromChild receiveFromChild(type, data, childComponent) Any child calls notifyParent()
receiveFromParent receiveFromParent(command, data) Parent calls notifyChildren() or notifyChild()

Practical Patterns

Coordinating Multiple Components

Parent acts as a coordinator between sibling components:

// Product list notifies parent when item is selected
wakaPAC('#product-list', {
    selectProduct(product) {
        this.notifyParent('product-selected', { product });
    }
});

// Product details receives data from parent
wakaPAC('#product-details', {
    currentProduct: null,

    receiveFromParent(command, data) {
        if (command === 'show-product') {
            this.currentProduct = data.product;
        }
    }
});

// Parent coordinates between the two siblings
wakaPAC('#app', {
    receiveFromChild(type, data, child) {
        if (type === 'product-selected') {
            // Route data to product details component
            this.notifyChild('product-details', 'show-product', data);
        }
    }
});

Form Validation Flow

Child form components validate and notify parent of results:

wakaPAC('#email-field', {
    email: '',

    validate() {
        const isValid = this.email.includes('@');
        this.notifyParent('field-validated', {
            field: 'email',
            valid: isValid,
            value: this.email
        });
    }
});

wakaPAC('#registration-form', {
    formValid: false,
    fields: {},

    receiveFromChild(type, data) {
        if (type === 'field-validated') {
            this.fields[data.field] = data;
            this.checkFormValidity();
        }
    },

    checkFormValidity() {
        this.formValid = Object.values(this.fields)
            .every(field => field.valid);
    }
});

Best Practices

  • Use descriptive message types: Name events clearly like 'form-submit', 'validation-error', not generic 'event' or 'update'
  • Keep messages focused: Send only necessary data - avoid passing entire component state
  • Handle missing handlers gracefully: Components don't need to define receiveFromChild or receiveFromParent if they don't use them
  • Avoid deep hierarchies: More than 2-3 levels becomes difficult to debug and maintain
  • Document communication contracts: Comment which messages each component sends and expects to receive
  • Consider alternatives for siblings: If siblings need to communicate frequently, they might belong in the same component

Hierarchy Detection Details

WakaPAC determines parent-child relationships during component initialization:

  1. When a component is created, WakaPAC checks if its container element is inside another component's container
  2. If yes, the containing component becomes the parent
  3. The relationship is established regardless of component creation order
  4. Each component can have zero or one parent, but multiple children

Example hierarchy:

<div id="app">              <!-- Level 1: Root -->
    <div id="sidebar">     <!-- Level 2: Child of app -->
        <div id="menu">    <!-- Level 3: Child of sidebar -->
        </div>
    </div>
    <div id="content">     <!-- Level 2: Child of app -->
    </div>
</div>

This creates the hierarchy: appsidebarmenu and appcontent