or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

attributes.mdcontext-system.mdcss-styling.mddata-binding.mddependency-injection.mdhtml-templates.mdindex.mdobservable-system.mdssr-hydration.mdstate-management.mdtemplate-directives.mdtesting-utilities.mdutilities.mdweb-components.md

dependency-injection.mddocs/

0

# Dependency Injection

1

2

Full-featured dependency injection container with decorators, service registration, and hierarchical resolution for building modular and testable applications.

3

4

## Capabilities

5

6

### DI Container

7

8

Core container for managing dependency registration, resolution, and lifetime management with hierarchical scoping support.

9

10

```typescript { .api }

11

/**

12

* Central DI container interface

13

*/

14

interface Container extends ServiceLocator {

15

/**

16

* Gets an instance of a dependency

17

* @param key - The key that identifies the dependency

18

*/

19

get<K extends Key>(key: K): Resolved<K>;

20

21

/**

22

* Gets an instance of a dependency asynchronously

23

* @param key - The key that identifies the dependency

24

*/

25

getAsync<K extends Key>(key: K): Promise<Resolved<K>>;

26

27

/**

28

* Gets all instances of a dependency

29

* @param key - The key that identifies the dependency

30

* @param searchAncestors - Whether to search ancestor containers

31

*/

32

getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];

33

34

/**

35

* Registers dependencies with the container

36

* @param registrations - The registrations to add

37

*/

38

register(...registrations: Registration[]): Container;

39

40

/**

41

* Registers a resolver for a specific key

42

* @param key - The key to register the resolver for

43

* @param resolver - The resolver to register

44

*/

45

registerResolver<K extends Key>(key: K, resolver: Resolver<K>): Resolver<K>;

46

47

/**

48

* Creates a child container

49

* @param config - Configuration for the child container

50

*/

51

createChild(config?: ContainerConfiguration): Container;

52

53

/**

54

* Disposes of the container and its resources

55

*/

56

dispose(): void;

57

}

58

59

/**

60

* Configuration options for containers

61

*/

62

interface ContainerConfiguration {

63

/** Parent container locator */

64

parentLocator?: ParentLocator;

65

66

/** Default resolver to use when no registration is found */

67

defaultResolver?: (key: Key, handler: Container) => Resolver | null;

68

69

/** Whether to inherit registrations from parent */

70

inheritParentResources?: boolean;

71

}

72

73

/**

74

* Main DI utilities object

75

*/

76

const DI: {

77

/**

78

* Creates a new DI container

79

* @param config - Container configuration

80

*/

81

createContainer(config?: ContainerConfiguration): Container;

82

83

/**

84

* Gets or creates a DOM container for an element

85

* @param node - The DOM node to associate with the container

86

*/

87

getOrCreateDOMContainer(node?: Node): DOMContainer;

88

89

/**

90

* Gets the design-time type associated with a key

91

* @param key - The key to get the type for

92

*/

93

getDesignTimeType<K extends Key>(key: K): K | null;

94

95

/**

96

* Gets dependencies for a type

97

* @param Type - The type to get dependencies for

98

*/

99

getDependencies(Type: Constructable): Key[];

100

101

/**

102

* Defines dependencies for a type

103

* @param Type - The type to define dependencies for

104

* @param dependencies - The dependencies

105

*/

106

defineDependencies(Type: Constructable, dependencies: Key[]): void;

107

108

/**

109

* Gets the factory for a type

110

* @param Type - The type to get the factory for

111

*/

112

getFactory<T extends Constructable>(Type: T): Factory<T>;

113

};

114

```

115

116

**Usage Examples:**

117

118

