0
# Utilities & Infrastructure
1
2
Core utilities for positioning, theming, date manipulation, component loading, and other infrastructure functionality.
3
4
## Capabilities
5
6
### Positioning
7
8
Advanced positioning calculations for overlays, tooltips, dropdowns, and other positioned elements.
9
10
```typescript { .api }
11
/**
12
* Service for element positioning calculations
13
*/
14
@Injectable()
15
class PositioningService {
16
/** Position target element relative to host element */
17
position(
18
hostElement: HTMLElement,
19
targetElement: HTMLElement,
20
placement?: string,
21
appendToBody?: boolean
22
): { top: number; left: number };
23
24
/** Get available positions for element */
25
getAvailablePositions(
26
hostElement: HTMLElement,
27
targetElement: HTMLElement
28
): string[];
29
30
/** Check if element fits in viewport */
31
checkFit(
32
element: HTMLElement,
33
placement: string
34
): boolean;
35
36
/** Auto position element based on available space */
37
autoPosition(
38
hostElement: HTMLElement,
39
targetElement: HTMLElement,
40
preferredPlacement?: string
41
): { top: number; left: number; placement: string };
42
}
43
44
/**
45
* Core positioning utility class
46
*/
47
class Positioning {
48
/** Get element position relative to document */
49
static position(element: HTMLElement, round?: boolean): { width: number; height: number; top: number; left: number };
50
51
/** Get element offset */
52
static offset(element: HTMLElement, round?: boolean): { width: number; height: number; top: number; left: number };
53
54
/** Position elements */
55
static positionElements(
56
hostElement: HTMLElement,
57
targetElement: HTMLElement,
58
placement: string,
59
appendToBody?: boolean,
60
options?: PositioningOptions
61
): { top: number; left: number };
62
}
63
64
/**
65
* Positioning configuration options
66
*/
67
interface PositioningOptions {
68
/** Offset from host element */
69
offset?: number;
70
/** Allow flipping placement */
71
allowFlip?: boolean;
72
/** Viewport boundaries */
73
boundary?: 'viewport' | 'window' | HTMLElement;
74
/** Placement modifiers */
75
modifiers?: PositioningModifier[];
76
}
77
78
/**
79
* Positioning modifier interface
80
*/
81
interface PositioningModifier {
82
/** Modifier name */
83
name: string;
84
/** Modifier options */
85
options?: any;
86
/** Modifier function */
87
fn?: (data: any, options: any) => any;
88
}
89
90
/**
91
* Available Bootstrap positioning options
92
*/
93
type AvailableBSPositions =
94
| 'top' | 'bottom' | 'left' | 'right'
95
| 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
96
| 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom';
97
98
/**
99
* Bootstrap 5 placement options
100
*/
101
type PlacementForBs5 =
102
| 'auto' | 'auto-start' | 'auto-end'
103
| 'top' | 'top-start' | 'top-end'
104
| 'bottom' | 'bottom-start' | 'bottom-end'
105
| 'right' | 'right-start' | 'right-end'
106
| 'left' | 'left-start' | 'left-end';
107
108
/**
109
* Utility functions for positioning
110
*/
111
declare function positionElements(
112
hostElement: HTMLElement,
113
targetElement: HTMLElement,
114
placement: string,
115
appendToBody?: boolean
116
): { top: number; left: number };
117
118
declare function checkMargins(margin: string | number): { top: number; right: number; bottom: number; left: number };
119
```
120
121
**Usage Example:**
122
123
```typescript
124
import { PositioningService, positionElements } from 'ngx-bootstrap/positioning';
125
126
@Component({
127
template: `
128
<button #hostBtn class="btn btn-primary" (click)="showTooltip()">
129
Host Element
130
</button>
131
132
<div #tooltipEl class="custom-tooltip" [style.display]="tooltipVisible ? 'block' : 'none'">
133
Custom positioned tooltip
134
</div>
135
`,
136
providers: [PositioningService]
137
})
138
export class PositioningExampleComponent {
139
@ViewChild('hostBtn') hostButton: ElementRef;
140
@ViewChild('tooltipEl') tooltipElement: ElementRef;
141
142
tooltipVisible = false;
143
144
constructor(private positioning: PositioningService) {}
145
146
showTooltip() {
147
this.tooltipVisible = true;
148
149
// Position using service
150
const position = this.positioning.position(
151
this.hostButton.nativeElement,
152
this.tooltipElement.nativeElement,
153
'top'
154
);
155
156
// Apply position
157
const tooltip = this.tooltipElement.nativeElement;
158
tooltip.style.top = position.top + 'px';
159
tooltip.style.left = position.left + 'px';
160
161
// Alternative: Use utility function
162
const utilityPosition = positionElements(
163
this.hostButton.nativeElement,
164
this.tooltipElement.nativeElement,
165
'bottom',
166
true
167
);
168
169
console.log('Service position:', position);
170
console.log('Utility position:', utilityPosition);
171
}
172
}
173
```
174
175
### Theme & Version Management
176
177
Utilities for managing Bootstrap theme versions and component styling.
178
179
```typescript { .api }
180
/**
181
* Set Bootstrap theme version
182
*/
183
declare function setTheme(theme: AvailableBsVersions): void;
184
185
/**
186
* Get current Bootstrap version information
187
*/
188
declare function getBsVer(): IBsVersion;
189
190
/**
191
* Current Bootstrap version information
192
*/
193
declare const currentBsVersion: IBsVersion;
194
195
/**
196
* Bootstrap version interface
197
*/
198
interface IBsVersion {
199
/** Is Bootstrap 3 */
200
isBs3: boolean;
201
/** Is Bootstrap 4 */
202
isBs4: boolean;
203
/** Is Bootstrap 5 */
204
isBs5: boolean;
205
}
206
207
/**
208
* Available Bootstrap versions
209
*/
210
type AvailableBsVersions = 'bs3' | 'bs4' | 'bs5';
211
212
/**
213
* Bootstrap version configurations
214
*/
215
interface BsVersions {
216
[key: string]: IBsVersion;
217
}
218
```
219
220
**Usage Example:**
221
222
```typescript
223
import { setTheme, getBsVer, currentBsVersion } from 'ngx-bootstrap/utils';
224
225
@Component({
226
template: `
227
<div class="version-info">
228
<h5>Current Bootstrap Version</h5>
229
<div class="alert alert-info">
230
<div *ngIf="version.isBs3">Bootstrap 3 is active</div>
231
<div *ngIf="version.isBs4">Bootstrap 4 is active</div>
232
<div *ngIf="version.isBs5">Bootstrap 5 is active</div>
233
</div>
234
235
<div class="btn-group" role="group">
236
<button class="btn btn-outline-primary" (click)="switchTheme('bs3')">
237
Bootstrap 3
238
</button>
239
<button class="btn btn-outline-primary" (click)="switchTheme('bs4')">
240
Bootstrap 4
241
</button>
242
<button class="btn btn-outline-primary" (click)="switchTheme('bs5')">
243
Bootstrap 5
244
</button>
245
</div>
246
</div>
247
`
248
})
249
export class ThemeManagerComponent implements OnInit {
250
version: IBsVersion;
251
252
ngOnInit() {
253
this.version = getBsVer();
254
console.log('Current version:', currentBsVersion);
255
}
256
257
switchTheme(theme: AvailableBsVersions) {
258
setTheme(theme);
259
this.version = getBsVer();
260
console.log('Switched to:', theme, this.version);
261
}
262
}
263
```
264
265
### Trigger Management
266
267
Advanced trigger handling for events, outside clicks, and keyboard interactions.
268
269
```typescript { .api }
270
/**
271
* Advanced trigger handling with multiple options
272
*/
273
declare function listenToTriggersV2(
274
renderer: Renderer2,
275
options: TriggerOptions
276
): () => void;
277
278
/**
279
* Register outside click detection
280
*/
281
declare function registerOutsideClick(
282
renderer: Renderer2,
283
options: OutsideClickOptions
284
): () => void;
285
286
/**
287
* Register escape key handling
288
*/
289
declare function registerEscClick(
290
renderer: Renderer2,
291
options: EscClickOptions
292
): () => void;
293
294
/**
295
* Trigger configuration options
296
*/
297
interface TriggerOptions {
298
/** Target element */
299
target: HTMLElement;
300
/** Trigger events */
301
triggers: string;
302
/** Show callback */
303
show: () => void;
304
/** Hide callback */
305
hide: () => void;
306
/** Toggle callback */
307
toggle?: () => void;
308
}
309
310
/**
311
* Outside click options
312
*/
313
interface OutsideClickOptions {
314
/** Target element */
315
targets: HTMLElement[];
316
/** Outside click callback */
317
outsideClick: () => void;
318
/** Exclude elements */
319
exclude?: HTMLElement[];
320
}
321
322
/**
323
* Escape click options
324
*/
325
interface EscClickOptions {
326
/** Escape key callback */
327
escClick: () => void;
328
}
329
330
/**
331
* Trigger utility class
332
*/
333
class Trigger {
334
/** Parse trigger string */
335
static parseTriggers(triggers: string): string[];
336
/** Support manual triggers */
337
static isManual(triggers: string): boolean;
338
}
339
```
340
341
**Usage Example:**
342
343
```typescript
344
import {
345
listenToTriggersV2,
346
registerOutsideClick,
347
registerEscClick
348
} from 'ngx-bootstrap/utils';
349
350
@Component({
351
template: `
352
<button #triggerBtn class="btn btn-primary">
353
Custom Trigger Element
354
</button>
355
356
<div #dropdown class="custom-dropdown" [style.display]="isOpen ? 'block' : 'none'">
357
<div class="dropdown-content">
358
Custom dropdown content
359
<button class="btn btn-sm btn-secondary" (click)="closeDropdown()">
360
Close
361
</button>
362
</div>
363
</div>
364
`
365
})
366
export class CustomTriggerComponent implements OnInit, OnDestroy {
367
@ViewChild('triggerBtn') triggerButton: ElementRef;
368
@ViewChild('dropdown') dropdown: ElementRef;
369
370
isOpen = false;
371
private unsubscribeTriggers: () => void;
372
private unsubscribeOutsideClick: () => void;
373
private unsubscribeEscClick: () => void;
374
375
constructor(private renderer: Renderer2) {}
376
377
ngOnInit() {
378
// Setup advanced triggers
379
this.unsubscribeTriggers = listenToTriggersV2(this.renderer, {
380
target: this.triggerButton.nativeElement,
381
triggers: 'click hover:500',
382
show: () => this.showDropdown(),
383
hide: () => this.hideDropdown(),
384
toggle: () => this.toggleDropdown()
385
});
386
387
// Setup outside click
388
this.unsubscribeOutsideClick = registerOutsideClick(this.renderer, {
389
targets: [this.triggerButton.nativeElement, this.dropdown.nativeElement],
390
outsideClick: () => this.hideDropdown()
391
});
392
393
// Setup escape key
394
this.unsubscribeEscClick = registerEscClick(this.renderer, {
395
escClick: () => this.hideDropdown()
396
});
397
}
398
399
ngOnDestroy() {
400
if (this.unsubscribeTriggers) this.unsubscribeTriggers();
401
if (this.unsubscribeOutsideClick) this.unsubscribeOutsideClick();
402
if (this.unsubscribeEscClick) this.unsubscribeEscClick();
403
}
404
405
showDropdown() {
406
this.isOpen = true;
407
}
408
409
hideDropdown() {
410
this.isOpen = false;
411
}
412
413
toggleDropdown() {
414
this.isOpen = !this.isOpen;
415
}
416
417
closeDropdown() {
418
this.hideDropdown();
419
}
420
}
421
```
422
423
### Component Loading
424
425
Dynamic component loading and management utilities for programmatic component creation.
426
427
```typescript { .api }
428
/**
429
* Component loader for dynamic component creation
430
*/
431
class ComponentLoader<T> {
432
/** Provide component to load */
433
provide(component: ComponentType<T>): ComponentLoader<T>;
434
435
/** Set container for component */
436
to(container?: string | ElementRef): ComponentLoader<T>;
437
438
/** Configure positioning */
439
position(opts?: PositioningOptions): ComponentLoader<T>;
440
441
/** Show component */
442
show(opts?: ComponentLoaderOptions): ComponentRef<T>;
443
444
/** Hide component */
445
hide(): ComponentLoader<T>;
446
447
/** Toggle component visibility */
448
toggle(): ComponentLoader<T>;
449
450
/** Dispose of component */
451
dispose(): void;
452
453
/** Get component instance */
454
getInstance(): ComponentRef<T>;
455
456
/** Check if component is shown */
457
isShown(): boolean;
458
}
459
460
/**
461
* Factory for creating component loaders
462
*/
463
@Injectable()
464
class ComponentLoaderFactory {
465
/** Create component loader */
466
createLoader<T>(
467
elementRef: ElementRef,
468
viewContainerRef: ViewContainerRef,
469
renderer: Renderer2
470
): ComponentLoader<T>;
471
}
472
473
/**
474
* Reference to loaded component
475
*/
476
class BsComponentRef<T> {
477
/** Template reference */
478
templateRef?: TemplateRef<T>;
479
/** View container reference */
480
viewContainer?: ViewContainerRef;
481
/** Component location */
482
location?: ElementRef;
483
484
/** Hide component */
485
hide(): void;
486
/** Show component */
487
show(): void;
488
/** Toggle component */
489
toggle(): void;
490
}
491
492
/**
493
* Reference to component content
494
*/
495
class ContentRef {
496
/** Nodes collection */
497
nodes: any[];
498
/** View reference */
499
viewRef?: ViewRef;
500
/** Component reference */
501
componentRef?: ComponentRef<any>;
502
503
/** Detach view */
504
detach(): void;
505
/** Attach view */
506
attach(): void;
507
}
508
509
/**
510
* Component loader options
511
*/
512
interface ComponentLoaderOptions {
513
/** Initial state for component */
514
initialState?: { [key: string]: any };
515
/** Providers for component */
516
providers?: StaticProvider[];
517
/** Injector for component */
518
injector?: Injector;
519
}
520
```
521
522
**Usage Example:**
523
524
```typescript
525
import {
526
ComponentLoaderFactory,
527
ComponentLoader,
528
BsComponentRef
529
} from 'ngx-bootstrap/component-loader';
530
531
// Dynamic component to load
532
@Component({
533
template: `
534
<div class="alert alert-info">
535
<h5>{{title}}</h5>
536
<p>{{message}}</p>
537
<button class="btn btn-primary" (click)="action()">
538
{{actionText}}
539
</button>
540
</div>
541
`
542
})
543
export class DynamicAlertComponent {
544
title = 'Dynamic Alert';
545
message = 'This component was loaded dynamically';
546
actionText = 'OK';
547
548
action() {
549
console.log('Dynamic component action triggered');
550
}
551
}
552
553
@Component({
554
template: `
555
<div class="component-loader-demo">
556
<button class="btn btn-primary" (click)="loadComponent()">
557
Load Dynamic Component
558
</button>
559
560
<button class="btn btn-success ml-2" (click)="showComponent()" [disabled]="!componentLoader">
561
Show Component
562
</button>
563
564
<button class="btn btn-warning ml-2" (click)="hideComponent()" [disabled]="!componentLoader">
565
Hide Component
566
</button>
567
568
<button class="btn btn-danger ml-2" (click)="disposeComponent()" [disabled]="!componentLoader">
569
Dispose Component
570
</button>
571
572
<!-- Container for dynamic component -->
573
<div #componentContainer class="mt-3"></div>
574
</div>
575
`
576
})
577
export class ComponentLoaderDemoComponent {
578
@ViewChild('componentContainer') containerRef: ElementRef;
579
580
private componentLoader: ComponentLoader<DynamicAlertComponent>;
581
582
constructor(
583
private elementRef: ElementRef,
584
private viewContainerRef: ViewContainerRef,
585
private renderer: Renderer2,
586
private componentLoaderFactory: ComponentLoaderFactory
587
) {}
588
589
loadComponent() {
590
// Create component loader
591
this.componentLoader = this.componentLoaderFactory
592
.createLoader<DynamicAlertComponent>(
593
this.elementRef,
594
this.viewContainerRef,
595
this.renderer
596
);
597
598
// Configure and show component
599
const componentRef = this.componentLoader
600
.provide(DynamicAlertComponent)
601
.to(this.containerRef)
602
.show({
603
initialState: {
604
title: 'Dynamically Loaded',
605
message: 'This component was created using ComponentLoader',
606
actionText: 'Close'
607
}
608
});
609
610
// Access component instance
611
if (componentRef && componentRef.instance) {
612
componentRef.instance.action = () => {
613
this.hideComponent();
614
};
615
}
616
}
617
618
showComponent() {
619
if (this.componentLoader) {
620
this.componentLoader.show();
621
}
622
}
623
624
hideComponent() {
625
if (this.componentLoader) {
626
this.componentLoader.hide();
627
}
628
}
629
630
disposeComponent() {
631
if (this.componentLoader) {
632
this.componentLoader.dispose();
633
this.componentLoader = null;
634
}
635
}
636
}
637
```
638
639
### Date/Time Utilities (Chronos)
640
641
Comprehensive date and time manipulation library with extensive functionality.
642
643
```typescript { .api }
644
/**
645
* Date manipulation functions
646
*/
647
declare function add(date: Date, amount: number, unit: TimeUnit): Date;
648
declare function subtract(date: Date, amount: number, unit: TimeUnit): Date;
649
declare function parseDate(input: string, format?: string, locale?: string): Date;
650
declare function formatDate(date: Date, format: string, locale?: string): string;
651
declare function utcAsLocal(date: Date): Date;
652
653
/**
654
* Date getters
655
*/
656
declare function getDay(date: Date): number;
657
declare function getMonth(date: Date): number;
658
declare function getFullYear(date: Date): number;
659
declare function isFirstDayOfWeek(date: Date, firstDayOfWeek: number): boolean;
660
declare function isSameYear(date1: Date, date2: Date): boolean;
661
declare function isSameDay(date1: Date, date2: Date): boolean;
662
declare function isSameMonth(date1: Date, date2: Date): boolean;
663
declare function getFirstDayOfMonth(date: Date): Date;
664
665
/**
666
* Date comparisons
667
*/
668
declare function isAfter(date1: Date, date2: Date): boolean;
669
declare function isBefore(date1: Date, date2: Date): boolean;
670
declare function isSame(date1: Date, date2: Date, unit?: TimeUnit): boolean;
671
declare function isDisabledDay(
672
date: Date,
673
daysDisabled: number[],
674
minDate?: Date,
675
maxDate?: Date
676
): boolean;
677
678
/**
679
* Date setters
680
*/
681
declare function shiftDate(date: Date, amount: number, unit: TimeUnit): Date;
682
declare function setFullDate(date: Date, year: number, month: number, day: number): Date;
683
684
/**
685
* Start/End operations
686
*/
687
declare function startOf(date: Date, unit: TimeUnit): Date;
688
declare function endOf(date: Date, unit: TimeUnit): Date;
689
690
/**
691
* Validation functions
692
*/
693
declare function isArray(input: any): boolean;
694
declare function isDate(input: any): boolean;
695
declare function isDateValid(date: Date): boolean;
696
697
/**
698
* Locale management
699
*/
700
declare function listLocales(): string[];
701
declare function getLocale(key?: string): any;
702
declare function updateLocale(key: string, values: any): void;
703
declare function defineLocale(key: string, config: any): void;
704
declare function getSetGlobalLocale(key?: string): string;
705
706
/**
707
* Locale data class
708
*/
709
class LocaleData {
710
/** Locale abbreviation */
711
abbr: string;
712
/** Month names */
713
months: string[];
714
/** Short month names */
715
monthsShort: string[];
716
/** Weekday names */
717
weekdays: string[];
718
/** Short weekday names */
719
weekdaysShort: string[];
720
/** Minimal weekday names */
721
weekdaysMin: string[];
722
}
723
724
/**
725
* Time units enumeration
726
*/
727
type TimeUnit = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond';
728
```
729
730
**Usage Example:**
731
732
```typescript
733
import {
734
add,
735
subtract,
736
parseDate,
737
formatDate,
738
isSameDay,
739
startOf,
740
endOf,
741
defineLocale,
742
getLocale
743
} from 'ngx-bootstrap/chronos';
744
import { esLocale } from 'ngx-bootstrap/locale';
745
746
@Component({
747
template: `
748
<div class="chronos-demo">
749
<h5>Date Manipulation</h5>
750
751
<div class="mb-3">
752
<label>Current Date: {{currentDate | date:'medium'}}</label>
753
</div>
754
755
<div class="mb-3">
756
<label>Add 7 days: {{futureDate | date:'medium'}}</label>
757
</div>
758
759
<div class="mb-3">
760
<label>Subtract 1 month: {{pastDate | date:'medium'}}</label>
761
</div>
762
763
<div class="mb-3">
764
<label>Start of month: {{monthStart | date:'medium'}}</label>
765
</div>
766
767
<div class="mb-3">
768
<label>End of month: {{monthEnd | date:'medium'}}</label>
769
</div>
770
771
<div class="mb-3">
772
<label>Custom format: {{customFormatted}}</label>
773
</div>
774
775
<div class="mb-3">
776
<label>Spanish locale: {{spanishFormatted}}</label>
777
</div>
778
779
<div class="form-group">
780
<label for="dateInput">Parse Date:</label>
781
<input
782
type="text"
783
class="form-control"
784
id="dateInput"
785
[(ngModel)]="dateInput"
786
(input)="parseInputDate()"
787
placeholder="YYYY-MM-DD">
788
<small class="form-text text-muted">
789
Parsed: {{parsedDate | date:'medium'}}
790
</small>
791
</div>
792
</div>
793
`
794
})
795
export class ChronosExampleComponent implements OnInit {
796
currentDate = new Date();
797
futureDate: Date;
798
pastDate: Date;
799
monthStart: Date;
800
monthEnd: Date;
801
customFormatted: string;
802
spanishFormatted: string;
803
804
dateInput = '2023-12-25';
805
parsedDate: Date;
806
807
ngOnInit() {
808
// Date manipulation
809
this.futureDate = add(this.currentDate, 7, 'day');
810
this.pastDate = subtract(this.currentDate, 1, 'month');
811
812
// Start/end operations
813
this.monthStart = startOf(this.currentDate, 'month');
814
this.monthEnd = endOf(this.currentDate, 'month');
815
816
// Custom formatting
817
this.customFormatted = formatDate(this.currentDate, 'dddd, MMMM Do YYYY');
818
819
// Setup Spanish locale
820
defineLocale('es', esLocale);
821
this.spanishFormatted = formatDate(this.currentDate, 'dddd, MMMM Do YYYY', 'es');
822
823
// Parse initial date
824
this.parseInputDate();
825
826
// Demonstrate comparisons
827
this.demonstrateComparisons();
828
}
829
830
parseInputDate() {
831
try {
832
this.parsedDate = parseDate(this.dateInput, 'YYYY-MM-DD');
833
} catch (error) {
834
console.log('Invalid date format');
835
this.parsedDate = null;
836
}
837
}
838
839
demonstrateComparisons() {
840
const today = new Date();
841
const tomorrow = add(today, 1, 'day');
842
843
console.log('Is same day?', isSameDay(today, tomorrow)); // false
844
console.log('Is tomorrow after today?', isAfter(tomorrow, today)); // true
845
console.log('Is today before tomorrow?', isBefore(today, tomorrow)); // true
846
}
847
}
848
```
849
850
### General Utilities
851
852
Additional utility functions and classes for common operations.
853
854
```typescript { .api }
855
/**
856
* Linked list implementation
857
*/
858
class LinkedList<T> {
859
/** Add item to list */
860
add(item: T, index?: number): void;
861
/** Remove item from list */
862
remove(index: number): void;
863
/** Get item at index */
864
get(index: number): T;
865
/** Get list length */
866
length(): number;
867
/** Convert to array */
868
toArray(): T[];
869
}
870
871
/**
872
* General utility functions
873
*/
874
class Utils {
875
/** Reflow DOM element */
876
static reflow(element: HTMLElement): void;
877
/** Get computed style */
878
static getComputedStyle(element: HTMLElement): CSSStyleDeclaration;
879
/** Check if element is visible */
880
static isVisible(element: HTMLElement): boolean;
881
}
882
883
/**
884
* Change detection decorator
885
*/
886
declare function OnChange(): PropertyDecorator;
887
888
/**
889
* Warning utility that shows message only once
890
*/
891
declare function warnOnce(message: string): void;
892
893
/**
894
* Browser object facades
895
*/
896
declare const window: Window;
897
declare const document: Document;
898
```
899
900
**Usage Example:**
901
902
```typescript
903
import { LinkedList, Utils, warnOnce } from 'ngx-bootstrap/utils';
904
905
@Component({
906
template: `
907
<div class="utils-demo">
908
<h5>Utility Functions Demo</h5>
909
910
<div class="mb-3">
911
<button class="btn btn-primary" (click)="addToList()">Add to List</button>
912
<button class="btn btn-secondary ml-2" (click)="removeFromList()">Remove Last</button>
913
<button class="btn btn-info ml-2" (click)="showListContents()">Show List</button>
914
</div>
915
916
<div class="mb-3">
917
<button class="btn btn-warning" (click)="triggerWarning()">Trigger Warning</button>
918
<button class="btn btn-success ml-2" (click)="checkVisibility()" #visibilityBtn>Check Visibility</button>
919
</div>
920
921
<div class="alert alert-info" *ngIf="listContents.length > 0">
922
<strong>List Contents:</strong>
923
<ul class="mb-0">
924
<li *ngFor="let item of listContents">{{item}}</li>
925
</ul>
926
</div>
927
</div>
928
`
929
})
930
export class UtilsExampleComponent {
931
@ViewChild('visibilityBtn') visibilityButton: ElementRef;
932
933
private myList = new LinkedList<string>();
934
listContents: string[] = [];
935
private itemCounter = 1;
936
937
addToList() {
938
const item = `Item ${this.itemCounter++}`;
939
this.myList.add(item);
940
this.updateListContents();
941
}
942
943
removeFromList() {
944
if (this.myList.length() > 0) {
945
this.myList.remove(this.myList.length() - 1);
946
this.updateListContents();
947
}
948
}
949
950
showListContents() {
951
this.updateListContents();
952
console.log('Current list:', this.listContents);
953
}
954
955
private updateListContents() {
956
this.listContents = this.myList.toArray();
957
}
958
959
triggerWarning() {
960
warnOnce('This warning will only appear once per session');
961
}
962
963
checkVisibility() {
964
if (this.visibilityButton) {
965
const isVisible = Utils.isVisible(this.visibilityButton.nativeElement);
966
const computedStyle = Utils.getComputedStyle(this.visibilityButton.nativeElement);
967
968
console.log('Button is visible:', isVisible);
969
console.log('Button computed style:', computedStyle);
970
971
// Demonstrate reflow
972
Utils.reflow(this.visibilityButton.nativeElement);
973
}
974
}
975
}
976
```