Concorde decorator — @autoSubscribe. > Legacy: prefer @subscribe + DataProviderKey. Examples below may still show PublisherManager for existing codebases. Doc ID: docs/_decorators/auto-subscribe. Keywords: Concorde, supersoniks, docs/_decorators/auto-subscribe, auto-subscribe, @autoSubscribe, decorator, autoSubscribe, DataProviderKey, PublisherManager, ;
}
render() {
return html. URL: https://concorde.supersoniks.org/crawl/docs/_decorators/auto-subscribe.html.
@autoSubscribe
Legacy: prefer @subscribe +
DataProviderKey. Examples below may still showPublisherManagerfor existing codebases.
The @autoSubscribe decorator automatically detects which publishers are accessed within a method and subscribes to them. When any of these publishers change, the method is automatically re-executed.
Principle
This decorator wraps a method to track which publishers are accessed during its execution. It then subscribes to all accessed publishers, and when any of them change, the method is re-executed. This provides automatic reactivity without manually managing subscriptions.
Usage
Import
import { autoSubscribe } from "@supersoniks/concorde/decorators";
Basic example
@customElement("demo-auto-subscribe")
export class DemoAutoSubscribe extends LitElement {
static styles = [tailwind];
@state() displayText: string = "";
@state() computedValue: number = 0;
@autoSubscribe()
updateDisplay() {
const value1 = PublisherManager.get("autoValue1").get() || 0;
const value2 = PublisherManager.get("autoValue2").get() || 0;
this.computedValue = value1 + value2;
this.displayText = `${value1} + ${value2} = ${this.computedValue}`;
}
render() {
return html`
<p><strong>${this.displayText}</strong></p>
<div>
<sonic-button @click=${() => this.randomizeValue("autoValue1")}>
Randomize Value 1
</sonic-button>
<sonic-button @click=${() => this.randomizeValue("autoValue2")}>
Randomize Value 2
</sonic-button>
</div>
`;
}
randomizeValue(publisherId: string) {
const value = PublisherManager.get(publisherId);
value.set(Math.floor(Math.random() * 100));
}
}
<docs-demo-sources for="demo-auto-subscribe"></docs-demo-sources>
<demo-auto-subscribe></demo-auto-subscribe>
Example with render method
@customElement("reactive-view")
export class ReactiveView extends LitElement {
@autoSubscribe()
render() {
const data = PublisherManager.get("myData");
const config = PublisherManager.get("config");
// This render method will be automatically re-executed
// when myData or config change
const value = data.get()?.value || 0;
const multiplier = config.get()?.multiplier || 1;
return html`
<div>
<h1>Result: ${value * multiplier}</h1>
<p>Value: ${value}</p>
<p>Multiplier: ${multiplier}</p>
</div>
`;
}
}
How it works
- First execution: When the method is called,
PublisherManager.collectModifiedPublisher()is used to track which publishers are accessed - Subscription: After execution, the decorator subscribes to all detected publishers
- Re-execution: When any subscribed publisher changes, the method is automatically called again
- Cleanup: On
disconnectedCallback, all subscriptions are automatically removed
Behavior
- The method is automatically called on
connectedCallback - The method is re-executed whenever any accessed publisher changes
- Subscriptions are managed automatically (no manual cleanup needed)
- Only publishers accessed during method execution are subscribed to
- The decorator uses
queueMicrotaskto batch multiple updates and avoid unnecessary re-renders
Use cases
This decorator is particularly useful for:
- Reactive rendering where the render method depends on multiple publishers
- Data transformation that needs to update when source data changes
- Computed properties that depend on multiple data sources
- Automatic synchronization between publishers and component state
Complete example
import { html, LitElement } from "lit";
import { customElement } from "lit/decorators.js";
import { autoSubscribe } from "@supersoniks/concorde/decorators";
import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
@customElement("shopping-cart")
export class ShoppingCart extends LitElement {
items: any[] = [];
total: number = 0;
discount: number = 0;
@autoSubscribe()
calculateTotal() {
const cart = PublisherManager.get("cart");
const promo = PublisherManager.get("promo");
// Access cart items
this.items = cart.items.get() || [];
// Access promo code
const promoCode = promo.code.get() || "";
const discountPercent = promoCode === "SAVE10" ? 0.1 : 0;
// Calculate totals
const subtotal = this.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
this.discount = subtotal * discountPercent;
this.total = subtotal - this.discount;
this.requestUpdate();
}
connectedCallback() {
super.connectedCallback();
this.calculateTotal();
}
render() {
return html`
<div class="cart">
<h2>Shopping Cart</h2>
${this.items.map(item => html`
<div>${item.name} x${item.quantity} - ${item.price}€</div>
`)}
<div class="total">
<p>Subtotal: ${this.total + this.discount}€</p>
<p>Discount: -${this.discount}€</p>
<p><strong>Total: ${this.total}€</strong></p>
</div>
</div>
`;
}
}
// When you update the publishers, calculateTotal is automatically called:
const cart = PublisherManager.get("cart");
cart.items.set([
{ name: "Product 1", price: 10, quantity: 2 },
{ name: "Product 2", price: 15, quantity: 1 }
]);
const promo = PublisherManager.get("promo");
promo.code.set("SAVE10");
// calculateTotal will be automatically called and the UI will update
Notes
- This decorator works with any component that has
connectedCallbackanddisconnectedCallbackmethods (such asLitElementor components extendingSubscriber) - The method is called automatically on
connectedCallback - Remember to call
this.requestUpdate()if you're updating component properties - The decorator uses debouncing via
queueMicrotaskto prevent excessive re-executions - For more information about publishers, see the documentation on Sharing data