0
# Layout Components
1
2
Layout and structural components including collapse, offcanvas, pagination, dropdown, progressbar, and scrollspy for organizing content and navigation.
3
4
## Core Imports
5
6
```typescript
7
import {
8
NgbCollapseModule,
9
NgbOffcanvasModule,
10
NgbPaginationModule,
11
NgbDropdownModule,
12
NgbProgressbarModule,
13
NgbScrollSpyModule
14
} from '@ng-bootstrap/ng-bootstrap';
15
```
16
17
## Capabilities
18
19
### NgbCollapse
20
21
Directive for collapsible content areas.
22
23
```typescript { .api }
24
@Directive({
25
selector: '[ngbCollapse]',
26
exportAs: 'ngbCollapse'
27
})
28
class NgbCollapse {
29
/** Controls collapsed state */
30
@Input() ngbCollapse: boolean;
31
32
/** Enable/disable animations */
33
@Input() animation: boolean;
34
35
/** Event emitted when collapse state changes */
36
@Output() ngbCollapseChange: EventEmitter<boolean>;
37
38
/** Toggle collapsed state */
39
toggle(): void;
40
}
41
```
42
43
### NgbOffcanvas
44
45
Service for opening and managing offcanvas panels.
46
47
```typescript { .api }
48
@Injectable({ providedIn: 'root' })
49
class NgbOffcanvas {
50
/** Open an offcanvas panel */
51
open(content: any, options?: NgbOffcanvasOptions): NgbOffcanvasRef;
52
53
/** Dismiss all open offcanvas panels */
54
dismissAll(reason?: any): void;
55
56
/** Check if there are any open offcanvas panels */
57
hasOpenOffcanvas(): boolean;
58
}
59
```
60
61
### NgbOffcanvasRef
62
63
Reference to an opened offcanvas instance.
64
65
```typescript { .api }
66
class NgbOffcanvasRef {
67
/** Instance of the component if offcanvas contains a component */
68
componentInstance?: any;
69
70
/** Promise that resolves when offcanvas is closed or dismissed */
71
result: Promise<any>;
72
73
/** Close the offcanvas with a result */
74
close(result?: any): void;
75
76
/** Dismiss the offcanvas with a reason */
77
dismiss(reason?: any): void;
78
}
79
```
80
81
### NgbActiveOffcanvas
82
83
Reference to the currently active offcanvas (used within offcanvas content).
84
85
```typescript { .api }
86
@Injectable()
87
class NgbActiveOffcanvas {
88
/** Close the offcanvas with a result */
89
close(result?: any): void;
90
91
/** Dismiss the offcanvas with a reason */
92
dismiss(reason?: any): void;
93
}
94
```
95
96
### Pagination Components
97
98
Components for navigating through pages of content with customizable templates.
99
100
#### NgbPagination
101
102
Main pagination component that displays page numbers and navigation links.
103
104
```typescript { .api }
105
@Component({
106
selector: 'ngb-pagination',
107
exportAs: 'ngbPagination'
108
})
109
class NgbPagination {
110
/** If true, pagination links will be disabled */
111
@Input() disabled: boolean;
112
113
/** If true, the "First" and "Last" page links are shown */
114
@Input() boundaryLinks: boolean;
115
116
/** If true, the "Next" and "Previous" page links are shown */
117
@Input() directionLinks: boolean;
118
119
/** If true, ellipsis symbols and first/last page numbers shown when maxSize > pages */
120
@Input() ellipses: boolean;
121
122
/** Whether to rotate pages when maxSize > number of pages */
123
@Input() rotate: boolean;
124
125
/** The number of items in your paginated collection (required) */
126
@Input({ required: true }) collectionSize: number;
127
128
/** The maximum number of pages to display */
129
@Input() maxSize: number;
130
131
/** The current page (starts with 1) */
132
@Input() page: number;
133
134
/** The number of items per page */
135
@Input() pageSize: number;
136
137
/** The pagination display size: 'sm', 'lg', or custom string */
138
@Input() size: 'sm' | 'lg' | string | null;
139
140
/** Event fired when page is changed */
141
@Output() pageChange: EventEmitter<number>;
142
143
/** Total number of pages */
144
pageCount: number;
145
146
/** Array of page numbers to display */
147
pages: number[];
148
149
/** Navigate to specific page */
150
selectPage(pageNumber: number): void;
151
152
/** Check if there's a previous page */
153
hasPrevious(): boolean;
154
155
/** Check if there's a next page */
156
hasNext(): boolean;
157
158
/** Check if next button should be disabled */
159
nextDisabled(): boolean;
160
161
/** Check if previous button should be disabled */
162
previousDisabled(): boolean;
163
}
164
```
165
166
#### Pagination Template Directives
167
168
Template directives for customizing pagination appearance.
169
170
```typescript { .api }
171
/** Template directive for ellipsis (...) */
172
@Directive({ selector: 'ng-template[ngbPaginationEllipsis]' })
173
class NgbPaginationEllipsis {
174
templateRef: TemplateRef<NgbPaginationLinkContext>;
175
}
176
177
/** Template directive for first page link */
178
@Directive({ selector: 'ng-template[ngbPaginationFirst]' })
179
class NgbPaginationFirst {
180
templateRef: TemplateRef<NgbPaginationLinkContext>;
181
}
182
183
/** Template directive for last page link */
184
@Directive({ selector: 'ng-template[ngbPaginationLast]' })
185
class NgbPaginationLast {
186
templateRef: TemplateRef<NgbPaginationLinkContext>;
187
}
188
189
/** Template directive for next page link */
190
@Directive({ selector: 'ng-template[ngbPaginationNext]' })
191
class NgbPaginationNext {
192
templateRef: TemplateRef<NgbPaginationLinkContext>;
193
}
194
195
/** Template directive for page number links */
196
@Directive({ selector: 'ng-template[ngbPaginationNumber]' })
197
class NgbPaginationNumber {
198
templateRef: TemplateRef<NgbPaginationNumberContext>;
199
}
200
201
/** Template directive for previous page link */
202
@Directive({ selector: 'ng-template[ngbPaginationPrevious]' })
203
class NgbPaginationPrevious {
204
templateRef: TemplateRef<NgbPaginationLinkContext>;
205
}
206
207
/** Template directive for all pages content */
208
@Directive({ selector: 'ng-template[ngbPaginationPages]' })
209
class NgbPaginationPages {
210
templateRef: TemplateRef<NgbPaginationPagesContext>;
211
}
212
```
213
214
#### Pagination Context Interfaces
215
216
```typescript { .api }
217
interface NgbPaginationLinkContext {
218
/** Page number displayed by the current link */
219
currentPage: number;
220
/** If true, the current link is disabled */
221
disabled: boolean;
222
}
223
224
interface NgbPaginationNumberContext extends NgbPaginationLinkContext {
225
/** The page number, displayed by the current page link */
226
$implicit: number;
227
}
228
229
interface NgbPaginationPagesContext {
230
/** The currently selected page number */
231
$implicit: number;
232
/** If true, pagination is disabled */
233
disabled: boolean;
234
/** Pages numbers that should be rendered starting with 1 */
235
pages: number[];
236
}
237
```
238
239
#### NgbPaginationConfig
240
241
Configuration service for pagination defaults.
242
243
```typescript { .api }
244
@Injectable({ providedIn: 'root' })
245
class NgbPaginationConfig {
246
disabled: boolean;
247
boundaryLinks: boolean;
248
directionLinks: boolean;
249
ellipses: boolean;
250
maxSize: number;
251
pageSize: number;
252
rotate: boolean;
253
size: 'sm' | 'lg' | string | null;
254
}
255
```
256
257
### Dropdown Components
258
259
Contextual overlays for displaying lists of links and interactive content.
260
261
#### NgbDropdown
262
263
Main dropdown directive that provides contextual overlays.
264
265
```typescript { .api }
266
@Directive({
267
selector: '[ngbDropdown]',
268
exportAs: 'ngbDropdown'
269
})
270
class NgbDropdown {
271
/** Auto-close behavior: true, false, 'inside', or 'outside' */
272
@Input() autoClose: boolean | 'outside' | 'inside';
273
274
/** Custom class applied to dropdown container */
275
@Input() dropdownClass: string;
276
277
/** Initial open state */
278
@Input('open') _open: boolean;
279
280
/** Preferred placement of dropdown menu */
281
@Input() placement: PlacementArray;
282
283
/** Popper options modifier function */
284
@Input() popperOptions: (options: Partial<Options>) => Partial<Options>;
285
286
/** Container element, currently only supports 'body' */
287
@Input() container: null | 'body';
288
289
/** Positioning behavior: 'dynamic' or 'static' */
290
@Input() display: 'dynamic' | 'static';
291
292
/** Event emitted when dropdown opens or closes */
293
@Output() openChange: EventEmitter<boolean>;
294
295
/** Check if dropdown is open */
296
isOpen(): boolean;
297
298
/** Open the dropdown */
299
open(): void;
300
301
/** Close the dropdown */
302
close(): void;
303
304
/** Toggle dropdown state */
305
toggle(): void;
306
}
307
```
308
309
#### NgbDropdownToggle
310
311
Directive for elements that toggle dropdown on click.
312
313
```typescript { .api }
314
@Directive({
315
selector: '[ngbDropdownToggle]'
316
})
317
class NgbDropdownToggle {
318
nativeElement: HTMLElement;
319
}
320
```
321
322
#### NgbDropdownAnchor
323
324
Directive for elements that anchor dropdown without click handling.
325
326
```typescript { .api }
327
@Directive({
328
selector: '[ngbDropdownAnchor]'
329
})
330
class NgbDropdownAnchor {
331
nativeElement: HTMLElement;
332
}
333
```
334
335
#### NgbDropdownMenu
336
337
Directive that wraps dropdown menu content.
338
339
```typescript { .api }
340
@Directive({
341
selector: '[ngbDropdownMenu]'
342
})
343
class NgbDropdownMenu {
344
menuItems: QueryList<NgbDropdownItem>;
345
nativeElement: HTMLElement;
346
}
347
```
348
349
#### NgbDropdownItem
350
351
Directive for dropdown items with keyboard navigation support.
352
353
```typescript { .api }
354
@Directive({
355
selector: '[ngbDropdownItem]'
356
})
357
class NgbDropdownItem {
358
/** Tab index for keyboard navigation */
359
@Input() tabindex: string | number;
360
361
/** Disabled state */
362
@Input() disabled: boolean;
363
364
nativeElement: HTMLElement;
365
}
366
```
367
368
#### NgbDropdownButtonItem
369
370
Additional directive for button dropdown items.
371
372
```typescript { .api }
373
@Directive({
374
selector: 'button[ngbDropdownItem]'
375
})
376
class NgbDropdownButtonItem {
377
item: NgbDropdownItem;
378
}
379
```
380
381
#### NgbDropdownConfig
382
383
Configuration service for dropdown defaults.
384
385
```typescript { .api }
386
@Injectable({ providedIn: 'root' })
387
class NgbDropdownConfig {
388
autoClose: boolean | 'outside' | 'inside';
389
placement: PlacementArray;
390
popperOptions: (options: Partial<Options>) => Partial<Options>;
391
container: null | 'body';
392
}
393
```
394
395
### Progressbar Components
396
397
Components for displaying progress indicators with various styling options.
398
399
#### NgbProgressbar
400
401
Main progress bar component that provides feedback on task completion.
402
403
```typescript { .api }
404
@Component({
405
selector: 'ngb-progressbar',
406
exportAs: 'ngbProgressbar'
407
})
408
class NgbProgressbar {
409
/** The current value for the progress bar (required) */
410
@Input({ required: true }) value: number;
411
412
/** The maximal value to be displayed (default: 100) */
413
@Input() max: number;
414
415
/** If true, the stripes are animated */
416
@Input() animated: boolean;
417
418
/** The accessible progress bar name */
419
@Input() ariaLabel: string;
420
421
/** If true, the progress bar will be displayed as striped */
422
@Input() striped: boolean;
423
424
/** If true, the current percentage will be shown in xx% format */
425
@Input() showValue: boolean;
426
427
/** Optional text variant type for text color */
428
@Input() textType: string;
429
430
/** The type/color of the progress bar (Bootstrap variants) */
431
@Input() type: string;
432
433
/** The height of the progress bar (accepts CSS values) */
434
@Input() height: string;
435
436
/** Get the current value within the valid range */
437
getValue(): number;
438
439
/** Get the current percentage value */
440
getPercentValue(): number;
441
}
442
```
443
444
#### NgbProgressbarStacked
445
446
Container component for stacked progress bars.
447
448
```typescript { .api }
449
@Component({
450
selector: 'ngb-progressbar-stacked',
451
exportAs: 'ngbProgressbarStacked'
452
})
453
class NgbProgressbarStacked {
454
// Container component - no inputs, only provides stacking context
455
}
456
```
457
458
#### NgbProgressbarConfig
459
460
Configuration service for progressbar defaults.
461
462
```typescript { .api }
463
@Injectable({ providedIn: 'root' })
464
class NgbProgressbarConfig {
465
max: number;
466
animated: boolean;
467
ariaLabel: string;
468
striped: boolean;
469
textType: string;
470
type: string;
471
showValue: boolean;
472
height: string;
473
}
474
```
475
476
### ScrollSpy Components
477
478
Components for automatically updating navigation based on scroll position.
479
480
#### NgbScrollSpy
481
482
Main scrollspy directive that observes fragments and maintains active state.
483
484
```typescript { .api }
485
@Directive({
486
selector: '[ngbScrollSpy]',
487
exportAs: 'ngbScrollSpy'
488
})
489
class NgbScrollSpy {
490
/** Custom process changes function */
491
@Input() processChanges: NgbScrollSpyProcessChanges;
492
493
/** Scroll behavior for programmatic scrolling */
494
@Input() scrollBehavior: 'auto' | 'smooth';
495
496
/** Current active fragment id */
497
get active(): string;
498
499
/** Observable of active fragment changes */
500
get active$(): Observable<string>;
501
502
/** Scroll to a specific fragment */
503
scrollTo(fragment: string | HTMLElement, options?: NgbScrollToOptions): void;
504
}
505
```
506
507
#### NgbScrollSpyItem
508
509
Directive for menu items that link to scrollspy fragments.
510
511
```typescript { .api }
512
@Directive({
513
selector: '[ngbScrollSpyItem]',
514
exportAs: 'ngbScrollSpyItem'
515
})
516
class NgbScrollSpyItem {
517
/** Reference to scroll spy, fragment id, and optional parent */
518
@Input('ngbScrollSpyItem') data: NgbScrollSpy | string | [NgbScrollSpy, string, string?];
519
520
/** The id of the associated fragment */
521
@Input() fragment: string;
522
523
/** The id of parent menu item for nested navigation */
524
@Input() parent: string;
525
526
/** Check if this item is currently active */
527
isActive(): boolean;
528
529
/** Scroll to the associated fragment */
530
scrollTo(): void;
531
}
532
```
533
534
#### NgbScrollSpyMenu
535
536
Directive for menu containers that manage scrollspy items.
537
538
```typescript { .api }
539
@Directive({
540
selector: '[ngbScrollSpyMenu]',
541
exportAs: 'ngbScrollSpyMenu'
542
})
543
class NgbScrollSpyMenu {
544
/** Reference to the scroll spy directive */
545
@Input('ngbScrollSpyMenu') scrollSpy: NgbScrollSpy;
546
547
/** Current active fragment id */
548
get active(): string;
549
550
/** Observable of active fragment changes */
551
get active$(): Observable<string>;
552
553
/** Scroll to a specific fragment */
554
scrollTo(fragment: string | HTMLElement, options?: NgbScrollToOptions): void;
555
}
556
```
557
558
#### NgbScrollSpyFragment
559
560
Directive that marks elements as scrollspy fragments.
561
562
```typescript { .api }
563
@Directive({
564
selector: '[ngbScrollSpyFragment]'
565
})
566
class NgbScrollSpyFragment {
567
/** The unique id of the fragment */
568
@Input('ngbScrollSpyFragment') id: string;
569
}
570
```
571
572
#### NgbScrollSpyService
573
574
Injectable service for global scrollspy functionality.
575
576
```typescript { .api }
577
@Injectable({ providedIn: 'root' })
578
class NgbScrollSpyService {
579
/** Current active fragment id */
580
get active(): string;
581
582
/** Observable of active fragment changes */
583
get active$(): Observable<string>;
584
585
/** Scroll to a specific fragment */
586
scrollTo(fragment: string | HTMLElement, options?: NgbScrollToOptions): void;
587
588
/** Start observing fragments */
589
start(options: NgbScrollSpyOptions): void;
590
591
/** Stop observing fragments */
592
stop(): void;
593
}
594
```
595
596
#### NgbScrollSpyConfig
597
598
Configuration service for scrollspy defaults.
599
600
```typescript { .api }
601
@Injectable({ providedIn: 'root' })
602
class NgbScrollSpyConfig {
603
scrollBehavior: 'auto' | 'smooth';
604
processChanges: NgbScrollSpyProcessChanges;
605
}
606
```
607
608
## Configuration Services
609
610
```typescript { .api }
611
@Injectable({ providedIn: 'root' })
612
class NgbCollapseConfig {
613
/** Default animation setting */
614
animation: boolean;
615
}
616
617
@Injectable({ providedIn: 'root' })
618
class NgbOffcanvasConfig {
619
/** Default backdrop behavior */
620
backdrop: boolean | 'static';
621
622
/** Default keyboard support */
623
keyboard: boolean;
624
625
/** Default position */
626
position: 'start' | 'end' | 'top' | 'bottom';
627
628
/** Default scroll behavior */
629
scroll: boolean;
630
631
/** Default animation setting */
632
animation: boolean;
633
}
634
635
@Injectable({ providedIn: 'root' })
636
class NgbPaginationConfig {
637
/** Default disabled state */
638
disabled: boolean;
639
640
/** Default boundary links setting */
641
boundaryLinks: boolean;
642
643
/** Default direction links setting */
644
directionLinks: boolean;
645
646
/** Default max size */
647
maxSize: number;
648
649
/** Default page size */
650
pageSize: number;
651
652
/** Default rotate setting */
653
rotate: boolean;
654
655
/** Default size */
656
size: 'sm' | 'lg';
657
}
658
```
659
660
## Type Definitions
661
662
```typescript { .api }
663
interface NgbOffcanvasOptions {
664
/** Backdrop behavior */
665
backdrop?: boolean | 'static';
666
667
/** Keyboard support */
668
keyboard?: boolean;
669
670
/** Position of offcanvas */
671
position?: 'start' | 'end' | 'top' | 'bottom';
672
673
/** Allow body scrolling */
674
scroll?: boolean;
675
676
/** Animation setting */
677
animation?: boolean;
678
679
/** CSS class for offcanvas */
680
panelClass?: string;
681
682
/** Container element */
683
container?: string | Element;
684
685
/** Injector for component content */
686
injector?: Injector;
687
}
688
689
enum OffcanvasDismissReasons {
690
/** Offcanvas dismissed by clicking backdrop */
691
BACKDROP_CLICK,
692
693
/** Offcanvas dismissed by pressing ESC key */
694
ESC
695
}
696
```
697
698
## Usage Examples
699
700
### Basic Collapse
701
702
```typescript
703
@Component({
704
template: `
705
<div class="mb-3">
706
<button
707
class="btn btn-primary"
708
(click)="isCollapsed = !isCollapsed">
709
Toggle collapse
710
</button>
711
</div>
712
713
<div [ngbCollapse]="isCollapsed">
714
<div class="card card-body">
715
This content can be collapsed and expanded.
716
<p>More content here...</p>
717
</div>
718
</div>
719
720
<p class="mt-3">Collapsed: {{ isCollapsed }}</p>
721
`
722
})
723
export class BasicCollapseComponent {
724
isCollapsed = false;
725
}
726
```
727
728
### Offcanvas Panel
729
730
```typescript
731
@Component({
732
template: `
733
<button class="btn btn-primary" (click)="openOffcanvas(content)">
734
Open offcanvas
735
</button>
736
737
<ng-template #content let-offcanvas>
738
<div class="offcanvas-header">
739
<h4 class="offcanvas-title">Offcanvas Title</h4>
740
<button type="button" class="btn-close" (click)="offcanvas.dismiss()"></button>
741
</div>
742
743
<div class="offcanvas-body">
744
<p>This is the offcanvas content.</p>
745
<ul>
746
<li>Item 1</li>
747
<li>Item 2</li>
748
<li>Item 3</li>
749
</ul>
750
</div>
751
</ng-template>
752
`
753
})
754
export class BasicOffcanvasComponent {
755
constructor(private offcanvasService: NgbOffcanvas) {}
756
757
openOffcanvas(content: any) {
758
const offcanvasRef = this.offcanvasService.open(content, {
759
position: 'start',
760
backdrop: true,
761
keyboard: true
762
});
763
764
offcanvasRef.result.then((result) => {
765
console.log('Offcanvas closed:', result);
766
}).catch((error) => {
767
console.log('Offcanvas dismissed:', error);
768
});
769
}
770
}
771
```
772
773
### Basic Pagination
774
775
```typescript
776
@Component({
777
template: `
778
<ngb-pagination
779
[(page)]="currentPage"
780
[collectionSize]="totalItems"
781
[pageSize]="itemsPerPage"
782
[maxSize]="5"
783
[rotate]="true"
784
[boundaryLinks]="true"
785
(pageChange)="onPageChange($event)">
786
</ngb-pagination>
787
788
<p>Current page: {{ currentPage }} of {{ totalPages }}</p>
789
<p>Showing items {{ startItem }} - {{ endItem }} of {{ totalItems }}</p>
790
`
791
})
792
export class BasicPaginationComponent {
793
currentPage = 1;
794
totalItems = 200;
795
itemsPerPage = 10;
796
797
get totalPages() {
798
return Math.ceil(this.totalItems / this.itemsPerPage);
799
}
800
801
get startItem() {
802
return (this.currentPage - 1) * this.itemsPerPage + 1;
803
}
804
805
get endItem() {
806
return Math.min(this.currentPage * this.itemsPerPage, this.totalItems);
807
}
808
809
onPageChange(page: number) {
810
console.log('Page changed to:', page);
811
// Load data for the new page
812
}
813
}
814
```
815
816
### Progress Bars
817
818
```typescript
819
@Component({
820
template: `
821
<div class="mb-3">
822
<label>Simple progress bar:</label>
823
<ngb-progressbar
824
[value]="progress"
825
[max]="100"
826
type="success"
827
[showValue]="true">
828
</ngb-progressbar>
829
</div>
830
831
<div class="mb-3">
832
<label>Striped and animated:</label>
833
<ngb-progressbar
834
[value]="progress"
835
[max]="100"
836
type="info"
837
[striped]="true"
838
[animated]="true">
839
</ngb-progressbar>
840
</div>
841
842
<div class="mb-3">
843
<label>Stacked progress bars:</label>
844
<ngb-progressbar-stacked [max]="100">
845
<ngb-progressbar [value]="25" type="success"></ngb-progressbar>
846
<ngb-progressbar [value]="35" type="warning"></ngb-progressbar>
847
<ngb-progressbar [value]="20" type="danger"></ngb-progressbar>
848
</ngb-progressbar-stacked>
849
</div>
850
851
<div class="mb-3">
852
<button class="btn btn-primary me-2" (click)="increaseProgress()">
853
Increase
854
</button>
855
<button class="btn btn-secondary" (click)="resetProgress()">
856
Reset
857
</button>
858
</div>
859
`
860
})
861
export class ProgressBarComponent {
862
progress = 45;
863
864
increaseProgress() {
865
this.progress = Math.min(this.progress + 10, 100);
866
}
867
868
resetProgress() {
869
this.progress = 0;
870
}
871
}
872
```
873
874
### Dropdown Menu
875
876
```typescript
877
@Component({
878
template: `
879
<div ngbDropdown class="d-inline-block">
880
<button class="btn btn-primary" ngbDropdownToggle>
881
Dropdown menu
882
</button>
883
884
<div ngbDropdownMenu>
885
<button ngbDropdownItem (click)="onAction('action1')">
886
Action 1
887
</button>
888
<button ngbDropdownItem (click)="onAction('action2')">
889
Action 2
890
</button>
891
<div class="dropdown-divider"></div>
892
<button ngbDropdownItem [disabled]="true">
893
Disabled action
894
</button>
895
</div>
896
</div>
897
898
<p *ngIf="lastAction" class="mt-3">
899
Last action: {{ lastAction }}
900
</p>
901
`
902
})
903
export class DropdownComponent {
904
lastAction: string = '';
905
906
onAction(action: string) {
907
this.lastAction = action;
908
console.log('Action selected:', action);
909
}
910
}
911
```
912
913
### ScrollSpy Navigation
914
915
```typescript
916
@Component({
917
template: `
918
<div class="row">
919
<div class="col-3">
920
<nav class="nav nav-pills flex-column sticky-top">
921
<a class="nav-link" [class.active]="activeSection === 'section1'"
922
(click)="scrollTo('section1')">Section 1</a>
923
<a class="nav-link" [class.active]="activeSection === 'section2'"
924
(click)="scrollTo('section2')">Section 2</a>
925
<a class="nav-link" [class.active]="activeSection === 'section3'"
926
(click)="scrollTo('section3')">Section 3</a>
927
</nav>
928
</div>
929
930
<div class="col-9">
931
<div ngbScrollSpy
932
[ngbScrollSpy]="['section1', 'section2', 'section3']"
933
(activeChange)="onActiveChange($event)"
934
style="height: 400px; overflow-y: auto;">
935
936
<div id="section1" style="height: 300px; background-color: #f8f9fa;">
937
<h3>Section 1</h3>
938
<p>Content for section 1...</p>
939
</div>
940
941
<div id="section2" style="height: 300px; background-color: #e9ecef;">
942
<h3>Section 2</h3>
943
<p>Content for section 2...</p>
944
</div>
945
946
<div id="section3" style="height: 300px; background-color: #dee2e6;">
947
<h3>Section 3</h3>
948
<p>Content for section 3...</p>
949
</div>
950
</div>
951
</div>
952
</div>
953
`
954
})
955
export class ScrollSpyComponent {
956
activeSection: string = 'section1';
957
958
onActiveChange(activeId: string) {
959
this.activeSection = activeId;
960
}
961
962
scrollTo(sectionId: string) {
963
const element = document.getElementById(sectionId);
964
if (element) {
965
element.scrollIntoView({ behavior: 'smooth' });
966
}
967
}
968
}
969
```