From d08e3a298083ea2fcca83062ec4c7c82c6264aae Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Tue, 25 May 2021 13:51:10 -0700 Subject: [PATCH] docs(common): rewrite docs for NgForOf#ngForTrackBy (#42329) Clarify the prupose of the tracking function and document how to create a good one. Fixes #40461 PR Close #42329 --- packages/common/src/directives/ng_for_of.ts | 25 +++++----- .../differs/iterable_differs.ts | 46 +++++++++++++++++-- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index c39daa3c82faf..34dc3783cf8eb 100644 --- a/packages/common/src/directives/ng_for_of.ts +++ b/packages/common/src/directives/ng_for_of.ts @@ -141,21 +141,22 @@ export class NgForOf = NgIterable> implements DoCh this._ngForOfDirty = true; } /** - * A function that defines how to track changes for items in the iterable. + * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. * - * When items are added, moved, or removed in the iterable, - * the directive must re-render the appropriate DOM nodes. - * To minimize churn in the DOM, only nodes that have changed - * are re-rendered. + * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object + * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) + * as the key. * - * By default, the change detector assumes that - * the object instance identifies the node in the iterable. - * When this function is supplied, the directive uses - * the result of calling this function to identify the item node, - * rather than the identity of the object itself. + * `NgForOf` uses the computed key to associate items in an iterable with DOM elements + * it produces for these items. * - * The function receives two inputs, - * the iteration index and the associated node data. + * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an + * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a + * primary key), and this iterable could be updated with new object instances that still + * represent the same underlying entity (for example, when data is re-fetched from the server, + * and the iterable is recreated and re-rendered, but most of the data is still the same). + * + * @see `TrackByFunction` */ @Input() set ngForTrackBy(fn: TrackByFunction) { diff --git a/packages/core/src/change_detection/differs/iterable_differs.ts b/packages/core/src/change_detection/differs/iterable_differs.ts index edb6b1e3b0870..e05f94cd56eaa 100644 --- a/packages/core/src/change_detection/differs/iterable_differs.ts +++ b/packages/core/src/change_detection/differs/iterable_differs.ts @@ -113,14 +113,52 @@ export interface IterableChangeRecord { } /** - * An optional function passed into the `NgForOf` directive that defines how to track - * changes for items in an iterable. - * The function takes the iteration index and item ID. - * When supplied, Angular tracks changes by the return value of the function. + * A function optionally passed into the `NgForOf` directive to customize how `NgForOf` uniquely + * identifies items in an iterable. * + * `NgForOf` needs to uniquely identify items in the iterable to correctly perform DOM updates + * when items in the iterable are reordered, new items are added, or existing items are removed. + * + * + * In all of these scenarios it is usually desirable to only update the DOM elements associated + * with the items affected by the change. This behavior is important to: + * + * - preserve any DOM-specific UI state (like cursor position, focus, text selection) when the + * iterable is modified + * - enable animation of item addition, removal, and iterable reordering + * + * A common use for custom `trackBy` functions is when the model that `NgForOf` iterates over + * contains a property with a unique identifier. For example, given a model: + * + * ```ts + * class User { + * id: number; + * name: string; + * ... + * } + * ``` + * a custom `trackBy` function could look like the following: + * ```ts + * function userTrackBy(index, user) { + * return user.id; + * } + * ``` + * + * A custom `trackBy` function must have several properties: + * + * - be [idempotent](https://en.wikipedia.org/wiki/Idempotence) (be without side effects, and always + * return the same value for a given input) + * - return unique value for all unique inputs + * - be fast + * + * @see [`NgForOf#ngForTrackBy`](api/common/NgForOf#ngForTrackBy) * @publicApi */ export interface TrackByFunction { + /** + * @param index The index of the item within the iterable. + * @param item The item in the iterable. + */ (index: number, item: T): any; }