From child to parent
Communication between a child component and its parent is based on custom events.
The parent registers an event listener on the child. The child emits the event, which triggers the callbacks defined in the parent.
You can implement your own event system if you prefer, but Jadis includes a built-in method useEvents that ensures memory safety and supports strong typing with both TypeScript and JSDoc, making event handling clean, scalable, and type-safe.
Signature
The main idea is to pass an interface to the function's generic in TypeScript and in JSDoc. In JavaScript, we need to use an object that represents the interface as closely as possible.
In JavaScript an object or an array can not be described more precisely than Object and Array.
You can read more about typing events!
useEvents({eventName: PrimitiveConstructor});
// PrimitiveConstructor can be any primitive constructor:
// String, Number, Boolean, etc.useEvents<{eventName: Type}>();/** @type {import('@jadis/core').UseEventsHandler<{eventName: Type}>} */
useEvents();Parameter
- An
objectused only for typing in JavaScript, unused in TypeScript and JSDoc,{someEvent: PrimitiveConstructor}
const events = this.useEvents({someEvent: String});/** @type {import('@jadis/core').UseEventsHandler<{someEvent: string}>} */
events = this.useEvents();Return value
- An object with 2 methods: register and emit.
registeris used to subscribe to an eventemitis used to emit an event.
emit dispatches a CustomEvent on the child itself. It does not bubble or cross shadow roots, so only listeners registered on that element receive it; the callback receives the payload directly. register binds the listener to the child's killSignal: register it in the parent's onConnect(), which runs again after each reconnection.
UseEvents Usage
/// <reference types="@jadis/core/jsx-runtime" />
/** @jsxImportSource @jadis/core */
import { Jadis, createSelector } from '@jadis/core';
class ChildComponent extends Jadis {
static selector = createSelector('child-component');
events = this.useEvents({ someEvent: String });
templateHtml() {
return <button id="btn">Click me</button>;
}
onConnect() {
this.on(this.getElement('#btn'), 'click', () => {
this.events.emit('someEvent', 'Button clicked!');
});
}
}
class ParentComponent extends Jadis {
static selector = createSelector('parent-component');
refs = this.useRefs((ref) => ({
message: ref('#message'),
childComponent: ref('child-component'),
}));
templateHtml() {
return (
<>
<child-component></child-component>
<p id="message"></p>
</>
);
}
onConnect() {
this.refs.childComponent.events.register('someEvent', (data) => {
this.refs.message.textContent = data;
});
}
}
ChildComponent.register();
ParentComponent.register();import { Jadis } from '@jadis/core';
class ChildComponent extends Jadis {
static readonly selector = 'child-component';
events = this.useEvents<{
someEvent: string;
}>();
templateHtml(): Node {
return <button id="btn">Click me</button>;
}
onConnect(): void {
this.on(this.getElement('#btn'), 'click', () => {
this.events.emit('someEvent', 'Button clicked!');
});
}
}
class ParentComponent extends Jadis {
static readonly selector = 'parent-component';
refs = this.useRefs((ref) => ({
message: ref<HTMLParagraphElement>('#message'),
childComponent: ref<ChildComponent>('child-component'),
}));
templateHtml(): Node {
return (
<>
<child-component></child-component>
<p id="message"></p>
</>
);
}
onConnect(): void {
this.refs.childComponent.events.register('someEvent', (data) => {
this.refs.message.textContent = data;
});
}
}
ChildComponent.register();
ParentComponent.register();// @ts-check
/// <reference types="@jadis/core/jsx-runtime" />
/** @jsxImportSource @jadis/core */
import { Jadis, createSelector } from '@jadis/core';
class ChildComponent extends Jadis {
static selector = createSelector('child-component');
/** @type {import('@jadis/core').UseEventsHandler<{someEvent: string}>} */
events = this.useEvents();
templateHtml() {
return <button id="btn">Click me</button>;
}
onConnect() {
this.on(this.getElement('#btn'), 'click', () => {
this.events.emit('someEvent', 'Button clicked!');
});
}
}
class ParentComponent extends Jadis {
static selector = createSelector('parent-component');
refs = this.useRefs((ref) => ({
/** @type {HTMLParagraphElement} */
message: ref('#message'),
/** @type {ChildComponent} */
childComponent: ref('child-component'),
}));
templateHtml() {
return (
<>
<child-component></child-component>
<p id="message"></p>
</>
);
}
onConnect() {
this.refs.childComponent.events.register('someEvent', (data) => {
this.refs.message.textContent = data;
});
}
}
ChildComponent.register();
ParentComponent.register();Typing the Event System
The best way to type the event system is with TypeScript or JSDoc. With either, you can pass an interface that clearly describes what each event’s payload should be.
Note
If you don’t want to send a payload, you can define the event as undefined, like so:
{
someEvent: undefined;
}Even when using plain JavaScript, you can still provide partial typing by passing an object that defines each event name as a key and its type as the corresponding value.
Just use primitive constructors like:
- Number
- String
- Boolean
- BigInt
- Symbol
- Function
- Array
- Object
{
someEvent: String,
someOther: Object,
}