```typescript

119

import { DI, Container, inject, singleton, transient } from "@microsoft/fast-element/di.js";

120

121

// Service interfaces and implementations

122

interface ILogger {

123

log(message: string): void;

124

error(message: string): void;

125

}

126

127

interface IApiClient {

128

get(url: string): Promise<any>;

129

post(url: string, data: any): Promise<any>;

130

}

131

132

interface IUserService {

133

getCurrentUser(): Promise<User>;

134

updateUser(user: User): Promise<void>;

135

}

136

137

// Service implementations

138

@singleton()

139

class ConsoleLogger implements ILogger {

140

log(message: string): void {

141

console.log(`[LOG] ${new Date().toISOString()}: ${message}`);

142

}

143

144

error(message: string): void {

145

console.error(`[ERROR] ${new Date().toISOString()}: ${message}`);

146

}

147

}

148

149

@singleton()

150

class HttpApiClient implements IApiClient {

151

constructor(@inject('baseUrl') private baseUrl: string) {}

152

153

async get(url: string): Promise<any> {

154

const response = await fetch(`${this.baseUrl}${url}`);

155

return response.json();

156

}

157

158

async post(url: string, data: any): Promise<any> {

159

const response = await fetch(`${this.baseUrl}${url}`, {

160

method: 'POST',

161

headers: { 'Content-Type': 'application/json' },

162

body: JSON.stringify(data)

163

});

164

return response.json();

165

}

166

}

167

168

@transient()

169

class UserService implements IUserService {

170

constructor(

171

@inject(IApiClient) private apiClient: IApiClient,

172

@inject(ILogger) private logger: ILogger

173

) {}

174

175

async getCurrentUser(): Promise<User> {

176

this.logger.log('Fetching current user');

177

try {

178

return await this.apiClient.get('/user/current');

179

} catch (error) {

180

this.logger.error(`Failed to fetch user: ${error}`);

181

throw error;

182

}

183

}

184

185

async updateUser(user: User): Promise<void> {

186

this.logger.log(`Updating user ${user.id}`);

187

try {

188

await this.apiClient.post('/user/update', user);

189

} catch (error) {

190

this.logger.error(`Failed to update user: ${error}`);

191

throw error;

192

}

193

}

194

}

195

196

// Container setup and usage

197

const container = DI.createContainer();

198

199

// Register dependencies

200

container.register(

201

// Register interfaces to implementations

202

Registration.singleton(ILogger, ConsoleLogger),

203

Registration.singleton(IApiClient, HttpApiClient),

204

Registration.transient(IUserService, UserService),

205

206

// Register configuration values

207

Registration.instance('baseUrl', 'https://api.example.com'),

208

Registration.instance('appName', 'My Application')

209

);

210

211

// Resolve and use services

212

const userService = container.get(IUserService);

213

const logger = container.get(ILogger);

214

215

// Services are now ready to use

216

logger.log('Application starting');

217

218

// Example component using DI

219

@customElement("di-example")

220

export class DIExample extends FASTElement {

221

constructor(

222

@inject(IUserService) private userService: IUserService,

223

@inject(ILogger) private logger: ILogger

224

) {

225

super();

226

}

227

228

async connectedCallback() {

229

super.connectedCallback();

230

231

try {

232

const user = await this.userService.getCurrentUser();

233

this.logger.log(`User ${user.name} connected`);

234

} catch (error) {

235

this.logger.error('Failed to load user data');

236

}

237

}

238

239

static template = html`

240

<div>Component with injected dependencies</div>

241

`;

242

}

243

244

// Create child container with additional registrations

245

const childContainer = container.createChild();

246

childContainer.register(

247

Registration.instance('environment', 'development')

248

);

249

250

// Child container inherits parent registrations

251

const childLogger = childContainer.get(ILogger); // Same instance as parent

252

const environment = childContainer.get('environment'); // 'development'

253

254

interface User {

255

id: string;

256

name: string;

257

email: string;

258

}

259

```

260

261

### Injection Decorators

262

263

Decorators for marking dependencies to be injected and configuring injection behavior.

264

265

```typescript { .api }

266

/**

267

* Decorator for dependency injection

268

* @param keys - The keys to inject

269

*/

270

function inject(...keys: Key[]): ParameterDecorator | PropertyDecorator;

271

272

/**

273

* Decorator for singleton registration

274

* @param RegisterTarget - Optional registration target

275

*/

276

function singleton<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;

277

278

/**

279

* Decorator for transient registration

280

* @param RegisterTarget - Optional registration target

281

*/

282

function transient<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;

283

284

/**

285

* Creates a resolver that resolves all instances of a key

286

* @param key - The key to resolve all instances for

287

*/

288

function all<K extends Key>(key: K): Key;

289

290

/**

291

* Creates a lazy resolver that defers resolution

292

* @param key - The key to resolve lazily

293

*/

294

function lazy<K extends Key>(key: K): Key;

295

296

/**

297

* Creates an optional resolver that returns null if not found

298

* @param key - The key to resolve optionally

299

*/

300

function optional<K extends Key>(key: K): Key;

301

302

/**

303

* Marks a parameter to be ignored during injection

304

*/

305

function ignore<K extends Key>(key: K): Key;

306

307

/**

308

* Creates a resolver for new instances per scope

309

* @param key - The key to create new instances for

310

*/

311

function newInstanceForScope<K extends Key>(key: K): Key;

312

313

/**

314

* Creates a resolver for new instances of a type

315

* @param key - The key to create new instances for

316

*/

317

function newInstanceOf<K extends Key>(key: K): Key;

318

```

319

320

**Usage Examples:**

