Document .once() method and 'loaded' event, update npm packages

Update npm packages including @jqhtml/core

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-03-06 23:50:01 +00:00
parent 198cd42ce1
commit 3294fc7337
22 changed files with 292 additions and 82 deletions

View File

@@ -974,22 +974,36 @@ CREATING COMPONENTS
LIFECYCLE EVENT CALLBACKS
External code can register callbacks for lifecycle events using
the .on() method. Useful when you need to know when a component
reaches a certain state.
the .on() and .once() methods. Useful when you need to know when
a component reaches a certain state.
Supported Events:
'render' - Fires after render phase completes
'create' - Fires after create phase completes
'load' - Fires after load phase completes (data available)
'loaded' - Fires after on_loaded() completes (this.data set and frozen)
'ready' - Fires after ready phase completes (fully initialized)
Methods:
.on(event, callback) - Register callback, fires on every occurrence
.once(event, callback) - Register callback, fires only once
Both methods share the same behavior:
- If the event already fired, callback executes immediately
- Returns this for chaining
- Custom events also supported (see CUSTOM COMPONENT EVENTS)
The difference: .on() fires on every future occurrence (useful for
re-renders), while .once() fires at most once and then removes itself.
Awaiting Data in Async Methods:
When a method needs data that may not be loaded yet, await the 'load'
event. If the event already fired, the callback executes immediately:
When a method needs data that may not be loaded yet, await the
'loaded' event. If the event already fired, the callback executes
immediately. Use .once() since you only need notification once:
async get_display_name() {
// Wait for on_load() to complete if not already
await new Promise(resolve => this.on('load', resolve));
await new Promise(resolve => this.once('loaded', resolve));
return `${this.data.first_name} ${this.data.last_name}`;
}
@@ -1004,6 +1018,11 @@ LIFECYCLE EVENT CALLBACKS
// Access component data, DOM, etc.
});
// Fire only once (even across re-renders)
component.once('ready', (comp) => {
console.log('First ready!', comp);
});
// Chain directly
$('#my-component').component().on('ready', (component) => {
console.log('User data:', component.data.user);
@@ -1013,17 +1032,16 @@ LIFECYCLE EVENT CALLBACKS
component
.on('render', () => console.log('Dashboard rendered'))
.on('create', () => console.log('Dashboard created'))
.on('load', () => console.log('Dashboard data loaded'))
.on('loaded', () => console.log('Dashboard data loaded'))
.on('ready', () => console.log('Dashboard ready'));
Key behaviors:
- Immediate execution: If lifecycle event already occurred,
callback fires immediately
- Future events: Callback also registers for future occurrences
(useful for re-renders)
callback fires immediately (both .on() and .once())
- .on(): Callback persists for future occurrences (re-renders)
- .once(): Callback fires once then auto-removes
- Multiple callbacks: Can register multiple for same event
- Chaining: Returns this so you can chain .on() calls
- Custom events also supported (see CUSTOM COMPONENT EVENTS)
- Chaining: Both return this so you can chain calls
Example - Wait for component initialization:
// React when dashboard component is ready
@@ -1031,14 +1049,12 @@ LIFECYCLE EVENT CALLBACKS
console.log('Dashboard loaded:', this.data);
});
// Process component data after load
$('#data-grid').component().on('load', (comp) => {
// One-time notification when data loads
$('#data-grid').component().once('loaded', (comp) => {
const total = comp.data.items.reduce((sum, i) => sum + i.value, 0);
$('#total').text(total);
});
Available in JQHTML v2.2.81+
CUSTOM COMPONENT EVENTS
Components can fire and listen to custom events using the jqhtml event
bus. Unlike jQuery's .trigger()/.on(), the jqhtml event bus guarantees
@@ -1059,6 +1075,11 @@ CUSTOM COMPONENT EVENTS
console.log('Event data:', data);
});
// Listen only once
this.sid('child_component').once('my_event', (component, data) => {
console.log('First occurrence only');
});
From external code:
$('#element').component().on('my_event', (component, data) => {
// Handle event
@@ -1066,6 +1087,7 @@ CUSTOM COMPONENT EVENTS
Callback Signature:
.on('event_name', (component, data) => { ... })
.once('event_name', (component, data) => { ... })
- component: The component instance that fired the event
- data: Optional data passed as second argument to trigger()
@@ -1109,14 +1131,14 @@ EVENT HANDLER PLACEMENT
Where to register event handlers depends on what you're attaching to:
Component Events (this.on()) - Register in on_create():
Component Events (this.on()/this.once()) - Register in on_create():
Component events attach to this.$ which persists across re-renders.
Register once in on_create() to avoid infinite loops.
on_create() {
// Component event - register once
this.on('file-drop', (_, data) => this._handle(data));
this.on('custom-event', (_, data) => this._process(data));
this.once('initialized', () => this._setup());
}
DANGER: If registered in on_ready() and the handler triggers
@@ -1146,9 +1168,9 @@ EVENT HANDLER PLACEMENT
}
Summary:
this.on('event', ...) → on_create() (component events)
this.sid('child').on('event') → on_ready() (child component events)
this.$sid('elem').on('click') → on_render() (child DOM events)
this.on/once('event', ...) → on_create() (component events)
this.sid('child').on/once('event') → on_ready() (child component events)
this.$sid('elem').on('click') → on_render() (child DOM events)
$REDRAWABLE ATTRIBUTE - LIGHTWEIGHT COMPONENTS
Convert any HTML element into a re-renderable component using the

View File

@@ -222,7 +222,7 @@ BREADCRUMB SYSTEM
// Helper to await loaded data
async _await_loaded() {
if (this.data.contact && this.data.contact.id) return;
await new Promise(resolve => this.on('load', resolve));
await new Promise(resolve => this.once('loaded', resolve));
}
async page_title() {
@@ -248,17 +248,17 @@ BREADCRUMB SYSTEM
Awaiting Loaded Data:
Breadcrumb methods are called BEFORE on_load() completes. If a method
needs loaded data (e.g., contact name), it must await the 'load' event:
needs loaded data (e.g., contact name), it must await the 'loaded' event:
async _await_loaded() {
// Check if data is already loaded
if (this.data.contact && this.data.contact.id) return;
// Otherwise wait for 'load' event
await new Promise(resolve => this.on('load', resolve));
// Otherwise wait for 'loaded' event
await new Promise(resolve => this.once('loaded', resolve));
}
The 'load' event fires immediately if already past that lifecycle
phase, so this pattern is safe to call multiple times.
The 'loaded' event fires after on_load() completes and this.data is
set. Using once() is appropriate since it only needs to fire once.
RSX_BREADCRUMB_RESOLVER
Framework class that handles breadcrumb resolution with caching.