Component system supporting both modern Glimmer components and classic Ember components with lifecycle management and template integration.
Modern component base class with simplified lifecycle and args-based API.
/**
* Modern component base class with simplified lifecycle and args-based API
*/
class Component {
/**
* Component constructor
* @param owner - DI container owner
* @param args - Component arguments passed from template
*/
constructor(owner: Owner, args: ComponentArgs);
/**
* Called when component is being destroyed for cleanup
*/
willDestroy(): void;
/** Component arguments passed from template - readonly */
readonly args: ComponentArgs;
/** Whether component is currently being destroyed */
readonly isDestroying: boolean;
/** Whether component has been destroyed */
readonly isDestroyed: boolean;
}Usage Examples:
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
export default class UserCard extends Component {
@tracked isExpanded = false;
@action
toggleExpanded() {
this.isExpanded = !this.isExpanded;
}
get displayName() {
return this.args.user?.name || 'Unknown User';
}
willDestroy() {
super.willDestroy();
// Cleanup any resources
this.cleanup?.();
}
}Template-only components have no backing class and are optimized for performance.
import templateOnly from "@ember/component/template-only";/**
* Creates a template-only component with no backing class
* @param moduleName - Optional module name for debugging
* @returns Template-only component instance
*/
function templateOnly<S = unknown>(moduleName?: string): TemplateOnlyComponent<S>;
interface TemplateOnlyComponent<S = unknown> {
// Template-only component with no instance properties
// Only template arguments ({{@arg}}) are accessible
}Usage Examples:
import templateOnly from "@ember/component/template-only";
// Simple template-only component
export default templateOnly();
// With module name for debugging
export default templateOnly('my-app/components/user-avatar');
// Template would access only arguments:
// {{@user.name}} - ✓ Valid
// {{this.someProperty}} - ✗ Invalid (no backing class)Classic component class with full lifecycle hooks and two-way binding support.
/**
* Classic component class with full lifecycle hooks and two-way binding support
*/
class EmberComponent {
/**
* Create a new component class
* @param properties - Component properties and methods
* @returns New component class
*/
static extend(properties?: object): typeof EmberComponent;
/**
* Create component instance
* @param properties - Initial properties
* @returns Component instance
*/
static create(properties?: object): EmberComponent;
/**
* Called when component DOM element is inserted
*/
didInsertElement(): void;
/**
* Called when component DOM element is updated
*/
didUpdateAttrs(): void;
/**
* Called when component is about to be destroyed
*/
willDestroyElement(): void;
/**
* Called when component DOM element is destroyed
*/
didDestroyElement(): void;
/** Component's DOM element */
readonly element: Element;
/** Component element tag name */
tagName: string;
/** CSS classes for component element */
classNames: string[];
/** Dynamic CSS classes based on properties */
classNameBindings: string[];
/** HTML attributes for component element */
attributeBindings: string[];
}Pre-built input components for common form elements.
/**
* Built-in input component for form inputs
*/
class Input extends EmberComponent {
/** Input type (text, email, password, etc.) */
type: string;
/** Input value */
value: any;
/** Placeholder text */
placeholder: string;
/** Whether input is disabled */
disabled: boolean;
/** Action to call when input changes */
input: Function;
/** Action to call when input receives focus */
focusIn: Function;
/** Action to call when input loses focus */
focusOut: Function;
}
/**
* Built-in textarea component for multi-line text input
*/
class Textarea extends EmberComponent {
/** Textarea value */
value: string;
/** Placeholder text */
placeholder: string;
/** Number of rows */
rows: number;
/** Number of columns */
cols: number;
/** Whether textarea is disabled */
disabled: boolean;
}Functions for associating templates with component classes.
/**
* Associate a template with a component class
* @param template - Compiled template function
* @param component - Component class to associate template with
*/
function setComponentTemplate(template: Template, component: ComponentClass): void;
/**
* Get the template associated with a component class
* @param component - Component class to get template for
* @returns Associated template or undefined
*/
function getComponentTemplate(component: ComponentClass): Template | undefined;Custom component manager API for advanced component behavior.
/**
* Set a custom component manager for a component definition
* @param manager - Component manager factory function
* @param component - Component class or definition
* @returns Component class with manager
*/
function setComponentManager(
manager: (owner: Owner) => ComponentManager,
component: ComponentClass
): ComponentClass;
/**
* Define capabilities for a component manager
* @param version - Manager API version
* @param options - Capability options
* @returns Capabilities object
*/
function capabilities(version: string, options?: CapabilityOptions): Capabilities;Service for dynamically looking up component classes.
/**
* Component lookup service for dynamic component resolution
*/
class ComponentLookup extends Service {
/**
* Look up a component class by name
* @param name - Component name to look up
* @param owner - DI container owner
* @returns Component class or undefined
*/
lookupFactory(name: string, owner: Owner): ComponentClass | undefined;
}Usage Examples:
import { setComponentTemplate, setComponentManager } from "@ember/component";
import { precompileTemplate } from "@ember/template-compilation";
// Associate template with component
const template = precompileTemplate(`
<div class="user-card {{if @isHighlighted "highlighted"}}">
<h3>{{@user.name}}</h3>
<button {{on "click" this.toggleDetails}}>
{{if this.showDetails "Hide" "Show"}} Details
</button>
{{#if this.showDetails}}
<p>{{@user.email}}</p>
{{/if}}
</div>
`);
setComponentTemplate(template, UserCardComponent);
// Custom component manager
class CustomManager {
createComponent(factory, args) {
return factory.create({ args });
}
updateComponent(instance, args) {
instance.args = args;
}
destroyComponent(instance) {
instance.destroy();
}
}
setComponentManager(() => new CustomManager(), MyCustomComponent);interface ComponentArgs {
/** Dynamic component arguments */
[key: string]: any;
}
interface ComponentClass {
/** Component constructor */
new (owner: Owner, args: ComponentArgs): Component;
}
interface Template {
/** Template rendering function */
(context: object): string;
}
interface ComponentManager {
/** Create component instance */
createComponent(factory: Factory, args: ComponentArgs): any;
/** Update component with new args */
updateComponent(instance: any, args: ComponentArgs): void;
/** Destroy component instance */
destroyComponent(instance: any): void;
/** Get component context for template */
getContext(instance: any): object;
}
interface CapabilityOptions {
/** Whether component creates DOM elements */
createInstance?: boolean;
/** Whether component uses args */
useArgs?: boolean;
/** Whether component has lifecycle hooks */
hasLifecycle?: boolean;
/** Whether component is async */
asyncLifecycleCallbacks?: boolean;
/** Whether component can be destroyed */
destructor?: boolean;
}
interface Capabilities {
/** Component manager capabilities */
[key: string]: boolean;
}