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

dynamic-attributes.mddocs/

0

# Dynamic Attributes

1

2

System for dynamically setting and managing HTML attributes on component elements at runtime, supporting both declarative attribute binding and imperative attribute manipulation.

3

4

## Capabilities

5

6

### Dynamic Attributes Directive

7

8

Main directive for dynamically setting HTML attributes on component host elements.

9

10

```typescript { .api }

11

/**

12

* Dynamically sets HTML attributes on component elements

13

* Automatically updates attributes when the attribute map changes

14

*/

15

@Directive({

16

selector: '[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]',

17

standalone: true,

18

exportAs: 'ndcDynamicAttributes'

19

})

20

export class DynamicAttributesDirective implements DoCheck {

21

/** Attributes for ndc-dynamic components */

22

@Input() ndcDynamicAttributes?: AttributesMap | null;

23

24

/** Attributes for NgComponentOutlet components */

25

@Input() ngComponentOutletNdcDynamicAttributes?: AttributesMap | null;

26

27

/**

28

* Manually set an attribute on the host element

29

* @param name - Attribute name

30

* @param value - Attribute value

31

* @param namespace - Optional namespace for the attribute

32

*/

33

setAttribute(name: string, value: string, namespace?: string): void;

34

35

/**

36

* Manually remove an attribute from the host element

37

* @param name - Attribute name to remove

38

* @param namespace - Optional namespace for the attribute

39

*/

40

removeAttribute(name: string, namespace?: string): void;

41

}

42

```

43

44

### Dynamic Attributes Module

45

46

NgModule that provides dynamic attributes functionality for traditional module-based applications.

47

48

```typescript { .api }

49

/**

50

* Module for dynamic attributes functionality

51

* Includes component outlet integration

52

*/

53

@NgModule({

54

imports: [DynamicAttributesDirective, ComponentOutletInjectorModule],

55

exports: [DynamicAttributesDirective, ComponentOutletInjectorModule]

56

})

57

export class DynamicAttributesModule {}

58

```

59

60

## Types

61

62

```typescript { .api }

63

/**

64

* Map of attribute names to their string values

65

* Used for declarative attribute binding

66

*/

67

interface AttributesMap {

68

[key: string]: string;

69

}

70

```

71

72

**Usage Examples:**

73

74

```typescript

75

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

76

import { DynamicComponent, DynamicAttributesDirective, AttributesMap } from 'ng-dynamic-component';

77

78

// Basic dynamic attributes with ndc-dynamic

79

@Component({

80

standalone: true,

81

imports: [DynamicComponent, DynamicAttributesDirective],

82

template: `

83

<ndc-dynamic

84

[ndcDynamicComponent]="component"

85

[ndcDynamicAttributes]="attributes">

86

</ndc-dynamic>

87

`

88

})

89

export class BasicAttributesExampleComponent {

90

component = MyComponent;

91

92

attributes: AttributesMap = {

93

'data-testid': 'dynamic-component',

94

'aria-label': 'Dynamic content area',

95

'role': 'region',

96

'class': 'theme-dark border-rounded',

97

'style': 'min-height: 200px; background: #f5f5f5;'

98

};

99

}

100

101

// Dynamic attribute changes

102

@Component({

103

template: `

104

<div class="controls">

105

<button (click)="toggleTheme()">Toggle Theme</button>

106

<button (click)="updateAccessibility()">Update Accessibility</button>

107

<button (click)="addCustomAttributes()">Add Custom Attrs</button>

108

</div>

109

110

<ndc-dynamic

111

[ndcDynamicComponent]="component"

112

[ndcDynamicAttributes]="currentAttributes"

113

#dynamicAttrs="ndcDynamicAttributes">

114

</ndc-dynamic>

115

116

<button (click)="imperativeUpdate(dynamicAttrs)">Imperative Update</button>

117

