or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-outlet.mdcore-services.mddynamic-attributes.mddynamic-component.mddynamic-directives.mdindex.mdinput-output.md

input-output.mddocs/

0

# Input/Output Management

1

2

Type-safe system for binding inputs and outputs to dynamic components at runtime, supporting both function handlers and complex output expressions with additional arguments and custom contexts.

3

4

## Capabilities

5

6

### Dynamic I/O Directive

7

8

Main directive for managing dynamic inputs and outputs on ndc-dynamic components.

9

10

```typescript { .api }

11

/**

12

* Manages dynamic inputs and outputs for ndc-dynamic components

13

* Automatically updates component properties and event handlers when inputs change

14

*/

15

@Directive({

16

selector: '[ndcDynamicInputs],[ndcDynamicOutputs]',

17

standalone: true,

18

exportAs: 'ndcDynamicIo'

19

})

20

export class DynamicIoDirective implements DoCheck {

21

/** Dynamic input bindings for the component */

22

@Input() ndcDynamicInputs?: InputsType | null;

23

24

/** Dynamic output bindings for the component */

25

@Input() ndcDynamicOutputs?: OutputsType | null;

26

}

27

```

28

29

### I/O Service

30

31

Core service for managing dynamic inputs and outputs with lifecycle management.

32

33

```typescript { .api }

34

/**

35

* Core service for managing dynamic inputs and outputs

36

* Handles property updates and event subscription management

37

*/

38

@Injectable()

39

export class IoService implements OnDestroy {

40

/**

41

* Updates component inputs and outputs

42

* @param inputs - Object mapping property names to values

43

* @param outputs - Object mapping event names to handlers

44

*/

45

update(inputs?: InputsType | null, outputs?: OutputsType | null): void;

46

}

47

48

/**

49

* Configuration options for IoService

50

*/

51

@Injectable({ providedIn: 'root' })

52

export class IoServiceOptions {

53

/** Whether to track output changes for optimization */

54

trackOutputChanges: boolean = false;

55

}

56

```

57

58

### I/O Factory Service

59

60

Factory service for creating IoService instances with custom configuration.

61

62

```typescript { .api }

63

/**

64

* Factory for creating IoService instances

65

* Enables custom configuration per component instance

66

*/

67

@Injectable({ providedIn: 'root' })

68

export class IoFactoryService {

69

/**

70

* Creates a new IoService instance

71

* @param componentInjector - Component injector providing ComponentRef access

72

* @param ioOptions - Optional configuration for the service

73

* @returns Configured IoService instance

74

*/

75

create(

76

componentInjector: DynamicComponentInjector,

77

ioOptions?: IoServiceOptions & IoFactoryServiceOptions

78

): IoService;

79

}

80

81

/**

82

* Additional options for IoFactoryService.create method

83

*/

84

interface IoFactoryServiceOptions {

85

/** Optional injector override */

86

injector?: Injector;

87

}

88

```

89

90

**Usage Examples:**

91

92

```typescript

93

import { Component } from '@angular/core';

94

import { DynamicComponent, DynamicIoDirective, InputsType, OutputsType } from 'ng-dynamic-component';

95

96

// Basic input/output binding

97

@Component({

98

standalone: true,

99

imports: [DynamicComponent, DynamicIoDirective],

100

template: `

101

<ndc-dynamic

102

[ndcDynamicComponent]="component"

103

[ndcDynamicInputs]="inputs"

104

[ndcDynamicOutputs]="outputs">

105

</ndc-dynamic>

106

`

107

})

108

export class BasicIoExampleComponent {

109

component = MyFormComponent;

110

111

inputs: InputsType = {

112

title: 'User Registration',

113

required: true,

114

placeholder: 'Enter your name',

115

maxLength: 50

116

};

117

118

outputs: OutputsType = {

119

onSubmit: (formData: any) => this.handleSubmit(formData),

120

onCancel: () => this.handleCancel(),

121

onChange: (value: string) => console.log('Value changed:', value)

122

};

123

124

handleSubmit(formData: any) {

125

console.log('Form submitted:', formData);

126

// Process form submission

127

}

128

129

handleCancel() {

130

console.log('Form cancelled');

131

// Handle cancellation

132

}

133

}

134

135

// Dynamic input/output changes

136

@Component({

137

template: `

138

<button (click)="updateInputs()">Update Inputs</button>

139

<button (click)="toggleOutputs()">Toggle Outputs</button>

140

141

<ndc-dynamic

142

[ndcDynamicComponent]="component"

143

[ndcDynamicInputs]="currentInputs"

144

[ndcDynamicOutputs]="currentOutputs">

145

</ndc-dynamic>

146

`

147

})

148

export class DynamicIoExampleComponent {

149

component = DataDisplayComponent;

150

151

private inputVariations: InputsType[] = [

152

{ title: 'Variation 1', theme: 'light', showHeader: true },

153

{ title: 'Variation 2', theme: 'dark', showHeader: false },

154

{ title: 'Variation 3', theme: 'auto', showHeader: true }

155

];

156

157

private currentVariation = 0;

158

private outputsEnabled = true;

159

160

get currentInputs(): InputsType {

161

return this.inputVariations[this.currentVariation];

162

}

163

164

get currentOutputs(): OutputsType | null {

165

return this.outputsEnabled ? {

166

onItemClick: (item: any) => console.log('Item clicked:', item),

167

onSelectionChange: (selection: any[]) => console.log('Selection:', selection)

168

} : null;

169

}

170

171

updateInputs() {

172

this.currentVariation = (this.currentVariation + 1) % this.inputVariations.length;

173

}

174

175

toggleOutputs() {

176

this.outputsEnabled = !this.outputsEnabled;

177

}

178

}

179

```

