Component metadata
@Component is where a view declares its slice of the world: scoped providers, module imports for standalone-style composition, and the controllers its subtree shares.
The three fields
@Component({
providers: [DraftStore], // this view's own scope only
controllers: [EditorController], // shared with the whole subtree
imports: [SharedModule], // pull a module's providers in scope
})
export class EditorView extends View { /* … */ }| Field | Effect |
|---|---|
providers | Component-level providers, Angular-style: one instance per mounted view, visible to the view and its descendants. Overrides a module provider of the same token for this subtree. |
controllers | Registered in the view's injector — parent and nested child views injecting the same controller share one instance. See Controllers. |
imports | Standalone-component imports: the module's providers and declared components join this view's scope, no module needed. See below. |
Standalone views: imports without a module
A view that isn't declared by any module can still pull a module's providers into its own injector — Angular's standalone: true + imports pattern:
@Module({ providers: [ThemeService] })
export class SharedModule {}
// hello.view.tsx — declared in NO module, still gets ThemeService:
@Component({ imports: [SharedModule] })
export class HelloView extends View {
private theme = inject(ThemeService); // resolved from the imported module
render() { /* … */ }
}The imported module's declared components are also mountable inside this view's subtree — the ownership check treats the view's injector as part of that module's closure.
Choosing a scope
| Need | Declare on |
|---|---|
| App-wide singleton | @Injectable({ providedIn: 'root' }) or root module providers |
| Feature-wide state | @Module({ providers }) |
| Per-screen state reset on navigation | @Component({ providers }) |
| Shared form/interaction controller | @Component({ controllers }) |
| Module-less view needing module services | @Component({ imports }) |