refactor(common): Remove ngOnChanges from NgForOf (#23378)

`NgForOf` used to implement `OnChanges` and than use
`ngOnChanges` callback to detect when `ngForOf` binding
changed to update the differ. We now do the checking
manually which puts less pressure on the runtime to do
the bookkeeping and should result in minor perf improvement.

PR Close #23378
This commit is contained in:
Miško Hevery 2018-04-13 17:11:21 -07:00 committed by Victor Berchet
parent 255463ed48
commit 08a18b82de
5 changed files with 16 additions and 28 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, OnChanges, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core'; import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core';
export class NgForOfContext<T> { export class NgForOfContext<T> {
constructor( constructor(
@ -93,8 +93,12 @@ export class NgForOfContext<T> {
* *
*/ */
@Directive({selector: '[ngFor][ngForOf]'}) @Directive({selector: '[ngFor][ngForOf]'})
export class NgForOf<T> implements DoCheck, OnChanges { export class NgForOf<T> implements DoCheck {
@Input() ngForOf: NgIterable<T>; @Input()
set ngForOf(ngForOf: NgIterable<T>) {
this._ngForOf = ngForOf;
this._ngForOfDirty = true;
}
@Input() @Input()
set ngForTrackBy(fn: TrackByFunction<T>) { set ngForTrackBy(fn: TrackByFunction<T>) {
if (isDevMode() && fn != null && typeof fn !== 'function') { if (isDevMode() && fn != null && typeof fn !== 'function') {
@ -110,6 +114,8 @@ export class NgForOf<T> implements DoCheck, OnChanges {
get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; } get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; }
private _ngForOf: NgIterable<T>;
private _ngForOfDirty: boolean = true;
private _differ: IterableDiffer<T>|null = null; private _differ: IterableDiffer<T>|null = null;
private _trackByFn: TrackByFunction<T>; private _trackByFn: TrackByFunction<T>;
@ -127,10 +133,11 @@ export class NgForOf<T> implements DoCheck, OnChanges {
} }
} }
ngOnChanges(changes: SimpleChanges): void { ngDoCheck(): void {
if ('ngForOf' in changes) { if (this._ngForOfDirty) {
this._ngForOfDirty = false;
// React on ngForOf changes only once all inputs have been initialized // React on ngForOf changes only once all inputs have been initialized
const value = changes['ngForOf'].currentValue; const value = this._ngForOf;
if (!this._differ && value) { if (!this._differ && value) {
try { try {
this._differ = this._differs.find(value).create(this.ngForTrackBy); this._differ = this._differs.find(value).create(this.ngForTrackBy);
@ -140,11 +147,8 @@ export class NgForOf<T> implements DoCheck, OnChanges {
} }
} }
} }
}
ngDoCheck(): void {
if (this._differ) { if (this._differ) {
const changes = this._differ.diff(this.ngForOf); const changes = this._differ.diff(this._ngForOf);
if (changes) this._applyChanges(changes); if (changes) this._applyChanges(changes);
} }
} }
@ -155,7 +159,7 @@ export class NgForOf<T> implements DoCheck, OnChanges {
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => { (item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
if (item.previousIndex == null) { if (item.previousIndex == null) {
const view = this._viewContainer.createEmbeddedView( const view = this._viewContainer.createEmbeddedView(
this._template, new NgForOfContext<T>(null !, this.ngForOf, -1, -1), currentIndex); this._template, new NgForOfContext<T>(null !, this._ngForOf, -1, -1), currentIndex);
const tuple = new RecordViewTuple<T>(item, view); const tuple = new RecordViewTuple<T>(item, view);
insertTuples.push(tuple); insertTuples.push(tuple);
} else if (currentIndex == null) { } else if (currentIndex == null) {

View File

@ -59,27 +59,18 @@
{ {
"name": "NgIfContext" "name": "NgIfContext"
}, },
{
"name": "NgOnChangesFeature"
},
{ {
"name": "Optional" "name": "Optional"
}, },
{ {
"name": "PARAMETERS" "name": "PARAMETERS"
}, },
{
"name": "PRIVATE_PREFIX"
},
{ {
"name": "ROOT_DIRECTIVE_INDICES" "name": "ROOT_DIRECTIVE_INDICES"
}, },
{ {
"name": "RecordViewTuple" "name": "RecordViewTuple"
}, },
{
"name": "SimpleChange"
},
{ {
"name": "SkipSelf" "name": "SkipSelf"
}, },

View File

@ -160,11 +160,6 @@ if (!(NgIf as any).ngDirectiveDef) {
selectors: [['', 'ngFor', '', 'ngForOf', '']], selectors: [['', 'ngFor', '', 'ngForOf', '']],
factory: () => new NgForOf( factory: () => new NgForOf(
injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)), injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)),
features: [NgOnChangesFeature({
ngForOf: 'ngForOf',
ngForTrackBy: 'ngForTrackBy',
ngForTemplate: 'ngForTemplate',
})],
inputs: { inputs: {
ngForOf: 'ngForOf', ngForOf: 'ngForOf',
ngForTrackBy: 'ngForTrackBy', ngForTrackBy: 'ngForTrackBy',

View File

@ -20,7 +20,6 @@ NgForOf.ngDirectiveDef = defineDirective({
selectors: [['', 'ngForOf', '']], selectors: [['', 'ngForOf', '']],
factory: () => new NgForOfDef( factory: () => new NgForOfDef(
injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)), injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)),
features: [NgOnChangesFeature()],
inputs: { inputs: {
ngForOf: 'ngForOf', ngForOf: 'ngForOf',
ngForTrackBy: 'ngForTrackBy', ngForTrackBy: 'ngForTrackBy',

View File

@ -240,13 +240,12 @@ export declare class NgComponentOutlet implements OnChanges, OnDestroy {
ngOnDestroy(): void; ngOnDestroy(): void;
} }
export declare class NgForOf<T> implements DoCheck, OnChanges { export declare class NgForOf<T> implements DoCheck {
ngForOf: NgIterable<T>; ngForOf: NgIterable<T>;
ngForTemplate: TemplateRef<NgForOfContext<T>>; ngForTemplate: TemplateRef<NgForOfContext<T>>;
ngForTrackBy: TrackByFunction<T>; ngForTrackBy: TrackByFunction<T>;
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T>>, _differs: IterableDiffers); constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T>>, _differs: IterableDiffers);
ngDoCheck(): void; ngDoCheck(): void;
ngOnChanges(changes: SimpleChanges): void;
} }
export declare class NgForOfContext<T> { export declare class NgForOfContext<T> {