321

322

```typescript

323

import {

324

inject,

325

singleton,

326

transient,

327

all,

328

lazy,

329

optional,

330

ignore,

331

newInstanceForScope,

332

newInstanceOf,

333

DI

334

} from "@microsoft/fast-element";

335

336

// Service interfaces

337

interface IPlugin {

338

name: string;

339

initialize(): void;

340

}

341

342

interface INotificationService {

343

send(message: string): void;

344

}

345

346

interface IConfigService {

347

get(key: string): any;

348

}

349

350

// Plugin implementations

351

@transient()

352

class EmailPlugin implements IPlugin {

353

name = "Email Plugin";

354

355

initialize(): void {

356

console.log("Email plugin initialized");

357

}

358

}

359

360

@transient()

361

class PushPlugin implements IPlugin {

362

name = "Push Plugin";

363

364

initialize(): void {

365

console.log("Push plugin initialized");

366

}

367

}

368

369

// Services with different injection patterns

370

@singleton()

371

class NotificationService implements INotificationService {

372

constructor(

373

// Inject all registered plugins

374

@inject(all(IPlugin)) private plugins: IPlugin[],

375

376

// Optional dependency - won't fail if not registered

377

@inject(optional(IConfigService)) private config?: IConfigService,

378

379

// Lazy injection - resolved when first accessed

380

@inject(lazy(ILogger)) private getLogger?: () => ILogger

381

) {}

382

383

send(message: string): void {

384

// Use logger if available (lazy resolution)

385

const logger = this.getLogger?.();

386

logger?.log(`Sending notification: ${message}`);

387

388

// Initialize all plugins

389

this.plugins.forEach(plugin => {

390

plugin.initialize();

391

});

392

393

// Use config if available

394

const timeout = this.config?.get('notificationTimeout') || 5000;

395

console.log(`Notification sent with timeout: ${timeout}ms`);

396

}

397

}

398

399

// Service with ignored parameters

400

@singleton()

401

class DatabaseService {

402

constructor(

403

@inject('connectionString') private connectionString: string,

404

@ignore private debugInfo?: string, // Not injected, can be passed manually

405

@inject(optional(ILogger)) private logger?: ILogger

406

) {

407

this.logger?.log(`Database service created with connection: ${connectionString}`);

408

}

409

}

410

411

// Service with new instance resolvers

412

@singleton()

413

class TaskProcessor {

414

constructor(

415

// Each task gets a new worker instance

416

@inject(newInstanceOf(ITaskWorker)) private createWorker: () => ITaskWorker,

417

418

// New session per processing scope

419

@inject(newInstanceForScope(IProcessingSession)) private getSession: () => IProcessingSession,

420

421

@inject(ILogger) private logger: ILogger

422

) {}

423

424

async processTask(task: Task): Promise<void> {

425

const worker = this.createWorker();

426

const session = this.getSession();

427

428

this.logger.log(`Processing task ${task.id} with worker ${worker.id}`);

429

430

try {

431

await worker.execute(task);

432

session.markCompleted(task.id);

433

} catch (error) {

434

session.markFailed(task.id, error);

435

throw error;

436

}

437

}

438

}

439

440

// Component with complex injection

441

@customElement("complex-di-example")

442

export class ComplexDIExample extends FASTElement {

443

private notificationService: INotificationService;

444

private taskProcessor: TaskProcessor;

445

private allPlugins: IPlugin[];

446

447

constructor(

448

@inject(INotificationService) notificationService: INotificationService,

449

@inject(TaskProcessor) taskProcessor: TaskProcessor,

450

@inject(all(IPlugin)) allPlugins: IPlugin[]

451

) {

452

super();

453

this.notificationService = notificationService;

454

this.taskProcessor = taskProcessor;

455

this.allPlugins = allPlugins;

456

}

457

458

connectedCallback() {

459

super.connectedCallback();

460

461

// Use injected services

462

this.notificationService.send("Component connected");

463

464

console.log(`Available plugins: ${this.allPlugins.map(p => p.name).join(', ')}`);

465

}

466

467

async processExampleTask() {

468

const task: Task = {

469

id: 'example-task',

470

data: { message: 'Hello World' },

471

priority: 'normal'

472

};

473

474

try {

475

await this.taskProcessor.processTask(task);

476

this.notificationService.send("Task processed successfully");

477

} catch (error) {

478

this.notificationService.send("Task processing failed");

479

}

480

}

481

482

static template = html<ComplexDIExample>`

483

<div class="complex-di-demo">

484

<h2>Complex DI Example</h2>

485

<p>This component uses various injection patterns</p>

486