`

118

})

119

export class DynamicAttributesExampleComponent {

120

component = ContentComponent;

121

122

private theme: 'light' | 'dark' = 'light';

123

private accessibilityLevel: 'basic' | 'enhanced' = 'basic';

124

private customAttrsEnabled = false;

125

126

get currentAttributes(): AttributesMap {

127

const attrs: AttributesMap = {

128

'data-theme': this.theme,

129

'class': `theme-${this.theme} component-wrapper`,

130

'role': 'main'

131

};

132

133

// Add accessibility attributes

134

if (this.accessibilityLevel === 'enhanced') {

135

attrs['aria-live'] = 'polite';

136

attrs['aria-describedby'] = 'component-description';

137

attrs['tabindex'] = '0';

138

}

139

140

// Add custom attributes

141

if (this.customAttrsEnabled) {

142

attrs['data-custom'] = 'enabled';

143

attrs['data-timestamp'] = Date.now().toString();

144

attrs['data-version'] = '1.0.0';

145

}

146

147

return attrs;

148

}

149

150

toggleTheme() {

151

this.theme = this.theme === 'light' ? 'dark' : 'light';

152

}

153

154

updateAccessibility() {

155

this.accessibilityLevel = this.accessibilityLevel === 'basic' ? 'enhanced' : 'basic';

156

}

157

158

addCustomAttributes() {

159

this.customAttrsEnabled = !this.customAttrsEnabled;

160

}

161

162

imperativeUpdate(directiveRef: DynamicAttributesDirective) {

163

// Manually set attributes using directive methods

164

directiveRef.setAttribute('data-manual', 'true');

165

directiveRef.setAttribute('data-updated', new Date().toISOString());

166

167

// Set namespaced attribute

168

directiveRef.setAttribute('custom-attr', 'value', 'custom-namespace');

169

170

// Remove an attribute

171

setTimeout(() => {

172

directiveRef.removeAttribute('data-manual');

173

}, 3000);

174

}

175

}

176

```

177

178

### Working with NgComponentOutlet

179

180

Use dynamic attributes with Angular's built-in component outlet:

181

182

```typescript

183

import { ComponentOutletIoDirective, DynamicAttributesDirective } from 'ng-dynamic-component';

184

185

@Component({

186

standalone: true,

187

imports: [ComponentOutletIoDirective, DynamicAttributesDirective],

188

template: `

189

<ng-container

190

*ngComponentOutlet="selectedComponent"

191

[ngComponentOutletNdcDynamicAttributes]="outletAttributes"

192

[ngComponentOutletNdcDynamicInputs]="inputs">

193

</ng-container>

194

`

195

})

196

export class OutletAttributesExampleComponent {

197

selectedComponent = DashboardComponent;

198

199

inputs = {

200

title: 'Dashboard',

201

refreshRate: 5000

202

};

203

204

outletAttributes: AttributesMap = {

205

'data-component': 'dashboard',

206

'aria-label': 'Dashboard content',

207

'class': 'outlet-component dashboard-theme',

208

'data-refresh-rate': '5000'

209

};

210

}

211

```

212

213

### Conditional and Computed Attributes

214

215

Create dynamic attributes based on component state and conditions:

216

217

```typescript

218

@Component({

219

template: `

220

<div class="status-indicators">

221

<span>Loading: {{ isLoading }}</span>

222

<span>Error: {{ hasError }}</span>

223

<span>Theme: {{ currentTheme }}</span>

224

</div>

225

226

<ndc-dynamic

227

[ndcDynamicComponent]="component"

228

[ndcDynamicAttributes]="computedAttributes">

229

</ndc-dynamic>

230

`

231

})

232

export class ConditionalAttributesComponent {

233

component = DataComponent;

234

235

isLoading = false;

236

hasError = false;

237

currentTheme = 'light';

238

userRole = 'user';

239

featureFlags = ['feature-a', 'feature-b'];

240

241

get computedAttributes(): AttributesMap {

242

const attrs: AttributesMap = {};

243

244

// Base attributes

245

attrs['data-component'] = 'data-display';

246

attrs['data-theme'] = this.currentTheme;

247

248

// Conditional attributes based on state

249

if (this.isLoading) {

250

attrs['aria-busy'] = 'true';

251

attrs['data-loading'] = 'true';

252

attrs['class'] = 'component-loading';

253

}

254

255

if (this.hasError) {

256

attrs['aria-invalid'] = 'true';

257

attrs['data-error'] = 'true';

258

attrs['class'] = (attrs['class'] || '') + ' component-error';

259

}

260

261

// Role-based attributes

262

if (this.userRole === 'admin') {

263

attrs['data-admin'] = 'true';

264

attrs['data-permissions'] = 'all';

265

}

266

267

// Feature flag attributes

268

if (this.featureFlags.includes('feature-a')) {

269

attrs['data-feature-a'] = 'enabled';

270

}

271

272

// Computed styles

273

attrs['style'] = this.getComputedStyles();

274

275

return attrs;

276

}

277

278

private getComputedStyles(): string {

279

const styles: string[] = [];

280

281

if (this.isLoading) {

282

styles.push('opacity: 0.6');

283

styles.push('pointer-events: none');

284

}

285

286

if (this.hasError) {

287

styles.push('border: 2px solid red');

288

}

289

290

if (this.currentTheme === 'dark') {

291

styles.push('background: #2d2d2d');

292

styles.push('color: #ffffff');

293

}

294

295

return styles.join('; ');

296

}

297

298

simulateLoading() {

299

this.isLoading = true;

300

setTimeout(() => {

301

this.isLoading = false;

302

this.hasError = Math.random() > 0.7; // 30% chance of error

303

}, 2000);

304

}

305

}

306

```

