fix(common): throw an error if trackBy is not a function (#13420)

* fix(common): throw an error if trackBy is not a function

Closes #13388

* refactor(platform-browser): disable no-console rule in DomAdapter
This commit is contained in:
Dzmitry Shylovich 2016-12-21 03:18:24 +03:00 committed by Chuck Jazdzewski
parent 383adc9ad9
commit fcd116fdc0
3 changed files with 22 additions and 4 deletions

View File

@ -89,9 +89,18 @@ export class NgForRow {
@Directive({selector: '[ngFor][ngForOf]'}) @Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges { export class NgFor implements DoCheck, OnChanges {
@Input() ngForOf: any; @Input() ngForOf: any;
@Input() ngForTrackBy: TrackByFn; @Input()
set ngForTrackBy(fn: TrackByFn) {
if (typeof fn !== 'function') {
throw new Error(`trackBy must be a function, but received ${JSON.stringify(fn)}`);
}
this._trackByFn = fn;
}
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
private _differ: IterableDiffer = null; private _differ: IterableDiffer = null;
private _trackByFn: TrackByFn;
constructor( constructor(
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>, private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
@ -119,7 +128,7 @@ export class NgFor implements DoCheck, OnChanges {
} }
} }
ngDoCheck() { 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);

View File

@ -294,6 +294,16 @@ export function main() {
})); }));
describe('track by', () => { describe('track by', () => {
it('should throw if trackBy is not a function', async(() => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="item?.id"></template>`;
fixture = createTestComponent(template);
getComponent().items = [{id: 1}, {id: 2}];
expect(() => fixture.detectChanges())
.toThrowError(/trackBy must be a function, but received null/);
}));
it('should set the context to the component instance', async(() => { it('should set the context to the component instance', async(() => {
const template = const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`; `<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;

View File

@ -67,7 +67,7 @@ const _chromeNumKeyPadMap = {
* @security Tread carefully! Interacting with the DOM directly is dangerous and * @security Tread carefully! Interacting with the DOM directly is dangerous and
* can introduce XSS risks. * can introduce XSS risks.
*/ */
/* tslint:disable:requireParameterType */ /* tslint:disable:requireParameterType no-console */
export class BrowserDomAdapter extends GenericBrowserDomAdapter { export class BrowserDomAdapter extends GenericBrowserDomAdapter {
parse(templateHtml: string) { throw new Error('parse not implemented'); } parse(templateHtml: string) { throw new Error('parse not implemented'); }
static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); } static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); }
@ -90,7 +90,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
log(error: string): void { log(error: string): void {
if (window.console) { if (window.console) {
// tslint:disable-next-line:no-console
window.console.log && window.console.log(error); window.console.log && window.console.log(error);
} }
} }