<button @click="${x => x.processExampleTask()}">

487

Process Task

488

</button>

489

</div>

490

`;

491

}

492

493

// Container setup with all registrations

494

const container = DI.createContainer();

495

496

container.register(

497

// Register all plugins

498

Registration.transient(IPlugin, EmailPlugin),

499

Registration.transient(IPlugin, PushPlugin),

500

501

// Register services

502

Registration.singleton(INotificationService, NotificationService),

503

Registration.singleton(TaskProcessor, TaskProcessor),

504

Registration.singleton(DatabaseService, DatabaseService),

505

506

// Register configuration

507

Registration.instance('connectionString', 'mongodb://localhost:27017'),

508

Registration.instance(IConfigService, {

509

get: (key: string) => {

510

const config = { notificationTimeout: 3000, maxRetries: 3 };

511

return config[key as keyof typeof config];

512

}

513

})

514

);

515

516

interface ITaskWorker {

517

id: string;

518

execute(task: Task): Promise<void>;

519

}

520

521

interface IProcessingSession {

522

markCompleted(taskId: string): void;

523

markFailed(taskId: string, error: any): void;

524

}

525

526

interface Task {

527

id: string;

528

data: any;

529

priority: 'low' | 'normal' | 'high';

530

}

531

```

532

533

### Service Registration

534

535

Registration utilities for configuring how services are resolved and their lifetimes.

536

537

```typescript { .api }

538

/**

539

* Registration utilities for DI container

540

*/

541

const Registration: {

542

/**

543

* Creates a transient registration (new instance each time)

544

* @param key - The key to register under

545

* @param value - The value or type to register

546

*/

547

transient<T>(key: Key, value: Constructable<T> | T): Registration<T>;

548

549

/**

550

* Creates a singleton registration (same instance each time)

551

* @param key - The key to register under

552

* @param value - The value or type to register

553

*/

554

singleton<T>(key: Key, value: Constructable<T> | T): Registration<T>;

555

556

/**

557

* Creates an instance registration (specific instance)

558

* @param key - The key to register under

559

* @param value - The instance to register

560

*/

561

instance<T>(key: Key, value: T): Registration<T>;

562

563

/**

564

* Creates a callback registration (custom resolution logic)

565

* @param key - The key to register under

566

* @param callback - The callback to resolve the dependency

567

*/

568

callback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;

569

570

/**

571

* Creates a cached callback registration

572

* @param key - The key to register under

573

* @param callback - The callback to resolve the dependency

574

*/

575

cachedCallback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;

576

577

/**

578

* Creates an aliased registration (redirects to another key)

579

* @param originalKey - The original key

580

* @param aliasKey - The alias key

581

*/

582

alias<T>(originalKey: Key, aliasKey: Key): Registration<T>;

583

584

/**

585

* Creates a defer registration (resolves when first requested)

586

* @param key - The key to register under

587

* @param callback - The callback to create the registration

588

*/

589

defer<T>(key: Key, callback: () => Registration<T>): Registration<T>;

590

};

591

592

/**

593

* Resolver interface for custom resolution logic

594

*/

595

interface Resolver<K = any> {

596

/** Marks this object as a resolver */

597

readonly $isResolver: true;

598

599

/**

600

* Resolves the dependency

601

* @param handler - The container handling the resolution

602

* @param requestor - The container that requested the resolution

603

*/

604

resolve(handler: Container, requestor: Container): Resolved<K>;

605

606

/**

607

* Resolves the dependency asynchronously

608

* @param handler - The container handling the resolution

609

* @param requestor - The container that requested the resolution

610

*/

611

resolveAsync(handler: Container, requestor: Container): Promise<Resolved<K>>;

612

613

/**

614

* Gets the factory for this resolver

615

* @param container - The container to get the factory from

616

*/

617

getFactory?(container: Container): Factory<K> | null;

618

}

619

```

620

621

**Usage Examples:**

622

623

