fix(Common): allow null/undefined values for `NgForTrackBy`
Reverts a breaking change introduced in 2.4.1 by #13420 fixes #13641
This commit is contained in:
parent
f822f9599c
commit
f88cd2f22e
|
@ -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, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {getTypeNameForDebugging} from '../facade/lang';
|
import {getTypeNameForDebugging} from '../facade/lang';
|
||||||
|
|
||||||
|
@ -91,9 +91,13 @@ export class NgFor implements DoCheck, OnChanges {
|
||||||
@Input() ngForOf: any;
|
@Input() ngForOf: any;
|
||||||
@Input()
|
@Input()
|
||||||
set ngForTrackBy(fn: TrackByFn) {
|
set ngForTrackBy(fn: TrackByFn) {
|
||||||
if (typeof fn !== 'function') {
|
if (isDevMode() && fn != null && typeof fn !== 'function') {
|
||||||
throw new Error(`trackBy must be a function, but received ${JSON.stringify(fn)}.
|
// TODO(vicb): use a log service once there is a public one available
|
||||||
See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
|
if (<any>console && <any>console.warn) {
|
||||||
|
console.warn(
|
||||||
|
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
|
||||||
|
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._trackByFn = fn;
|
this._trackByFn = fn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,14 +294,25 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('track by', () => {
|
describe('track by', () => {
|
||||||
it('should throw if trackBy is not a function', async(() => {
|
it('should console.warn if trackBy is not a function', async(() => {
|
||||||
|
// TODO(vicb): expect a warning message when we have a proper log service
|
||||||
const template =
|
const template =
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="item?.id"></template>`;
|
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value"></template>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
fixture.componentInstance.value = 0;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
getComponent().items = [{id: 1}, {id: 2}];
|
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
|
||||||
expect(() => fixture.detectChanges())
|
// TODO(vicb): expect no warning message when we have a proper log service
|
||||||
.toThrowError(/trackBy must be a function, but received null/);
|
const template =
|
||||||
|
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value">{{ item }}</template>`;
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
fixture.componentInstance.items = ['a', 'b', 'c'];
|
||||||
|
fixture.componentInstance.value = null;
|
||||||
|
detectChangesAndExpectText('abc');
|
||||||
|
fixture.componentInstance.value = undefined;
|
||||||
|
detectChangesAndExpectText('abc');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should set the context to the component instance', async(() => {
|
it('should set the context to the component instance', async(() => {
|
||||||
|
@ -343,6 +354,7 @@ export function main() {
|
||||||
getComponent().items = [{'id': 'a', 'color': 'red'}];
|
getComponent().items = [{'id': 'a', 'color': 'red'}];
|
||||||
detectChangesAndExpectText('red');
|
detectChangesAndExpectText('red');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should move items around and keep them updated ', async(() => {
|
it('should move items around and keep them updated ', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||||
|
@ -378,6 +390,7 @@ class Foo {
|
||||||
@Component({selector: 'test-cmp', template: ''})
|
@Component({selector: 'test-cmp', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
||||||
|
value: any;
|
||||||
items: any[] = [1, 2];
|
items: any[] = [1, 2];
|
||||||
trackById(index: number, item: any): string { return item['id']; }
|
trackById(index: number, item: any): string { return item['id']; }
|
||||||
trackByIndex(index: number, item: any): number { return index; }
|
trackByIndex(index: number, item: any): number { return index; }
|
||||||
|
|
Loading…
Reference in New Issue