defineAngularRegistry()
Creates an AngularRegistry that maps element type names to Angular component classes.
Import
import { defineAngularRegistry } from '@threadplane/render';Signature
function defineAngularRegistry(
componentMap: Record<string, AngularComponentRenderer | RenderViewEntry>,
): AngularRegistry;Parameters
| Parameter | Type | Description |
|---|---|---|
componentMap | Record<string, AngularComponentRenderer | RenderViewEntry> | An object mapping type name strings to Angular component classes, or to { component, fallback? } objects |
AngularComponentRenderer is defined as Type<unknown> -- any Angular component class. Each entry can be a bare component class or a RenderViewEntry object:
interface RenderViewEntry {
component: AngularComponentRenderer;
fallback?: AngularComponentRenderer;
schema?: StandardSchemaV1;
description?: string;
}Use the object form to configure a custom per-entry fallback, a Standard Schema validator, or a description for higher-level tooling. A bare component class is shorthand for { component } paired with the library's default fallback.
Returns
An AngularRegistry object with a single entry accessor:
interface AngularRegistry {
getEntry(name: string): NormalizedEntry | undefined;
names(): string[];
}
interface NormalizedEntry {
component: Type<unknown>;
fallback: Type<unknown>;
schema?: StandardSchemaV1;
description?: string;
}| Method | Description |
|---|---|
getEntry(name) | Returns the fully-normalized entry for a registered name, or undefined if not registered. The component, the resolved fallback (the entry's own or the library default), and any optional schema/description all hang off the returned object. |
names() | Returns an array of all registered type name strings |
Usage
Basic Registry
import { defineAngularRegistry } from '@threadplane/render';
import { TextComponent } from './text.component';
import { CardComponent } from './card.component';
const registry = defineAngularRegistry({
Text: TextComponent,
Card: CardComponent,
});
registry.getEntry('Text')?.component; // TextComponent
registry.getEntry('Card')?.component; // CardComponent
registry.getEntry('Missing'); // undefined
registry.names(); // ['Text', 'Card']With provideRender()
import { provideRender, defineAngularRegistry } from '@threadplane/render';
export const appConfig: ApplicationConfig = {
providers: [
provideRender({
registry: defineAngularRegistry({
Text: TextComponent,
Card: CardComponent,
Button: ButtonComponent,
}),
}),
],
};As a Component Input
@Component({
imports: [RenderSpecComponent],
template: `<render-spec [spec]="spec" [registry]="registry" />`,
})
export class MyComponent {
registry = defineAngularRegistry({
Text: TextComponent,
});
}Per-Component Fallbacks
Each entry can be a bare component class or a { component, fallback } object. The fallback mounts while any state-bound prop on the element is still unresolved -- useful for streaming UI, where structure arrives before data:
import { defineAngularRegistry } from '@threadplane/render';
import { TextComponent } from './text.component';
import { CardComponent } from './card.component';
import { CardSkeletonComponent } from './card-skeleton.component';
const registry = defineAngularRegistry({
Text: TextComponent,
Card: { component: CardComponent, fallback: CardSkeletonComponent },
});
registry.getEntry('Card')?.fallback; // CardSkeletonComponent (the configured fallback)
registry.getEntry('Text')?.fallback; // DefaultFallbackComponent (entry omits one)
registry.getEntry('Missing'); // undefined (not registered)An entry that omits fallback -- including every bare-component entry like Text above -- falls back to the library's DefaultFallbackComponent. Once the real component mounts, the choice is monotonic for that element instance: a later prop resolving to undefined never reverts it to the fallback.
Schema-Gated Mounting
Add schema when a component should mount only after resolved props satisfy a Standard Schema validator such as Zod:
import { z } from 'zod';
import { defineAngularRegistry } from '@threadplane/render';
const registry = defineAngularRegistry({
WeatherCard: {
component: WeatherCardComponent,
fallback: WeatherCardSkeletonComponent,
schema: z.object({
city: z.string(),
temperature: z.number(),
}),
description: 'Shows current weather for a city.',
},
});While props are missing or fail validation, the fallback renders. Once the props pass validation and the component mounts, later updates do not move that element back to the fallback.
Internal Behavior
The function normalizes each input entry into a NormalizedEntry ({ component, fallback, schema?, description? }) and stores them in an internal Map for O(1) lookups. A bare component class is paired with DefaultFallbackComponent; an object entry keeps its own fallback (or the default) and preserves any schema/description:
function normalize(
entry: AngularComponentRenderer | RenderViewEntry,
): NormalizedEntry {
// Bare Type — register with the default fallback.
if (typeof entry === 'function') {
return { component: entry, fallback: DefaultFallbackComponent };
}
// Object form — preserve component; use configured fallback or default;
// carry the optional schema/description through untouched.
return {
component: entry.component,
fallback: entry.fallback ?? DefaultFallbackComponent,
schema: entry.schema,
description: entry.description,
};
}
function defineAngularRegistry(
componentMap: Record<string, AngularComponentRenderer | RenderViewEntry>,
): AngularRegistry {
const map = new Map<string, NormalizedEntry>();
for (const [name, entry] of Object.entries(componentMap)) {
map.set(name, normalize(entry));
}
return {
getEntry: (name: string) => map.get(name),
names: () => [...map.keys()],
};
}The registry is immutable after creation. To add or remove components, create a new registry with the updated component map. The internal Map is created once and not exposed for mutation.
Type Name Conventions
Type names in the registry must match the type field in your spec elements exactly (case-sensitive):
// Registry
defineAngularRegistry({ Text: TextComponent });
// Spec -- must match exactly
{ type: 'Text', props: { label: 'Hello' } } // matches
{ type: 'text', props: { label: 'Hello' } } // does NOT matchRelated
- Component Registry Guide -- full guide on creating registries and the component input contract
- AngularComponentInputs -- the standard input interface for rendered components
- provideRender() -- setting a global registry