```typescript

624

import { Registration, DI, Container, ResolveCallback } from "@microsoft/fast-element/di.js";

625

626

// Service interfaces

627

interface IDataService {

628

getData(): Promise<any>;

629

}

630

631

interface ICacheService {

632

get(key: string): any;

633

set(key: string, value: any): void;

634

}

635

636

interface IMetricsService {

637

recordEvent(event: string): void;

638

}

639

640

// Service implementations

641

class ApiDataService implements IDataService {

642

constructor(private apiUrl: string) {}

643

644

async getData(): Promise<any> {

645

const response = await fetch(`${this.apiUrl}/data`);

646

return response.json();

647

}

648

}

649

650

class MemoryCacheService implements ICacheService {

651

private cache = new Map<string, any>();

652

653

get(key: string): any {

654

return this.cache.get(key);

655

}

656

657

set(key: string, value: any): void {

658

this.cache.set(key, value);

659

}

660

}

661

662

class MetricsService implements IMetricsService {

663

recordEvent(event: string): void {

664

console.log(`Metrics: ${event} at ${new Date().toISOString()}`);

665

}

666

}

667

668

// Container setup with different registration types

669

const container = DI.createContainer();

670

671

// 1. Instance registration - specific instance

672

const cacheInstance = new MemoryCacheService();

673

container.register(

674

Registration.instance(ICacheService, cacheInstance)

675

);

676

677

// 2. Singleton registration - same instance each time

678

container.register(

679

Registration.singleton(IMetricsService, MetricsService)

680

);

681

682

// 3. Transient registration - new instance each time

683

container.register(

684

Registration.transient(IDataService, ApiDataService)

685

);

686

687

// 4. Callback registration - custom resolution logic

688

const dataServiceCallback: ResolveCallback<IDataService> = (handler, requestor, resolver) => {

689

const environment = handler.get('environment');

690

const apiUrl = environment === 'production'

691

? 'https://api.prod.example.com'

692

: 'https://api.dev.example.com';

693

694

return new ApiDataService(apiUrl);

695

};

696

697

container.register(

698

Registration.callback(IDataService, dataServiceCallback)

699

);

700

701

// 5. Cached callback registration - callback result cached

702

const expensiveServiceCallback: ResolveCallback<IExpensiveService> = (handler, requestor, resolver) => {

703

console.log('Creating expensive service (this should only happen once)');

704

return new ExpensiveService();

705

};

706

707

container.register(

708

Registration.cachedCallback(IExpensiveService, expensiveServiceCallback)

709

);

710

711

// 6. Alias registration - redirect one key to another

712

container.register(

713

Registration.alias('cache', ICacheService),

714

Registration.alias('metrics', IMetricsService)

715

);

716

717

// 7. Defer registration - create registration when needed

718

container.register(

719

Registration.defer('conditionalService', () => {

720

const shouldUseAdvanced = container.get('useAdvancedFeatures');

721

722

return shouldUseAdvanced

723

? Registration.singleton('conditionalService', AdvancedService)

724

: Registration.singleton('conditionalService', BasicService);

725

})

726

);

727

728

// Complex registration scenarios

729

class ServiceFactory {

730

static createDatabaseRegistrations(connectionString: string): Registration[] {

731

return [

732

Registration.instance('connectionString', connectionString),

733

734

Registration.callback('dbConnection', (handler, requestor, resolver) => {

735

const connStr = handler.get('connectionString');

736

return new DatabaseConnection(connStr);

737

}),

738

739

Registration.singleton('userRepository', UserRepository),

740

Registration.singleton('productRepository', ProductRepository)

741

];

742

}

743

744

static createHttpClientRegistrations(baseUrl: string, timeout: number): Registration[] {

745

return [

746

Registration.instance('httpConfig', { baseUrl, timeout }),

747

748

Registration.cachedCallback('httpClient', (handler, requestor, resolver) => {

749

const config = handler.get('httpConfig');

750

const client = new HttpClient(config.baseUrl);

751

client.timeout = config.timeout;

752

return client;

753

}),

754

755

Registration.transient('apiService', ApiService)

756

];

757

}

758

}

759

760

// Register groups of related services

761

container.register(

762

...ServiceFactory.createDatabaseRegistrations('mongodb://localhost:27017'),

763

...ServiceFactory.createHttpClientRegistrations('https://api.example.com', 5000)

764

);

765

766

// Environment-specific registrations

767

const environment = process.env.NODE_ENV || 'development';

768

769

if (environment === 'development') {

770

container.register(

771

Registration.singleton(ILogger, VerboseLogger),

772

Registration.instance('debugMode', true)

773

);

774

} else {

775

container.register(

776

Registration.singleton(ILogger, ProductionLogger),

777

Registration.instance('debugMode', false)

778

);

779

}

780

781

// Custom resolver example

782

class DatabasePoolResolver implements Resolver<IDatabasePool> {

783

readonly $isResolver = true;

784

private pool?: IDatabasePool;

785

786

resolve(handler: Container, requestor: Container): IDatabasePool {

787

if (!this.pool) {

788

const connectionString = handler.get('connectionString');

789

const maxConnections = handler.get('maxConnections') || 10;

790

791

this.pool = new DatabasePool(connectionString, maxConnections);

792

}

793

794

return this.pool;

795

}

796

797

async resolveAsync(handler: Container, requestor: Container): Promise<IDatabasePool> {

798

return this.resolve(handler, requestor);

799

}

800

}

801

802

// Register custom resolver

803

container.registerResolver(IDatabasePool, new DatabasePoolResolver());

804

805

// Usage examples

806

const cache = container.get(ICacheService); // Gets the specific instance

807

const metrics1 = container.get(IMetricsService); // Gets singleton instance

808

const metrics2 = container.get(IMetricsService); // Same instance as metrics1

809

810

const data1 = container.get(IDataService); // Gets new transient instance

811

const data2 = container.get(IDataService); // Gets different transient instance

812

813

const aliasedCache = container.get('cache'); // Same as ICacheService instance

814

const aliasedMetrics = container.get('metrics'); // Same as IMetricsService instance

815

816

// Mock interfaces for examples

817

interface IExpensiveService {

818

performCalculation(): number;

819

}

820

821

interface IDatabasePool {

822

getConnection(): Promise<any>;

823

}

824

825

interface ILogger {

826

log(message: string): void;

827

}

828

829

class ExpensiveService implements IExpensiveService {

830

performCalculation(): number {

831

return Math.random() * 1000;

832

}

833

}

834

835

class AdvancedService {}

836

class BasicService {}

837

class VerboseLogger implements ILogger {

838

log(message: string): void {

839

console.log(`[VERBOSE] ${message}`);

840

}

841

}

842

843

class ProductionLogger implements ILogger {

844

log(message: string): void {

845

console.log(message);

846

}

847

}

848

849

class DatabaseConnection {}

850

class UserRepository {}

851

class ProductRepository {}

852

class HttpClient {

853

timeout: number = 5000;

854

constructor(private baseUrl: string) {}

855

}

856

class ApiService {}

857

class DatabasePool implements IDatabasePool {

858

constructor(private connectionString: string, private maxConnections: number) {}

859

860

async getConnection(): Promise<any> {

861

return {}; // Mock connection

862

}

863

}

864

```

