Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ng-zone optimization #340

Merged
merged 5 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions demo/app/samples/test.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<input [(ngModel)]="sizeIndex" style="width: 50px">
<button (click)="doChangeSize()">Size</button>
<br>
<small>ngx-ui-scroll version: {{datasource.adapter.version}}</small>
<small>ngx-ui-scroll version: {{datasource.adapter.packageInfo.consumer.version}}</small>
<br>
<small>isLoading:
{{datasource.adapter.isLoading}}
Expand All @@ -47,14 +47,9 @@
</div>
<br><br>
</div>

<div class="viewport" #viewport style="height: 600px" id="my-viewport">
<div *ngIf="show">
<div
*uiScroll="let item of datasource"
(click)="doToggleItem(item)"
[style.height]="item.size + 'px'"
>
<div *uiScroll="let item of datasource" (click)="doToggleItem(item)" [style.height]="item.size + 'px'">
<app-samples-test-inner>
{{item.text}}
<span [style.color]="item.color">
Expand All @@ -63,4 +58,4 @@
</app-samples-test-inner>
</div>
</div>
</div>
</div>
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"ngx-bootstrap": "^8.0.0-RC.5",
"rxjs": "~7.5.1",
"tslib": "^2.3.1",
"vscroll": "^1.5.4",
"vscroll": "^1.5.5",
"zone.js": "~0.11.4"
},
"devDependencies": {
Expand All @@ -56,4 +56,4 @@
"puppeteer": "^13.0.1",
"typescript": "~4.5.4"
}
}
}
7 changes: 2 additions & 5 deletions scroller/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
{
"name": "ngx-ui-scroll",
"version": "3.0.2",
"version": "3.1.0",
"license": "MIT",
"peerDependencies": {
"@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0",
"vscroll": "^1.5.4"
},
"dependencies": {
"tslib": "^2.3.1"
"vscroll": "^1.5.5"
}
}
50 changes: 34 additions & 16 deletions scroller/src/ui-scroll.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
Component, OnInit, OnDestroy,
TemplateRef, ElementRef,
ChangeDetectionStrategy, ChangeDetectorRef
ChangeDetectionStrategy, ChangeDetectorRef, NgZone
} from '@angular/core';

import { IDatasource, Workflow, Item } from './vscroll';
Expand Down Expand Up @@ -43,25 +43,43 @@ export class UiScrollComponent<Data = unknown> implements OnInit, OnDestroy {
public workflow!: Workflow<Data>;

constructor(
public changeDetector: ChangeDetectorRef,
public elementRef: ElementRef) { }
private changeDetector: ChangeDetectorRef,
private elementRef: ElementRef,
private ngZone: NgZone
) {}

ngOnInit(): void {
this.workflow = new Workflow<Data>({
consumer,
element: this.elementRef.nativeElement,
datasource: this.datasource as IDatasource<Data>,
run: (items: Item<Data>[]) => {
if (!items.length && !this.items.length) {
return;
}
this.items = items;
this.changeDetector.detectChanges();
}
});
// The workflow should be created outside of the Angular zone because it's causing many
// change detection cycles. It runs its `init()` function in a `setTimeout` task, which
// then sets up the `scroll` listener. The `scroll` event listener would force Angular to
// run `tick()` any time the `scroll` task is invoked. We don't care about `scroll` events
// since they're handled internally by `vscroll`. We still run change detection manually
// tho when the `run` function is invoked.
// `scroll` events may be also unpatched through zone.js flags:
// `(window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll']`.
// Having `runOutsideAngular` guarantees we're responsible for running change detection
// only when it's "required" (when `items` are updated and the template should be updated too).
this.workflow = this.ngZone.runOutsideAngular(
() =>
new Workflow<Data>({
consumer,
element: this.elementRef.nativeElement,
datasource: this.datasource as IDatasource<Data>,
run: (items: Item<Data>[]) => {
if (!items.length && !this.items.length) {
return;
}
// Re-enter the Angular zone only when items are set and when we have to run the local change detection.
this.ngZone.run(() => {
this.items = items;
this.changeDetector.detectChanges();
});
},
})
);
}

ngOnDestroy(): void {
this.workflow && this.workflow.dispose();
this.workflow?.dispose();
}
}
19 changes: 10 additions & 9 deletions scroller/src/ui-scroll.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Directive, Input, TemplateRef, ViewContainerRef, ComponentFactoryResolver, OnInit } from '@angular/core';
import {
Directive,
Input,
TemplateRef,
ViewContainerRef,
OnInit,
} from '@angular/core';

import { UiScrollComponent } from './ui-scroll.component';
import { IDatasource } from './ui-scroll.datasource';
Expand All @@ -9,20 +15,15 @@ export class UiScrollDirective<ItemData = unknown> implements OnInit {

constructor(
private templateRef: TemplateRef<unknown>,
private viewContainer: ViewContainerRef,
private resolver: ComponentFactoryResolver
) {
}
private viewContainer: ViewContainerRef
) {}

@Input() set uiScrollOf(datasource: IDatasource<ItemData>) {
this.datasource = datasource;
}

ngOnInit(): void {
const compFactory = this.resolver.resolveComponentFactory(UiScrollComponent);
const componentRef = this.viewContainer.createComponent(
compFactory, void 0, this.viewContainer.injector
);
const componentRef = this.viewContainer.createComponent(UiScrollComponent);
componentRef.instance.datasource = this.datasource as IDatasource;
componentRef.instance.template = this.templateRef;
}
Expand Down
1 change: 0 additions & 1 deletion scroller/src/ui-scroll.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ import { UiScrollDirective } from './ui-scroll.directive';
imports: [CommonModule],
entryComponents: [UiScrollComponent],
exports: [UiScrollDirective],
providers: []
})
export class UiScrollModule { }
2 changes: 1 addition & 1 deletion scroller/src/ui-scroll.version.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default {
name: 'ngx-ui-scroll',
version: '3.0.2'
version: '3.1.0'
};
17 changes: 12 additions & 5 deletions tests/recreation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,25 @@ describe('Recreation Spec', () => {
adapterId = misc.adapter.id;
});

const init = (flag: boolean): Promise<boolean> =>
firstValueFrom(
misc.testComponent.datasource.adapter.init$.pipe(
filter(v => flag ? v : !v), take(1)
));
const init = (flag: boolean): boolean | Promise<boolean> =>
(flag !== misc.testComponent.datasource.adapter.init) ?
firstValueFrom(
misc.testComponent.datasource.adapter.init$.pipe(
filter(v => flag ? v : !v), take(1)
))
: Promise.resolve(flag);

const ngIfReload = async (onBeforeHide?: () => void): Promise<void> => {
await init(true);
await misc.adapter.relax();

// hide test component
onBeforeHide && onBeforeHide();
misc.testComponent.show = false;
misc.fixture.changeDetectorRef.detectChanges();
await init(false);

// show test component
misc.testComponent.show = true;
misc.fixture.changeDetectorRef.detectChanges();
await init(true);
Expand Down
Loading