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.
Component hierarchy lets you build complex applications from smaller, focused components while maintaining clear communication paths:
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.
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.
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>
receiveFromChild, notifications from children are silently ignored. This is intentional - not all parents need to handle child events.
Parents send commands to children using two methods:
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'
});
}
});
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);
}
});
<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>
receiveFromParent, commands from the parent are silently ignored. Children only handle the commands they care about.
| Direction | Method | Parameters | Use Case |
|---|---|---|---|
| Child → Parent | notifyParent(type, data) |
type: stringdata: any
|
User actions, form submission, validation errors |
| Parent → All Children | notifyChildren(command, data) |
command: stringdata: any
|
Theme changes, global state updates, broadcasts |
| Parent → Specific Child | notifyChild(selector, command, data) |
selector: string (data-pac-id value)command: stringdata: any
|
Focus management, targeted component commands |
| Method | Signature | Called When |
|---|---|---|
receiveFromChild |
receiveFromChild(type, data, childComponent) |
Any child calls notifyParent() |
receiveFromParent |
receiveFromParent(command, data) |
Parent calls notifyChildren() or notifyChild() |
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);
}
}
});
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);
}
});
'form-submit', 'validation-error', not generic 'event' or 'update'receiveFromChild or receiveFromParent if they don't use themWakaPAC determines parent-child relationships during component initialization:
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: app → sidebar → menu and app → content