865

866

### DOM Container Integration

867

868

Integration with DOM elements for providing dependency injection within component hierarchies.

869

870

```typescript { .api }

871

/**

872

* A container associated with DOM nodes

873

*/

874

interface DOMContainer extends Container {

875

/** The DOM node this container is associated with */

876

readonly owner: Node;

877

878

/**

879

* Gets the closest DOMContainer by walking up the DOM tree

880

* @param node - The starting node

881

*/

882

getClosest(node: Node): DOMContainer | null;

883

}

884

885

/**

886

* Service locator interface for dependency resolution

887

*/

888

interface ServiceLocator {

889

/**

890

* Gets an instance of a dependency

891

* @param key - The key identifying the dependency

892

*/

893

get<K extends Key>(key: K): Resolved<K>;

894

895

/**

896

* Gets an instance of a dependency asynchronously

897

* @param key - The key identifying the dependency

898

*/

899

getAsync<K extends Key>(key: K): Promise<Resolved<K>>;

900

901

/**

902

* Gets all instances of a dependency

903

* @param key - The key identifying the dependency

904

* @param searchAncestors - Whether to search ancestor containers

905

*/

906

getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];

907

908

/**

909

* Checks if a dependency is registered

910

* @param key - The key to check

911

* @param searchAncestors - Whether to search ancestor containers

912

*/

913

has<K extends Key>(key: K, searchAncestors?: boolean): boolean;

914

}

915

916

/**

917

* Context keys for DI integration

918

*/

919

const Container: Context<Container>;

920

const DOMContainer: Context<DOMContainer>;

921

const ServiceLocator: Context<ServiceLocator>;

922

```

923

924

**Usage Examples:**

925

926

