igniteui-angular 22.1.4 → 22.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/combo/README.md +19 -0
- package/drop-down/README.md +40 -4
- package/fesm2022/igniteui-angular-accordion.mjs +7 -7
- package/fesm2022/igniteui-angular-action-strip.mjs +10 -10
- package/fesm2022/igniteui-angular-avatar.mjs +7 -7
- package/fesm2022/igniteui-angular-badge.mjs +7 -7
- package/fesm2022/igniteui-angular-banner.mjs +10 -10
- package/fesm2022/igniteui-angular-bottom-nav.mjs +22 -22
- package/fesm2022/igniteui-angular-button-group.mjs +12 -13
- package/fesm2022/igniteui-angular-button-group.mjs.map +1 -1
- package/fesm2022/igniteui-angular-calendar.mjs +61 -61
- package/fesm2022/igniteui-angular-card.mjs +31 -31
- package/fesm2022/igniteui-angular-carousel.mjs +22 -22
- package/fesm2022/igniteui-angular-chat-extras.mjs +6 -6
- package/fesm2022/igniteui-angular-chat.mjs +12 -12
- package/fesm2022/igniteui-angular-checkbox.mjs +7 -7
- package/fesm2022/igniteui-angular-chips.mjs +10 -10
- package/fesm2022/igniteui-angular-combo.mjs +268 -124
- package/fesm2022/igniteui-angular-combo.mjs.map +1 -1
- package/fesm2022/igniteui-angular-core.mjs +207 -59
- package/fesm2022/igniteui-angular-core.mjs.map +1 -1
- package/fesm2022/igniteui-angular-date-picker.mjs +80 -106
- package/fesm2022/igniteui-angular-date-picker.mjs.map +1 -1
- package/fesm2022/igniteui-angular-dialog.mjs +13 -13
- package/fesm2022/igniteui-angular-directives.mjs +321 -457
- package/fesm2022/igniteui-angular-directives.mjs.map +1 -1
- package/fesm2022/igniteui-angular-drop-down.mjs +331 -104
- package/fesm2022/igniteui-angular-drop-down.mjs.map +1 -1
- package/fesm2022/igniteui-angular-expansion-panel.mjs +28 -28
- package/fesm2022/igniteui-angular-grids-core.mjs +552 -516
- package/fesm2022/igniteui-angular-grids-core.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-grid.mjs +79 -80
- package/fesm2022/igniteui-angular-grids-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-hierarchical-grid.mjs +59 -46
- package/fesm2022/igniteui-angular-grids-hierarchical-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-lite.mjs +12 -12
- package/fesm2022/igniteui-angular-grids-pivot-grid.mjs +113 -86
- package/fesm2022/igniteui-angular-grids-pivot-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-grids-tree-grid.mjs +57 -57
- package/fesm2022/igniteui-angular-grids-tree-grid.mjs.map +1 -1
- package/fesm2022/igniteui-angular-icon.mjs +10 -10
- package/fesm2022/igniteui-angular-input-group.mjs +73 -83
- package/fesm2022/igniteui-angular-input-group.mjs.map +1 -1
- package/fesm2022/igniteui-angular-list.mjs +40 -40
- package/fesm2022/igniteui-angular-navbar.mjs +13 -13
- package/fesm2022/igniteui-angular-navigation-drawer.mjs +16 -16
- package/fesm2022/igniteui-angular-paginator.mjs +19 -19
- package/fesm2022/igniteui-angular-progressbar.mjs +19 -19
- package/fesm2022/igniteui-angular-query-builder.mjs +23 -23
- package/fesm2022/igniteui-angular-query-builder.mjs.map +1 -1
- package/fesm2022/igniteui-angular-radio.mjs +59 -35
- package/fesm2022/igniteui-angular-radio.mjs.map +1 -1
- package/fesm2022/igniteui-angular-select.mjs +36 -48
- package/fesm2022/igniteui-angular-select.mjs.map +1 -1
- package/fesm2022/igniteui-angular-simple-combo.mjs +44 -33
- package/fesm2022/igniteui-angular-simple-combo.mjs.map +1 -1
- package/fesm2022/igniteui-angular-slider.mjs +28 -28
- package/fesm2022/igniteui-angular-snackbar.mjs +7 -7
- package/fesm2022/igniteui-angular-splitter.mjs +13 -13
- package/fesm2022/igniteui-angular-stepper.mjs +34 -34
- package/fesm2022/igniteui-angular-switch.mjs +7 -7
- package/fesm2022/igniteui-angular-tabs.mjs +34 -34
- package/fesm2022/igniteui-angular-time-picker.mjs +32 -51
- package/fesm2022/igniteui-angular-time-picker.mjs.map +1 -1
- package/fesm2022/igniteui-angular-toast.mjs +7 -7
- package/fesm2022/igniteui-angular-tree.mjs +28 -28
- package/fesm2022/igniteui-angular-virtual-scroll.mjs +1257 -0
- package/fesm2022/igniteui-angular-virtual-scroll.mjs.map +1 -0
- package/fesm2022/igniteui-angular.mjs +1 -0
- package/fesm2022/igniteui-angular.mjs.map +1 -1
- package/input-group/README.md +2 -0
- package/lib/core/styles/components/scrollbar/scrollbar-component.scss +0 -2
- package/lib/core/styles/components/scrollbar/scrollbar-theme.scss +23 -51
- package/lib/grids/core/src/column-actions/themes/_derived.scss +3 -3
- package/lib/grids/core/src/filtering/excel-style/themes/_derived.scss +3 -3
- package/lib/grids/themes/_derived.scss +3 -3
- package/lib/query-builder/src/query-builder/themes/_derived.scss +3 -3
- package/migrations/common/UpdateChanges.d.ts +36 -0
- package/migrations/common/UpdateChanges.js +175 -31
- package/migrations/common/UpdateChanges.spec.js +66 -0
- package/migrations/migration-collection.json +10 -0
- package/migrations/update-22_2_0/changes/theme-changes.json +77 -0
- package/migrations/update-22_2_0/index.d.ts +3 -0
- package/migrations/update-22_2_0/index.js +18 -0
- package/migrations/update-22_2_0/index.spec.d.ts +1 -0
- package/migrations/update-22_2_0/index.spec.js +161 -0
- package/migrations/update-22_2_0_grid-summary-borders/changes/theme-changes.json +17 -0
- package/migrations/update-22_2_0_grid-summary-borders/index.d.ts +3 -0
- package/migrations/update-22_2_0_grid-summary-borders/index.js +64 -0
- package/migrations/update-22_2_0_grid-summary-borders/index.spec.d.ts +1 -0
- package/migrations/update-22_2_0_grid-summary-borders/index.spec.js +100 -0
- package/package.json +6 -2
- package/schematics/tsconfig.tsbuildinfo +1 -1
- package/simple-combo/README.md +3 -0
- package/skills/igniteui-angular-components/references/form-controls.md +58 -0
- package/styles/igniteui-angular-dark.css +1 -1
- package/styles/igniteui-angular.css +1 -1
- package/styles/igniteui-bootstrap-dark.css +1 -1
- package/styles/igniteui-bootstrap-light.css +1 -1
- package/styles/igniteui-dark-green.css +1 -1
- package/styles/igniteui-fluent-dark-excel.css +1 -1
- package/styles/igniteui-fluent-dark-word.css +1 -1
- package/styles/igniteui-fluent-dark.css +1 -1
- package/styles/igniteui-fluent-light-excel.css +1 -1
- package/styles/igniteui-fluent-light-word.css +1 -1
- package/styles/igniteui-fluent-light.css +1 -1
- package/styles/igniteui-indigo-dark.css +1 -1
- package/styles/igniteui-indigo-light.css +1 -1
- package/styles/maps/igniteui-angular-dark.css.map +1 -1
- package/styles/maps/igniteui-angular.css.map +1 -1
- package/styles/maps/igniteui-bootstrap-dark.css.map +1 -1
- package/styles/maps/igniteui-bootstrap-light.css.map +1 -1
- package/styles/maps/igniteui-dark-green.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark-excel.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark-word.css.map +1 -1
- package/styles/maps/igniteui-fluent-dark.css.map +1 -1
- package/styles/maps/igniteui-fluent-light-excel.css.map +1 -1
- package/styles/maps/igniteui-fluent-light-word.css.map +1 -1
- package/styles/maps/igniteui-fluent-light.css.map +1 -1
- package/styles/maps/igniteui-indigo-dark.css.map +1 -1
- package/styles/maps/igniteui-indigo-light.css.map +1 -1
- package/types/igniteui-angular-calendar.d.ts +1 -1
- package/types/igniteui-angular-combo.d.ts +55 -12
- package/types/igniteui-angular-core.d.ts +73 -11
- package/types/igniteui-angular-date-picker.d.ts +3 -4
- package/types/igniteui-angular-directives.d.ts +50 -164
- package/types/igniteui-angular-drop-down.d.ts +76 -4
- package/types/igniteui-angular-grids-core.d.ts +63 -28
- package/types/igniteui-angular-grids-grid.d.ts +2 -7
- package/types/igniteui-angular-grids-hierarchical-grid.d.ts +5 -4
- package/types/igniteui-angular-grids-pivot-grid.d.ts +11 -5
- package/types/igniteui-angular-grids-tree-grid.d.ts +1 -1
- package/types/igniteui-angular-icon.d.ts +1 -1
- package/types/igniteui-angular-input-group.d.ts +18 -5
- package/types/igniteui-angular-query-builder.d.ts +1 -1
- package/types/igniteui-angular-radio.d.ts +16 -1
- package/types/igniteui-angular-select.d.ts +1 -2
- package/types/igniteui-angular-simple-combo.d.ts +7 -0
- package/types/igniteui-angular-stepper.d.ts +1 -1
- package/types/igniteui-angular-time-picker.d.ts +1 -3
- package/types/igniteui-angular-tree.d.ts +3 -3
- package/types/igniteui-angular-virtual-scroll.d.ts +408 -0
- package/types/igniteui-angular.d.ts +1 -0
- package/virtual-scroll/README.md +332 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# IgxVirtualScrollComponent
|
|
2
|
+
|
|
3
|
+
A high-performance virtual-scrolling component that renders only the items visible inside the viewport (plus a configurable over-scan buffer). It supports both vertical and horizontal axes, variable item sizes measured at runtime, lists far larger than the browser's maximum scroll coordinate, and remote / infinite scrolling through the `dataRequest` event.
|
|
4
|
+
|
|
5
|
+
## Imports
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
IgxVirtualScrollComponent,
|
|
10
|
+
IgxVirtualItemDirective,
|
|
11
|
+
} from 'igniteui-angular/virtual-scroll';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Basic usage
|
|
17
|
+
|
|
18
|
+
Define your list and provide a template using the `igxVirtualItem` directive:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<igx-virtual-scroll [data]="items" style="height: 400px;">
|
|
22
|
+
<ng-template igxVirtualItem let-item let-i="index">
|
|
23
|
+
<div class="list-row">{{ i }}: {{ item.name }}</div>
|
|
24
|
+
</ng-template>
|
|
25
|
+
</igx-virtual-scroll>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
@Component({ /* ... */ })
|
|
30
|
+
export class MyComponent {
|
|
31
|
+
items = Array.from({ length: 10_000 }, (_, i) => ({ name: `Item ${i}` }));
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Inputs
|
|
38
|
+
|
|
39
|
+
| Input | Type | Default | Description |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| `data` | `T[]` | `[]` | The array of items to virtualize. Compared by reference. See [Updating `data`](#updating-data). |
|
|
42
|
+
| `dataWindow` | `VirtualDataWindow<T> \| null` | `null` | A loaded page of a larger collection. Takes the place of `data` while it is set. See [Paged data](#paged-data). |
|
|
43
|
+
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Scroll axis. |
|
|
44
|
+
| `overScan` | `number` | `2` | Extra items to render beyond each edge of the viewport. Higher values reduce blank flashes during fast scrolling at the cost of slightly more DOM nodes. Normalized to a non-negative integer. |
|
|
45
|
+
| `estimatedItemSize` | `number` | `50` | Pixel size used for items before they are measured in the DOM. Set this close to the real average size for the best initial-render accuracy. A non-positive value falls back to `50`. |
|
|
46
|
+
| `itemTemplate` | `TemplateRef<IgxVsItemContext<T>> \| null` | `null` | Programmatic template that takes precedence over a content `ng-template[igxVirtualItem]`. |
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Paged data
|
|
50
|
+
|
|
51
|
+
For data that arrives a page at a time, bind `dataWindow` instead of `data`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
interface VirtualDataWindow<T> {
|
|
55
|
+
readonly items: readonly T[]; // The loaded page
|
|
56
|
+
readonly startIndex: number; // The index items[0] has in the whole collection
|
|
57
|
+
readonly totalCount: number; // How many items the whole collection has
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The list is as long as `totalCount`, so the scrollbar spans the whole collection while only
|
|
62
|
+
the page is in memory. An index in the list is an index in that collection: the item at
|
|
63
|
+
`index` is `items[index - startIndex]`, and `IgxVsItemContext.index` and `.count` are the
|
|
64
|
+
global index and the total. Indices the page does not cover render nothing, so no template
|
|
65
|
+
is instantiated for an item that has not arrived.
|
|
66
|
+
|
|
67
|
+
`stateChange` reports the range the viewport wants, which is what a consumer supplies the
|
|
68
|
+
next page from:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
load(state: VirtualScrollState) {
|
|
72
|
+
const startIndex = state.startIndex;
|
|
73
|
+
this.service.fetch(startIndex, state.endIndex - startIndex + 1)
|
|
74
|
+
.subscribe(page => this.window = { items: page.rows, startIndex, totalCount: page.total });
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Sizes are measured and kept per index, and the rows a new page renders are measured again in
|
|
79
|
+
the DOM, so moving the window costs the page rather than the collection. This assumes the
|
|
80
|
+
indexing stays stable while `totalCount` holds. A page with another `totalCount` is taken as a
|
|
81
|
+
different collection, a filtered one for instance, and every size is measured again. A sort that
|
|
82
|
+
keeps the count but puts different records at the same indices leaves the sizes measured for the
|
|
83
|
+
previous ones in place, for the indices that are not re-rendered.
|
|
84
|
+
|
|
85
|
+
`dataRequest` is not emitted in this mode — it asks for items to append, which a sized
|
|
86
|
+
collection does not need.
|
|
87
|
+
|
|
88
|
+
Paging keeps the *items* down to a page, not the size bookkeeping. The engine holds one size
|
|
89
|
+
entry per index, so its memory grows with `totalCount` rather than with the page: roughly
|
|
90
|
+
17 MB per million items. Give `totalCount` the size of the collection the consumer really
|
|
91
|
+
pages through; a value far beyond what the platform can allocate fails at the allocation.
|
|
92
|
+
|
|
93
|
+
### Lists inside a popup
|
|
94
|
+
|
|
95
|
+
A list inside a drop-down, dialog or any other container that is hidden until it opens has
|
|
96
|
+
no size to measure in the change detection pass that reveals it. The component learns its
|
|
97
|
+
size from a `ResizeObserver` and from `afterNextRender`, both of which run after a render,
|
|
98
|
+
so that first render is laid out against a viewport of zero and produces no rows. The rows
|
|
99
|
+
arrive in the next frame, once the host has been measured.
|
|
100
|
+
|
|
101
|
+
A wrapper that reacts to whether the list has children can flip state between those passes,
|
|
102
|
+
which Angular reports as `NG0100` in development mode. Read the rows after `layoutComplete`
|
|
103
|
+
instead.
|
|
104
|
+
|
|
105
|
+
A host with no box at all — hidden or detached — is not measured, because the zero it reports
|
|
106
|
+
says nothing about how large it will be once shown. Its last measurement is kept so the list
|
|
107
|
+
renders its window in the pass that reveals it again. The deliberate consequence is that the
|
|
108
|
+
rendered window stays in the DOM while the host is away.
|
|
109
|
+
|
|
110
|
+
Changing `estimatedItemSize` re-applies it to every item that has **not** yet been measured in the DOM. Items that have been measured keep their real size.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Outputs
|
|
115
|
+
|
|
116
|
+
| Output | Payload | Description |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `stateChange` | `VirtualScrollState` | Emitted when the virtual window changes. It reports the range the viewport wants, over-scan included; with `dataWindow` bound that range can reach past the loaded page, so it is not always the set of rows in the DOM. Consecutive renders that produce an identical window are not re-emitted. |
|
|
119
|
+
| `dataRequest` | `VirtualScrollDataRequest` | Emitted when the rendered window comes within a few items of the end of `data`. Use this to implement infinite / remote scrolling. |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Public API
|
|
124
|
+
|
|
125
|
+
### `scrollToIndex(index: number, options?: ScrollIntoViewOptions): Promise<void>`
|
|
126
|
+
|
|
127
|
+
Scrolls the viewport to the item at `index`.
|
|
128
|
+
|
|
129
|
+
Items outside the rendered window only have an *estimated* size, so the first jump can miss the target. The component measures the items at the landing point and corrects the scroll position, repeating until the offset is stable. The returned promise resolves on that final offset; callers that only need the first, approximate scroll can ignore it.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
@ViewChild(IgxVirtualScrollComponent) vs!: IgxVirtualScrollComponent<any>;
|
|
133
|
+
|
|
134
|
+
// Leading edge, instant (the default).
|
|
135
|
+
await this.vs.scrollToIndex(500);
|
|
136
|
+
|
|
137
|
+
// Centered, animated.
|
|
138
|
+
await this.vs.scrollToIndex(500, { block: 'center', behavior: 'smooth' });
|
|
139
|
+
|
|
140
|
+
// Only scroll if the item is not already fully visible.
|
|
141
|
+
await this.vs.scrollToIndex(500, { block: 'nearest' });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Option | Values | Notes |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `block` | `'start'` \| `'center'` \| `'end'` \| `'nearest'` | Alignment on the vertical axis. Defaults to `'start'`. |
|
|
147
|
+
| `inline` | same as `block` | Alignment on the horizontal axis; falls back to `block`. |
|
|
148
|
+
| `behavior` | `'auto'` \| `'smooth'` | Defaults to `'auto'`. |
|
|
149
|
+
|
|
150
|
+
`'nearest'` leaves the scroll position untouched when the item is already fully in view, or when the item is larger than the viewport and currently covers it. Otherwise it brings the item to its nearer edge: the start for an item before the viewport, the end for one past it. This matches native `scrollIntoView({ block: 'nearest' })`.
|
|
151
|
+
|
|
152
|
+
Out-of-range indices are clamped to the data, and the resulting offset is clamped to the largest reachable scroll position.
|
|
153
|
+
|
|
154
|
+
### `layoutComplete: Promise<void>`
|
|
155
|
+
|
|
156
|
+
Resolves once the virtual scroll has settled: the current render pass is complete, the item-size measurements it triggered are complete, and so are the renders those measurements scheduled.
|
|
157
|
+
|
|
158
|
+
Useful when you need to read the resulting DOM after a `data` change, a scroll, or a viewport resize:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
this.items = await this.service.fetch();
|
|
162
|
+
await this.vs.layoutComplete;
|
|
163
|
+
// The rendered window and the track size now reflect the new data.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## `IgxVirtualItemDirective`
|
|
169
|
+
|
|
170
|
+
Marks an `ng-template` as the item template for the nearest `igx-virtual-scroll`. The template context is typed as `IgxVsItemContext<T>`.
|
|
171
|
+
|
|
172
|
+
### Template context variables
|
|
173
|
+
|
|
174
|
+
| Variable | Type | Description |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `$implicit` (or `let-item`) | `T` | The current item. |
|
|
177
|
+
| `index` | `number` | The item's index within the full data array. |
|
|
178
|
+
| `count` | `number` | Total number of items in `data`. |
|
|
179
|
+
| `first` | `boolean` | `true` when `index === 0`. |
|
|
180
|
+
| `last` | `boolean` | `true` when `index === count - 1`. |
|
|
181
|
+
| `even` | `boolean` | `true` when `index` is even. |
|
|
182
|
+
| `odd` | `boolean` | `true` when `index` is odd. |
|
|
183
|
+
|
|
184
|
+
```html
|
|
185
|
+
<ng-template igxVirtualItem let-item let-i="index" let-first="first">
|
|
186
|
+
<div [class.first-row]="first">{{ i }}: {{ item }}</div>
|
|
187
|
+
</ng-template>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Output type reference
|
|
193
|
+
|
|
194
|
+
### `VirtualScrollState`
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
interface VirtualScrollState {
|
|
198
|
+
startIndex: number; // First item index of the wanted range
|
|
199
|
+
endIndex: number; // Last item index of the wanted range (inclusive)
|
|
200
|
+
viewportSize: number; // Viewport height (or width) in px
|
|
201
|
+
totalSize: number; // Total virtual content size in px
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The range is what the viewport wants, the over-scan buffer included. Bound to `data` that is
|
|
206
|
+
the set of rows in the DOM. Bound to `dataWindow` it is the range to load next, and the rows
|
|
207
|
+
actually rendered are its intersection with the page - which can be narrower, or empty.
|
|
208
|
+
|
|
209
|
+
### `VirtualScrollDataRequest`
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
interface VirtualScrollDataRequest {
|
|
213
|
+
startIndex: number; // First index that does not yet have data
|
|
214
|
+
count: number; // Suggested number of items to fetch
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Updating `data`
|
|
221
|
+
|
|
222
|
+
`data` is compared **by reference**. Mutating the array in place (`items.push(...)`) does not trigger an update. Assign a new array instead.
|
|
223
|
+
|
|
224
|
+
The component diffs the new array against the previous one to decide which item measurements it can keep:
|
|
225
|
+
|
|
226
|
+
* **Appending** (`[...items, ...more]`) keeps the identity of every existing index, so all previous measurements are retained.
|
|
227
|
+
* **Replacing, filtering or sorting** invalidates every index from the first difference onwards; those items are measured again on their next render.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Horizontal scrolling
|
|
232
|
+
|
|
233
|
+
Set `orientation="horizontal"`. Items are laid out in a row; ensure each item has an explicit `width` so the engine can measure sizes correctly.
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<igx-virtual-scroll [data]="items" orientation="horizontal" style="width: 100%; height: 60px;">
|
|
237
|
+
<ng-template igxVirtualItem let-item>
|
|
238
|
+
<div class="col" style="width: 120px; height: 60px;">{{ item }}</div>
|
|
239
|
+
</ng-template>
|
|
240
|
+
</igx-virtual-scroll>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Right-to-left (RTL) layouts are fully supported. When the component (or an ancestor) sets `dir="rtl"`, horizontal scrolling, content positioning, and `scrollToIndex` are mirrored automatically. No extra configuration is required.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Infinite / remote scrolling
|
|
248
|
+
|
|
249
|
+
Listen to the `dataRequest` output and append more items to the `data` array:
|
|
250
|
+
|
|
251
|
+
```html
|
|
252
|
+
<igx-virtual-scroll [data]="items" (dataRequest)="loadMore($event)" style="height: 500px;">
|
|
253
|
+
<ng-template igxVirtualItem let-item>
|
|
254
|
+
<div class="row">{{ item.label }}</div>
|
|
255
|
+
</ng-template>
|
|
256
|
+
</igx-virtual-scroll>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
loadMore(req: VirtualScrollDataRequest) {
|
|
261
|
+
this.myService.fetch(req.startIndex, req.count).subscribe(newItems => {
|
|
262
|
+
this.items = [...this.items, ...newItems];
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`dataRequest` is also emitted on the **first render** when the initially loaded items do not fill the viewport, so a short initial `data` array is enough to start the loading chain. An **empty** array is not: with nothing loaded there is no rendered window to run out of, so load the first page yourself and let `dataRequest` carry the rest.
|
|
268
|
+
|
|
269
|
+
Only one request is in flight at a time: the next one is emitted after `data` changes. If your source is exhausted and you reassign `data` without adding items, the component will not ask again for the same `startIndex`.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Programmatic template
|
|
274
|
+
|
|
275
|
+
Pass a `TemplateRef` via `[itemTemplate]` when the template is defined outside the component:
|
|
276
|
+
|
|
277
|
+
```html
|
|
278
|
+
<ng-template #myTpl let-item>
|
|
279
|
+
<div>{{ item }}</div>
|
|
280
|
+
</ng-template>
|
|
281
|
+
|
|
282
|
+
<igx-virtual-scroll [data]="items" [itemTemplate]="myTpl" style="height: 400px;" />
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Styling and DOM structure
|
|
288
|
+
|
|
289
|
+
```html
|
|
290
|
+
<igx-virtual-scroll class="igx-virtual-scroll" data-orientation="vertical" role="list">
|
|
291
|
+
<div class="igx-virtual-scroll__track" role="presentation"> <!-- full virtual extent -->
|
|
292
|
+
<div class="igx-virtual-scroll__content" role="presentation"><!-- translated into position -->
|
|
293
|
+
<div class="igx-virtual-item" role="presentation" data-index="12">
|
|
294
|
+
<!-- your item template -->
|
|
295
|
+
</div>
|
|
296
|
+
...
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</igx-virtual-scroll>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
| Class / attribute | Element | Notes |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| `igx-virtual-scroll` | Host | Always present. |
|
|
305
|
+
| `data-orientation` | Host | Reflects the `orientation` input: `"vertical"` or `"horizontal"`. |
|
|
306
|
+
| `igx-virtual-scroll__track` | Inner spacer div | Sized to the full virtual height/width. |
|
|
307
|
+
| `igx-virtual-scroll__content` | Rendered-items wrapper | Absolutely positioned; translated to the correct virtual offset. |
|
|
308
|
+
| `igx-virtual-item` | Per-item wrapper | One per rendered item; carries `data-index` and is the element the engine measures. |
|
|
309
|
+
|
|
310
|
+
The host element must have a **fixed height** (vertical) or **fixed width** (horizontal) and `overflow: auto` or `overflow: scroll`. The default styles already set this.
|
|
311
|
+
|
|
312
|
+
### Item sizing
|
|
313
|
+
|
|
314
|
+
Items are measured by their **border box**, so margins are not included and accumulate as drift down the list. Use `padding` on the item, or a `gap` on a wrapper, instead of margins.
|
|
315
|
+
|
|
316
|
+
### Accessibility
|
|
317
|
+
|
|
318
|
+
Only the current window is in the DOM, so assistive technology cannot infer an item's position from the markup. The host carries `role="list"`; if your template renders a role with set semantics (`listitem`, `option`, `row`, ...), map the context's `index` and `count` onto `aria-posinset` and `aria-setsize`:
|
|
319
|
+
|
|
320
|
+
```html
|
|
321
|
+
<ng-template igxVirtualItem let-item let-i="index" let-count="count">
|
|
322
|
+
<div role="listitem" [attr.aria-posinset]="i + 1" [attr.aria-setsize]="count">
|
|
323
|
+
{{ item }}
|
|
324
|
+
</div>
|
|
325
|
+
</ng-template>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Very large lists
|
|
331
|
+
|
|
332
|
+
Browsers cap how far an element can scroll. When the total item size exceeds that limit, the component compresses the virtual coordinate space into the range the browser can represent and scales scroll positions accordingly. Items still render at their real pixel size, so lists of millions of items scroll correctly with no configuration.
|