bindController

The contract every controller follows, and the one function that ties a controller to a host element.

Reach for it when you write a new controller, or hold one without a createX factory.

Import

import { bindController, type Controller, type ControllerHost } from '@ecopages/radiant-ui/controllers';

Try it

A controller of three lines and a stand-in host. Connect runs the connected callbacks and Disconnect the cleanup callbacks, as a RadiantElement does when it enters and leaves the document.

Not attached

<div data-example="bind-controller">
	<button type="button">Connect</button>
	<button type="button">Disconnect</button>
	<p data-status>Not attached</p>
</div>
import { bindController, type Controller } from '@ecopages/radiant-ui/controllers';
 
export function mount(root: HTMLElement): () => void {
	const status = root.querySelector('[data-status]')!;
	const [connect, disconnect] = root.querySelectorAll('button');
 
	class Pulse implements Controller {
		attach(): void {
			status.textContent = 'Attached';
		}
		destroy(): void {
			status.textContent = 'Destroyed';
		}
	}
 
	const connected: (() => void)[] = [];
	const cleanup: (() => void)[] = [];
	bindController(
		{
			registerConnectedCallback: (callback) => connected.push(callback),
			registerCleanupCallback: (callback) => cleanup.push(callback),
		},
		new Pulse(),
	);
 
	connect.addEventListener('click', () => connected.forEach((callback) => callback()));
	disconnect.addEventListener('click', () => cleanup.forEach((callback) => callback()));
	return () => cleanup.forEach((callback) => callback());
}

mount runs once for each example element on the page, and the function it returns undoes it; see how the examples run.

Config

PartDescription
Controllerattach() starts work and destroy() ends all of it. The constructor is inert. Both are safe to call repeatedly, and a destroyed controller can be attached again.
ControllerHostThe two methods a host needs: registerConnectedCallback and registerCleanupCallback. A RadiantElement has both; a plain object with them satisfies the type.
bindController(host, controller)Registers attach() on every connect and destroy() on every cleanup, and returns the same controller.

Lifecycle

bindController runs nothing itself: it registers controller.attach() as a connected callback and controller.destroy() as a cleanup callback. Every createX(host, config) factory is bindController(host, new X(config)).

Components compose controllers, a controller can be extended through its documented protected hooks, and a component never extends a controller. See Composing a component.