```typescript

927

import {

928

DI,

929

DOMContainer as DOMContainerContext,

930

Container as ContainerContext,

931

FASTElement,

932

customElement,

933

html

934

} from "@microsoft/fast-element";

935

936

// Root application container setup

937

class Application {

938

private rootContainer: Container;

939

940

constructor() {

941

this.rootContainer = DI.createContainer();

942

this.setupRootServices();

943

}

944

945

private setupRootServices() {

946

this.rootContainer.register(

947

Registration.singleton(IAppConfig, AppConfig),

948

Registration.singleton(IThemeService, ThemeService),

949

Registration.singleton(IAuthService, AuthService),

950

Registration.instance('apiBaseUrl', 'https://api.example.com')

951

);

952

}

953

954

bootstrap(rootElement: HTMLElement) {

955

// Associate root container with DOM

956

const domContainer = DI.getOrCreateDOMContainer(rootElement);

957

958

// Copy root registrations to DOM container

959

domContainer.register(

960

...this.rootContainer.getAll(Registration) // Copy all registrations

961

);

962

963

// Render root component

964

const appComponent = document.createElement('app-root');

965

rootElement.appendChild(appComponent);

966

}

967

}

968

969

// Application-level component

970

@customElement("app-root")

971

export class AppRoot extends FASTElement {

972

private themeService?: IThemeService;

973

private authService?: IAuthService;

974

975

connectedCallback() {

976

super.connectedCallback();

977

978

// Get services from DOM container hierarchy

979

const domContainer = DI.getOrCreateDOMContainer(this);

980

this.themeService = domContainer.get(IThemeService);

981

this.authService = domContainer.get(IAuthService);

982

983

this.initializeApp();

984

}

985

986

private async initializeApp() {

987

// Initialize theme

988

await this.themeService?.initialize();

989

990

// Check authentication

991

const isAuthenticated = await this.authService?.checkAuth();

992

993

if (isAuthenticated) {

994

this.renderAuthenticatedApp();

995

} else {

996

this.renderLoginApp();

997

}

998

}

999

1000

private renderAuthenticatedApp() {

1001

// Create child container for authenticated features

1002

const domContainer = DI.getOrCreateDOMContainer(this);

1003

const childContainer = domContainer.createChild();

1004

1005

// Register authenticated services

1006

childContainer.register(

1007

Registration.singleton(IUserService, UserService),

1008

Registration.singleton(INotificationService, NotificationService)

1009

);

1010

1011

// Associate child container with section

1012

const mainSection = this.shadowRoot?.querySelector('#main-content') as HTMLElement;

1013

if (mainSection) {

1014

const sectionContainer = DI.getOrCreateDOMContainer(mainSection);

1015

// Copy child registrations

1016

sectionContainer.register(

1017

Registration.alias(IUserService, IUserService),

1018

Registration.alias(INotificationService, INotificationService)

1019

);

1020

}

1021

}

1022

1023

private renderLoginApp() {

1024

// Different services for login flow

1025

const domContainer = DI.getOrCreateDOMContainer(this);

1026

const loginContainer = domContainer.createChild();

1027

1028

loginContainer.register(

1029

Registration.transient(ILoginService, LoginService),

1030

Registration.singleton(IValidationService, ValidationService)

1031

);

1032

}

1033

1034

static template = html<AppRoot>`

1035

<div class="app-root">

1036

<header id="app-header">

1037

<app-header></app-header>

1038

</header>

1039

1040

<main id="main-content">

1041

<router-outlet></router-outlet>

1042

</main>

1043

1044

<footer id="app-footer">

1045

<app-footer></app-footer>

1046

</footer>

1047

</div>

1048

`;

1049

}

1050

1051

// Feature component that uses parent container services

1052

@customElement("user-profile")

1053

export class UserProfile extends FASTElement {

1054

private userService?: IUserService;

1055

private notificationService?: INotificationService;

1056

1057

connectedCallback() {

1058

super.connectedCallback();

1059

1060

// Automatically finds services up the DOM tree

1061

const domContainer = DI.getOrCreateDOMContainer(this);

1062

this.userService = domContainer.get(IUserService);

1063

this.notificationService = domContainer.get(INotificationService);

1064

}

1065

1066

private async saveProfile(profileData: any) {

1067

try {

1068

await this.userService?.updateProfile(profileData);

1069

this.notificationService?.showSuccess('Profile updated successfully');

1070

} catch (error) {

1071

this.notificationService?.showError('Failed to update profile');

1072

}

1073

}

1074

1075

static template = html<UserProfile>`

1076

<div class="user-profile">

1077

<h2>User Profile</h2>

1078

<!-- Profile form content -->

1079

</div>

1080