307

308

### Accessibility and ARIA Attributes

309

310

Dynamically manage accessibility attributes for better user experience:

311

312

```typescript

313

@Component({

314

template: `

315

<div class="accessibility-controls">

316

<label>

317

<input type="checkbox" [(ngModel)]="screenReaderOptimized" />

318

Screen Reader Optimized

319

</label>

320

<label>

321

<input type="checkbox" [(ngModel)]="highContrast" />

322

High Contrast

323

</label>

324

<label>

325

<input type="range" min="1" max="5" [(ngModel)]="complexityLevel" />

326

Complexity Level: {{ complexityLevel }}

327

</label>

328

</div>

329

330

<ndc-dynamic

331

[ndcDynamicComponent]="component"

332

[ndcDynamicAttributes]="accessibilityAttributes">

333

</ndc-dynamic>

334

`

335

})

336

export class AccessibilityAttributesComponent {

337

component = InteractiveComponent;

338

339

screenReaderOptimized = false;

340

highContrast = false;

341

complexityLevel = 3;

342

343

get accessibilityAttributes(): AttributesMap {

344

const attrs: AttributesMap = {

345

'role': 'application',

346

'aria-label': 'Interactive content area'

347

};

348

349

if (this.screenReaderOptimized) {

350

attrs['aria-live'] = 'polite';

351

attrs['aria-atomic'] = 'true';

352

attrs['aria-relevant'] = 'additions text';

353

attrs['aria-describedby'] = 'sr-instructions';

354

}

355

356

if (this.highContrast) {

357

attrs['data-high-contrast'] = 'true';

358

attrs['class'] = 'high-contrast-mode';

359

}

360

361

// Complexity-based attributes

362

attrs['data-complexity'] = this.complexityLevel.toString();

363

364

if (this.complexityLevel <= 2) {

365

attrs['aria-label'] = 'Simple interactive content';

366

attrs['data-ui-mode'] = 'simplified';

367

} else if (this.complexityLevel >= 4) {

368

attrs['aria-label'] = 'Advanced interactive content with multiple features';

369

attrs['data-ui-mode'] = 'advanced';

370

attrs['aria-expanded'] = 'false';

371

}

372

373

return attrs;

374

}

375

}

376

```

377

378

### Data Attributes for Testing and Analytics

379

380

Use dynamic attributes for testing identifiers and analytics tracking:

381

382

```typescript

383

@Component({

384

template: `

385

<ndc-dynamic

386

[ndcDynamicComponent]="component"

387

[ndcDynamicAttributes]="testingAndAnalyticsAttributes">

388

</ndc-dynamic>

389

`

390

})

391

export class TestingAttributesComponent implements OnInit {

392

component = TestableComponent;

393

394

private sessionId = '';

395

private userId = '';

396

private experimentVariant = '';

397

398

ngOnInit() {

399

this.sessionId = this.generateSessionId();

400

this.userId = this.getCurrentUserId();

401

this.experimentVariant = this.getExperimentVariant();

402

}

403

404

get testingAndAnalyticsAttributes(): AttributesMap {

405

const attrs: AttributesMap = {

406

// Testing attributes

407

'data-testid': 'dynamic-component',

408

'data-qa': 'main-content',

409

'data-component-type': this.component.name,

410

411

// Analytics attributes

412

'data-analytics-id': 'dynamic-content',

413

'data-session-id': this.sessionId,

414

'data-user-id': this.userId,

415

'data-page-section': 'main',

416

417

// A/B testing attributes

418

'data-experiment': 'layout-test',

419

'data-variant': this.experimentVariant,

420

421

// Performance tracking

422

'data-track-performance': 'true',

423

'data-component-load-time': Date.now().toString()

424

};

425

426

return attrs;

427

}

428

429

private generateSessionId(): string {

430

return 'session-' + Math.random().toString(36).substr(2, 9);

431

}

432

433

private getCurrentUserId(): string {

434

// Get from authentication service

435

return 'user-123';

436

}

437

438

private getExperimentVariant(): string {

439

// Get from A/B testing service

440

return Math.random() > 0.5 ? 'variant-a' : 'variant-b';

441

}

442

}

443

```

444

445

## Module Usage

446

447

For NgModule-based applications:

448

449

```typescript

450

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

451

import { DynamicAttributesModule } from 'ng-dynamic-component';

452

453

@NgModule({

454

imports: [

455

DynamicAttributesModule

456

],

457

// Components can now use dynamic attributes

458

})

459

export class MyFeatureModule {}

460

```