180

181

## Types

182

183

### Core Types

184

185

```typescript { .api }

186

/**

187

* Type for dynamic input bindings

188

* Maps property names to their values

189

*/

190

interface InputsType {

191

[k: string]: unknown;

192

}

193

194

/**

195

* Type for dynamic output bindings

196

* Maps event names to handler expressions

197

*/

198

interface OutputsType {

199

[k: string]: OutputExpression | undefined;

200

}

201

202

/**

203

* Union type for output expressions

204

* Can be a simple handler function or complex expression with arguments

205

*/

206

type OutputExpression = EventHandler | OutputWithArgs;

207

208

/**

209

* Type for event handler functions

210

* @template T - The event data type

211

*/

212

type EventHandler<T = unknown> = (event: T) => unknown;

213

214

/**

215

* Generic function type for handlers with arguments

216

*/

217

type AnyFunction = (...args: unknown[]) => unknown;

218

```

219

220

### Advanced Output Types

221

222

```typescript { .api }

223

/**

224

* Output expression with additional arguments

225

* Enables passing extra data to event handlers

226

*/

227

interface OutputWithArgs {

228

/** The event handler function */

229

handler: AnyFunction;

230

/** Optional arguments to pass to the handler */

231

args?: unknown[];

232

}

233

```

234

235

**Usage Examples:**

236

237

```typescript

238

// Using OutputWithArgs for complex event handling

239

const complexOutputs: OutputsType = {

240

// Simple handler

241

onSimpleEvent: (data: any) => console.log(data),

242

243

// Handler with additional arguments

244

onComplexEvent: {

245

handler: (event: any, userId: number, context: string) => {

246

console.log('Event:', event, 'User:', userId, 'Context:', context);

247

},

248

args: [123, 'dashboard']

249

}

250

};

251

```

252

253

## Event Context and Configuration

254

255

### Event Argument Configuration

256

257

```typescript { .api }

258

/**

259

* Default factory for event argument token

260

* @returns The default event argument name

261

*/

262

function defaultEventArgumentFactory(): string;

263

264

/**

265

* Token for configuring event argument name in output expressions

266

* Default value is '$event'

267

*/

268

const IoEventArgumentToken: InjectionToken<string>;

269

270

/**

271

* @deprecated Use IoEventArgumentToken instead

272

* Deprecated since v10.4.0

273

*/

274

const EventArgumentToken: InjectionToken<string>;

275

```

276

277

### Event Context Providers

278

279

```typescript { .api }

280

/**

281

* Token for providing custom context for output handlers

282

* Allows injecting additional data available to all event handlers

283

*/

284

const IoEventContextToken: InjectionToken<unknown>;

285

286

/**

287

* Token for providing StaticProvider that configures IoEventContextToken

288

* Enables dynamic context configuration per component

289

*/

290

const IoEventContextProviderToken: InjectionToken<StaticProvider>;

291

```

292

293

**Usage Examples:**

294

295

```typescript

296

// Custom event argument configuration

297

@Component({

298

providers: [

299

{ provide: IoEventArgumentToken, useValue: '$data' }

300

],

301

template: `

302

<ndc-dynamic

303

[ndcDynamicComponent]="component"

304

[ndcDynamicOutputs]="outputs">

305

</ndc-dynamic>

306

`

307

})

308

export class CustomEventArgComponent {

309

outputs = {

310

// Now '$data' is used instead of '$event'

311

onClick: '$data.preventDefault(); handleClick($data)'

312

};

313

}

314

315

// Custom event context

316

@Component({

317

providers: [

318

{

319

provide: IoEventContextToken,

320

useValue: { userId: 123, permissions: ['read', 'write'] }

321

}

322

],

323

template: `<!-- Dynamic component with custom context -->`

324

})

325

export class ContextExampleComponent { }

326

```

327

328

## Component I/O Abstract Service

329

330

### ComponentIO Service

331

332

```typescript { .api }

333

/**

334

* Abstract service for setting inputs and getting outputs on dynamic components

335

* Extensible interface for custom I/O management implementations

336

*/

337

@Injectable({

338

providedIn: 'root',

339

useClass: ClassicComponentIO

340

})

341

export abstract class ComponentIO {

342

/**

343

* Sets an input property on a component

344

* @param componentRef - Reference to the component

345

* @param name - Name of the input property

346

* @param value - Value to set

347

*/

348

abstract setInput<T, K extends ComponentInputKey<T>>(

349

componentRef: ComponentRef<T>,

350

name: K,

351

value: T[K]

352

): void;

353

354

/**

355

* Gets an output observable from a component

356

* @param componentRef - Reference to the component

357

* @param name - Name of the output property

358

* @returns Observable of the output events

359

*/

360

abstract getOutput<T, K extends ComponentInputKey<T>>(

361

componentRef: ComponentRef<T>,

362

name: K

363

): Observable<unknown>;

364

}

365

366

/**

367

* Type constraint for component input property names

368

* Ensures type safety when setting component inputs

369

*/

370

type ComponentInputKey<T> = keyof T & string;

371

```