`;

1081

}

1082

1083

// Context-aware service that can access its container

1084

class ContextAwareService {

1085

constructor(@inject(ContainerContext) private container: Container) {}

1086

1087

getRelatedService<T>(key: Key): T {

1088

// Can resolve other services from the same container

1089

return this.container.get(key);

1090

}

1091

1092

createChildScope(): Container {

1093

// Can create child scopes for temporary operations

1094

return this.container.createChild();

1095

}

1096

}

1097

1098

// Service that needs DOM context

1099

class DOMContextService {

1100

constructor(@inject(DOMContainerContext) private domContainer: DOMContainer) {}

1101

1102

findNearestService<T>(key: Key): T | null {

1103

// Walk up DOM tree looking for service

1104

let current = this.domContainer.owner.parentElement;

1105

1106

while (current) {

1107

const container = DI.getOrCreateDOMContainer(current);

1108

if (container.has(key)) {

1109

return container.get(key);

1110

}

1111

current = current.parentElement;

1112

}

1113

1114

return null;

1115

}

1116

1117

broadcastToChildren(message: any) {

1118

// Find all child DOM containers and send message

1119

const walker = document.createTreeWalker(

1120

this.domContainer.owner,

1121

NodeFilter.SHOW_ELEMENT

1122

);

1123

1124

let node = walker.nextNode();

1125

while (node) {

1126

const childContainer = DI.getOrCreateDOMContainer(node as Element);

1127

if (childContainer.has(IMessageHandler)) {

1128

const handler = childContainer.get(IMessageHandler);

1129

handler.handleMessage(message);

1130

}

1131

node = walker.nextNode();

1132

}

1133

}

1134

}

1135

1136

// Module-based registration

1137

class FeatureModule {

1138

static register(container: Container) {

1139

container.register(

1140

Registration.singleton(IFeatureService, FeatureService),

1141

Registration.transient(IFeatureRepository, FeatureRepository),

1142

Registration.instance('featureConfig', {

1143

enabled: true,

1144

maxItems: 100

1145

})

1146

);

1147

}

1148

}

1149

1150

// Usage in application

1151

const app = new Application();

1152

app.bootstrap(document.getElementById('app')!);

1153

1154

// Mock interfaces for examples

1155

interface IAppConfig {

1156

apiUrl: string;

1157

version: string;

1158

}

1159

1160

interface IThemeService {

1161

initialize(): Promise<void>;

1162

setTheme(theme: string): void;

1163

}

1164

1165

interface IAuthService {

1166

checkAuth(): Promise<boolean>;

1167

login(credentials: any): Promise<void>;

1168

}

1169

1170

interface IUserService {

1171

updateProfile(data: any): Promise<void>;

1172

}

1173

1174

interface INotificationService {

1175

showSuccess(message: string): void;

1176

showError(message: string): void;

1177

}

1178

1179

interface ILoginService {

1180

validateCredentials(credentials: any): Promise<boolean>;

1181

}

1182

1183

interface IValidationService {

1184

validateForm(data: any): ValidationResult;

1185

}

1186

1187

interface IMessageHandler {

1188

handleMessage(message: any): void;

1189

}

1190

1191

interface IFeatureService {}

1192

interface IFeatureRepository {}

1193

1194

interface ValidationResult {

1195

isValid: boolean;

1196

errors: string[];

1197

}

1198

1199

class AppConfig implements IAppConfig {

1200

apiUrl = 'https://api.example.com';

1201

version = '1.0.0';

1202

}

1203

1204

class ThemeService implements IThemeService {

1205

async initialize(): Promise<void> {}

1206

setTheme(theme: string): void {}

1207

}

1208

1209

class AuthService implements IAuthService {

1210

async checkAuth(): Promise<boolean> { return false; }

1211

async login(credentials: any): Promise<void> {}

1212

}

1213

1214

class UserService implements IUserService {

1215

async updateProfile(data: any): Promise<void> {}

1216

}

1217

1218

class NotificationService implements INotificationService {

1219

showSuccess(message: string): void {}

1220

showError(message: string): void {}

1221

}

1222

1223

class LoginService implements ILoginService {

1224

async validateCredentials(credentials: any): Promise<boolean> { return true; }

1225

}

1226

1227

class ValidationService implements IValidationService {

1228

validateForm(data: any): ValidationResult {

1229

return { isValid: true, errors: [] };

1230

}

1231

}

1232

1233

class FeatureService implements IFeatureService {}

1234

class FeatureRepository implements IFeatureRepository {}

1235

```

1236

1237

## Types

1238

1239

```typescript { .api }

1240

/**

1241

* Dependency injection key type

1242

*/

1243

type Key = PropertyKey | Constructable | object;

1244

1245

/**

1246

* Resolved type helper

1247

*/

1248

type Resolved<K> = K extends Constructable<infer T> ? T : K;

1249

1250

/**

1251

* Injectable class type

1252

*/

1253

type Injectable<T = {}> = Constructable<T> & { register?(container: Container): void };

1254

1255

/**

1256

* Self-registering type

1257

*/

1258

type RegisterSelf<T extends Constructable> = T & Registration<InstanceType<T>>;

1259

1260

/**

1261

* Parent container locator function

1262

*/

1263

type ParentLocator = (container: Container) => Container | null;

1264

1265

/**

1266

* Async registration locator function

1267

*/

1268

type AsyncRegistrationLocator = (container: Container) => Promise<Registration[]>;

1269

```