Concorde decorator — @awaitConnectedAncestors and @dispatchConnectedEvent. The @awaitConnectedAncestors and @dispatchConnectedEvent decorators delay a web component's initialization until its matching ancestors have executed their connectedCallback. This is when contextual e Doc ID: docs/_decorators/wait-for-ancestors. Keywords: Concorde, supersoniks, docs/_decorators/wait-for-ancestors, wait-for-ancestors, @awaitConnectedAncestors and @dispatchConnectedEvent, decorator, awaitConnectedAncestors and @dispatchConnectedEvent, @awaitConnectedAncestors, @dispatchConnectedEvent, connectedCallback, sonic-connected, @dispatchConnectedEvent(). URL: https://concorde.supersoniks.org/crawl/docs/_decorators/wait-for-ancestors.html.
@awaitConnectedAncestors and @dispatchConnectedEvent
The @awaitConnectedAncestors and @dispatchConnectedEvent decorators delay a web component's initialization until its matching ancestors have executed their connectedCallback. This is when contextual elements (publisher, dataProvider, etc.) are configured.
Principle
When a child component attaches to the DOM, its ancestors may not yet be initialized (especially if custom element definitions are loaded asynchronously). The @awaitConnectedAncestors decorator delays the component's connectedCallback until all ancestors matching the provided CSS selectors have executed their connectedCallback.
The @dispatchConnectedEvent decorator allows ancestors to signal they are ready by dispatching the sonic-connected event at the end of their connectedCallback. The event bubbles, so it can be listened to from anywhere (e.g. document.addEventListener(CONNECTED, handler)).
Ancestors that are not web components (no hyphen in tag name) are considered connected by default and do not need to emit the event.
Usage
Import
import { awaitConnectedAncestors, dispatchConnectedEvent, ancestorAttribute } from "@supersoniks/concorde/decorators";
Basic example
An ancestor container decorated with @dispatchConnectedEvent() signals when it is ready. A child component decorated with @awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]") waits for this container to be initialized before initializing itself. Parameters are CSS selectors (element.matches()).
The parent is registered via customElements.define() (vanilla JS) rather than @customElement, so it can be defined later—e.g. when the user clicks a button. This demonstrates the child waiting until the parent exists.
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
// Parent: registered later via customElements.define(), not @customElement
@dispatchConnectedEvent()
export class DemoWaitAncestorContainer extends LitElement {
render() {
return html`<slot></slot>`;
}
}
// Child: waits for parent before initializing
@customElement("demo-wait-ancestor-value")
@awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]")
export class DemoWaitAncestorValue extends LitElement {
@ancestorAttribute("dataProvider")
dataProvider: string | null = null;
@state() initializedAt: string = "";
connectedCallback() {
super.connectedCallback();
this.initializedAt = new Date().toISOString();
}
render() {
return html`
<p>DataProvider from ancestor: <strong>${this.dataProvider || "—"}</strong></p>
<p>Initialized at: ${this.initializedAt || "(waiting for parent…)"}</p>
`;
}
}
// Demo section: register parent via customElements.define() when user clicks
@customElement("demo-wait-ancestors-section")
export class DemoWaitAncestorsSection extends LitElement {
registerParent() {
if (!customElements.get("demo-wait-ancestor-container")) {
customElements.define("demo-wait-ancestor-container", DemoWaitAncestorContainer);
}
}
render() {
return html`
<sonic-button @click=${this.registerParent}>Register parent component</sonic-button>
<demo-wait-ancestor-container dataProvider="waitAncestorDemo">
<demo-wait-ancestor-value></demo-wait-ancestor-value>
</demo-wait-ancestor-container>
`;
}
}
<docs-demo-sources for="demo-wait-ancestors-section"></docs-demo-sources>
<demo-wait-ancestors-section></demo-wait-ancestors-section>
Multiple ancestors
The child waits for all specified ancestors. Register outer first, then inner — the child initializes only when both are ready.
<demo-wait-ancestors-multi-section></demo-wait-ancestors-multi-section>
Ancestors already connected
When the parent is defined at load and already in the DOM, the child initializes immediately (no delay).
Static (both in DOM from start):
<docs-demo-sources for="demo-wait-ancestors-static-section"></docs-demo-sources>
<demo-wait-ancestors-static-section></demo-wait-ancestors-static-section>
Dynamic (child added on button click):
<docs-demo-sources for="demo-wait-ancestors-ready-section"></docs-demo-sources>
<demo-wait-ancestors-ready-section></demo-wait-ancestors-ready-section>
CSS selector support
Parameters are CSS selectors matched via element.matches() — e.g. "sonic-subscriber", "sonic-subscriber[dataProvider]", ".my-container", or multiple: "sonic-subscriber", "sonic-sdui".
Behavior
- Ancestor search uses CSS selectors (
element.matches(selector)) — supports tag names, classes, attributes, combinators, etc. - Traversal includes shadow roots (parentNode / host)
- Non-web components (no hyphen in tag name) are considered connected by default
- For web component ancestors, it waits for
customElements.whenDefined(tagName)and thesonic-connectedevent (or a timeout as fallback) - If no matching ancestor is found, the original
connectedCallbackis called immediately - Ancestors that do not emit
sonic-connectedtrigger the fallback after the timeout (compatibility with existing components)
Use cases
These decorators are particularly useful for:
- sonic-value inside a sonic-subscriber: the value waits for the subscriber to configure its publisher
- Components inside sonic-sdui: wait for the SDUI to load and configure its context
- Any component depending on context provided by an ancestor custom element
Listening to the connected event
The sonic-connected event bubbles, so you can listen to it from anywhere:
import { CONNECTED } from "@supersoniks/concorde/decorators";
someConnectable.addEventListener(CONNECTED, (e) => {
console.log("Component connected:", e.target);
});
Notes
- These decorators apply only to web components (classes extending
HTMLElement) - The fallback timeout ensures compatibility with components that do not yet use
@dispatchConnectedEvent - Traversal includes shadow roots
- For a guarantee without relying on the timeout, decorate ancestors (Subscriber, Fetcher, etc.) with
@dispatchConnectedEvent