Skip to content

Commit

Permalink
Merge pull request #340 from dhilt/ng-zone-optimization
Browse files Browse the repository at this point in the history
Ng-zone optimization
  • Loading branch information
dhilt authored Jun 16, 2023
2 parents aca492f + 64944f3 commit 993b270
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 54 deletions.
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

0 comments on commit 993b270

Please sign in to comment.