Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6530085

Browse files
authored
Merge pull request #1693 from rx-angular/virtual-scroll-update
feat(template): introduce appendOnly mode, initialScrollIndex & bugfixes
2 parents d282385 + d71d1ec commit 6530085

File tree

14 files changed

+356
-114
lines changed

14 files changed

+356
-114
lines changed

apps/demos/src/app/features/template/rx-virtual-for/virtual-rendering/virtual-for-demo.component.ts

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,23 +127,40 @@ import { RxVirtualScrollViewportComponent } from '@rx-angular/template/experimen
127127
</div>
128128
</div>
129129
<rxa-array-provider
130+
numberOfItems="1000"
131+
[initialNumberOfItems]="1000"
130132
[unpatched]="[]"
131133
[buttons]="true"
132134
></rxa-array-provider>
133135
<div class="d-flex justify-content-between">
134136
<div class="w-50" *rxIf="showRxa$">
135137
<h2 class="mat-subheading-2">*rxVirtualFor</h2>
136138
<div class="d-flex">
137-
<input
138-
style="width: 200px"
139-
matInput
140-
#scrollToInput
141-
placeholder="scrollToIndex"
142-
type="number"
143-
/>
144-
<button mat-button (click)="scrollToIndex(scrollToInput.value)">
145-
ScrollTo
146-
</button>
139+
<div class="d-flex">
140+
<input
141+
style="width: 200px"
142+
matInput
143+
#scrollToInput
144+
placeholder="scrollToIndex"
145+
type="number"
146+
/>
147+
<button mat-button (click)="scrollToIndex(scrollToInput.value)">
148+
ScrollTo
149+
</button>
150+
</div>
151+
<div class="d-flex">
152+
<input
153+
style="width: 200px"
154+
matInput
155+
#initialScrollToInput
156+
[value]="initialScrollTo"
157+
(change)="
158+
setInitialScrollTo(initialScrollToInput.valueAsNumber)
159+
"
160+
placeholder="initialScrollTo"
161+
type="number"
162+
/>
163+
</div>
147164
</div>
148165
<h2 class="mat-subheading-1">Stats</h2>
149166
<div class="stats">
@@ -166,6 +183,7 @@ import { RxVirtualScrollViewportComponent } from '@rx-angular/template/experimen
166183
<rx-virtual-scroll-viewport
167184
(scrolledIndexChange)="rxaScrolledIndex$.next($event)"
168185
*ngIf="state.scrollStrategy === 'fixed'"
186+
[initialScrollIndex]="initialScrollTo"
169187
[itemSize]="itemSize"
170188
[runwayItemsOpposite]="state.runwayItemsOpposite"
171189
[runwayItems]="state.runwayItems"
@@ -189,6 +207,7 @@ import { RxVirtualScrollViewportComponent } from '@rx-angular/template/experimen
189207
*ngIf="state.scrollStrategy === 'auto'"
190208
(scrolledIndexChange)="rxaScrolledIndex$.next($event)"
191209
autosize
210+
[initialScrollIndex]="initialScrollTo"
192211
withSyncScrollbar
193212
[resizeObserverConfig]="{
194213
extractSize: extractSize
@@ -376,7 +395,7 @@ export class VirtualForDemoComponent implements OnInit, AfterViewInit {
376395
strategy$ = new Subject<RxStrategyNames<string>>();
377396
scrollStrategy$ = this.state.select('scrollStrategy');
378397
rxVirtualForState$ = this.state.select();
379-
components$ = new BehaviorSubject<'cdk' | 'rxa' | 'both'>('both');
398+
components$ = new BehaviorSubject<'cdk' | 'rxa' | 'both'>('rxa');
380399

381400
showRxa$ = this.components$.pipe(
382401
map((components) => components === 'rxa' || components === 'both')
@@ -433,6 +452,8 @@ export class VirtualForDemoComponent implements OnInit, AfterViewInit {
433452
item: TestItem & { tmpl: TemplateRef<any>; content: string }
434453
): number => item.id;
435454

455+
initialScrollTo = parseInt(localStorage.getItem('vs-initialScrollTo') ?? '0');
456+
436457
constructor(
437458
public state: RxState<{
438459
data: any[];
@@ -451,6 +472,7 @@ export class VirtualForDemoComponent implements OnInit, AfterViewInit {
451472
ngOnInit() {}
452473

453474
ngAfterViewInit() {
475+
this.arrayProvider.addItemsImmutable(1000);
454476
this.state.connect(
455477
'data',
456478
this.arrayProvider.array$.pipe(
@@ -471,6 +493,11 @@ export class VirtualForDemoComponent implements OnInit, AfterViewInit {
471493
);
472494
}
473495

496+
setInitialScrollTo(index: number) {
497+
localStorage.setItem('vs-initialScrollTo', index.toString());
498+
this.initialScrollTo = index;
499+
}
500+
474501
scrollToIndex(index: string): void {
475502
this.virtualViewport.scrollToIndex(coerceNumberProperty(index, 0));
476503
}

apps/demos/src/app/shared/debug-helper/value-provider/array-provider.service.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class ArrayProviderService extends RxState<ProvidedValues> {
2727
})
2828
);
2929
protected completeSubject = new Subject<any>();
30-
protected resetSubject = new Subject<any>();
30+
protected resetSubject = new Subject<number | undefined>();
3131

3232
protected addItemsImmutableSubject = new Subject<number | undefined>();
3333
protected moveItemsImmutableSubject = new Subject<number | undefined>();
@@ -42,7 +42,6 @@ export class ArrayProviderService extends RxState<ProvidedValues> {
4242

4343
private resetAll = () => {
4444
this.resetObservables();
45-
this.cdRef.markForCheck();
4645
};
4746

4847
private resetObservables = () => {
@@ -93,6 +92,10 @@ export class ArrayProviderService extends RxState<ProvidedValues> {
9392
removeItemsMutable(state?.array || [], ids)
9493
);
9594

95+
this.connect('array', this.resetSubject, (state, itemsToAdd) =>
96+
addItemImmutable([], itemsToAdd ?? 0)
97+
);
98+
9699
this.resetAll();
97100
}
98101

@@ -105,9 +108,9 @@ export class ArrayProviderService extends RxState<ProvidedValues> {
105108
}
106109

107110
shuffleAttack(): void {
108-
from([0, 1, 2]).subscribe(v => {
111+
from([0, 1, 2]).subscribe((v) => {
109112
this.shuffleItemsImmutable();
110-
})
113+
});
111114
}
112115

113116
shuffleItemsImmutable(): void {
@@ -146,7 +149,7 @@ export class ArrayProviderService extends RxState<ProvidedValues> {
146149
this.completeSubject.next(undefined);
147150
}
148151

149-
reset(): void {
150-
this.resetSubject.next(undefined);
152+
reset(num?: number): void {
153+
this.resetSubject.next(num);
151154
}
152155
}
Lines changed: 114 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,121 @@
1-
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
1+
import {
2+
ChangeDetectionStrategy,
3+
ChangeDetectorRef,
4+
Component,
5+
Input,
6+
OnInit,
7+
} from '@angular/core';
28
import { ArrayProviderService } from '../array-provider.service';
39

410
@Component({
511
selector: 'rxa-array-provider',
612
exportAs: 'rxaArrayProvider',
7-
template: `
8-
<ng-container *ngIf="buttons">
9-
<div class="row">
10-
<div class="col">
11-
<p>Immutable Operations</p>
12-
<button mat-raised-button [unpatch]="unpatched" (click)="addItemsImmutable(1)">
13-
Add
14-
</button>
15-
<button mat-raised-button [unpatch]="unpatched" (click)="moveItemsImmutable(1)">
16-
Move
17-
</button>
18-
<button mat-raised-button [unpatch]="unpatched" (click)="updateItemsImmutable(1)">
19-
Update
20-
</button>
21-
<button mat-raised-button [unpatch]="unpatched" (click)="removeItemsImmutable(1)">
22-
Remove
23-
</button>
24-
<br/>
25-
<button mat-raised-button [unpatch]="unpatched" (click)="addItemsImmutable(numberOfItems)">
26-
Add Many
27-
</button>
28-
<button mat-raised-button [unpatch]="unpatched" (click)="moveItemsImmutable(numberOfItems/2)">
29-
Move Many
30-
</button>
31-
<button mat-raised-button [unpatch]="unpatched" (click)="shuffleItemsImmutable()">
32-
Shuffle
33-
</button>
34-
<button mat-raised-button [unpatch]="unpatched" (click)="shuffleAttack()">
35-
Shuffle Attack
36-
</button>
37-
<button mat-raised-button [unpatch]="unpatched" (click)="updateItemsImmutable(numberOfItems/2)">
38-
Update Many
39-
</button>
40-
<button mat-raised-button [unpatch]="unpatched" (click)="removeItemsImmutable(numberOfItems/2)">
41-
Remove Many
42-
</button>
13+
template: ` <div *ngIf="buttons">
14+
<p>Immutable Operations</p>
15+
<div class="d-flex align-items-center">
16+
<button
17+
mat-raised-button
18+
[unpatch]="unpatched"
19+
(click)="addItemsImmutable(1)"
20+
>
21+
Add
22+
</button>
23+
<button
24+
mat-raised-button
25+
[unpatch]="unpatched"
26+
(click)="moveItemsImmutable(1)"
27+
>
28+
Move
29+
</button>
30+
<button
31+
mat-raised-button
32+
[unpatch]="unpatched"
33+
(click)="updateItemsImmutable(1)"
34+
>
35+
Update
36+
</button>
37+
<button
38+
mat-raised-button
39+
[unpatch]="unpatched"
40+
(click)="removeItemsImmutable(1)"
41+
>
42+
Remove
43+
</button>
44+
<div class="d-flex align-items-center">
4345
<mat-form-field>
4446
<mat-label>Number of items</mat-label>
45-
<input matInput [(ngModel)]="numberOfItems" type="number">
47+
<input matInput value="10" #resetInput type="number" />
4648
</mat-form-field>
49+
<button
50+
mat-raised-button
51+
[unpatch]="unpatched"
52+
(click)="reset(resetInput.valueAsNumber)"
53+
>
54+
Reset
55+
</button>
4756
</div>
4857
</div>
49-
</ng-container>
58+
<div class="d-flex align-items-center">
59+
<button
60+
mat-raised-button
61+
[unpatch]="unpatched"
62+
(click)="addItemsImmutable(numberOfItems)"
63+
>
64+
Add Many
65+
</button>
66+
<button
67+
mat-raised-button
68+
[unpatch]="unpatched"
69+
(click)="moveItemsImmutable(numberOfItems / 2)"
70+
>
71+
Move Many
72+
</button>
73+
<button
74+
mat-raised-button
75+
[unpatch]="unpatched"
76+
(click)="shuffleItemsImmutable()"
77+
>
78+
Shuffle
79+
</button>
80+
<button
81+
mat-raised-button
82+
[unpatch]="unpatched"
83+
(click)="shuffleAttack()"
84+
>
85+
Shuffle Attack
86+
</button>
87+
<button
88+
mat-raised-button
89+
[unpatch]="unpatched"
90+
(click)="updateItemsImmutable(numberOfItems / 2)"
91+
>
92+
Update Many
93+
</button>
94+
<button
95+
mat-raised-button
96+
[unpatch]="unpatched"
97+
(click)="removeItemsImmutable(numberOfItems / 2)"
98+
>
99+
Remove Many
100+
</button>
101+
<mat-form-field>
102+
<mat-label>Number of items</mat-label>
103+
<input matInput [(ngModel)]="numberOfItems" type="number" />
104+
</mat-form-field>
105+
</div>
106+
</div>
50107
<ng-content></ng-content>`,
51-
changeDetection: ChangeDetectionStrategy.OnPush
108+
changeDetection: ChangeDetectionStrategy.OnPush,
52109
})
53-
export class ArrayProviderComponent extends ArrayProviderService {
54-
numberOfItems = 10
110+
export class ArrayProviderComponent
111+
extends ArrayProviderService
112+
implements OnInit
113+
{
114+
@Input()
115+
initialNumberOfItems = 0;
116+
117+
@Input()
118+
numberOfItems = 10;
55119

56120
@Input()
57121
buttons = false;
@@ -67,4 +131,12 @@ export class ArrayProviderComponent extends ArrayProviderService {
67131
constructor(protected cdRef: ChangeDetectorRef) {
68132
super(cdRef);
69133
}
134+
135+
ngOnInit() {
136+
if (this.initialNumberOfItems) {
137+
Promise.resolve().then(() =>
138+
this.addItemsImmutable(this.initialNumberOfItems)
139+
);
140+
}
141+
}
70142
}

apps/docs/docs/template/api/virtual-scrolling.mdx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,28 @@ export class AnyComponent {
197197

198198
> 💡 See examples for other scroll strategies [here](#rxvirtualscrollstrategy)
199199
200+
### appendOnly mode
201+
202+
Append items to the list as the user scrolls without removing rendered views. The appendOnly input ensures views that are already rendered persist in the DOM after they scroll out of view.
203+
204+
This might be useful when integrating with the [`@angular/cdk/drag-drop`](https://material.angular.io/cdk/drag-drop/overview) package.
205+
206+
```html
207+
<rx-virtual-scroll-viewport [itemSize]="50" appendOnly>
208+
<div class="hero" *rxVirtualFor="let hero of heroes; trackBy: 'id'">
209+
<div>
210+
<div><strong>{{ hero.name }}</strong></div>
211+
<div>{{ hero.id }}</div>
212+
<div>{{ hero.description }}</div>
213+
</div>
214+
</div>
215+
</rx-virtual-scroll-viewport>
216+
```
217+
218+
> 💡 the [appendOnly] input reacts to changes, you can toggle it on runtime
219+
220+
> 💡 see the [angular/cdk implementation](https://material.angular.io/cdk/scrolling/overview#append-only-mode)
221+
200222
### Using `trackBy` shortcut to reduce boilerplate
201223

202224
The `trackBy` input either takes a `keyof T` or the regular `TrackByFunction` (`(index: number, item: T) => any`) as a value.
@@ -487,6 +509,12 @@ Its main purpose is to implement the `RxVirtualScrollViewport` interface as well
487509
height in order to give the provided `RxVirtualScrollStrategy` room to position items. Furthermore, it will gather and forward
488510
all events to the consumer of `rxVirtualFor`.
489511

512+
#### Inputs
513+
514+
| Output | Type | description |
515+
| -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
516+
| `initialScrollIndex` | `number` | Sets the first view to be visible to the user. The viewport waits for the data to arrive and scrolls to the given index immediately. |
517+
490518
#### Outputs
491519

492520
| Output | Type | description |

0 commit comments

Comments
 (0)