Check for changes made in the data and render each change (node added/removed/moved).
(
data: readonly T[],
dataDiffer: IterableDiffer<T> = this._dataDiffer,
viewContainer: ViewContainerRef = this._nodeOutlet.viewContainer,
parentData?: T,
)
| 511 | |
| 512 | /** Check for changes made in the data and render each change (node added/removed/moved). */ |
| 513 | renderNodeChanges( |
| 514 | data: readonly T[], |
| 515 | dataDiffer: IterableDiffer<T> = this._dataDiffer, |
| 516 | viewContainer: ViewContainerRef = this._nodeOutlet.viewContainer, |
| 517 | parentData?: T, |
| 518 | ) { |
| 519 | const changes = dataDiffer.diff(data); |
| 520 | |
| 521 | // Some tree consumers expect change detection to propagate to nodes |
| 522 | // even when the array itself hasn't changed; we explicitly detect changes |
| 523 | // anyways in order for nodes to update their data. |
| 524 | // |
| 525 | // However, if change detection is called while the component's view is |
| 526 | // still initing, then the order of child views initing will be incorrect; |
| 527 | // to prevent this, we only exit early if the view hasn't initialized yet. |
| 528 | if (!changes && !this._viewInit) { |
| 529 | return; |
| 530 | } |
| 531 | |
| 532 | changes?.forEachOperation( |
| 533 | ( |
| 534 | item: IterableChangeRecord<T>, |
| 535 | adjustedPreviousIndex: number | null, |
| 536 | currentIndex: number | null, |
| 537 | ) => { |
| 538 | if (item.previousIndex == null) { |
| 539 | this.insertNode(data[currentIndex!], currentIndex!, viewContainer, parentData); |
| 540 | } else if (currentIndex == null) { |
| 541 | viewContainer.remove(adjustedPreviousIndex!); |
| 542 | } else { |
| 543 | const view = viewContainer.get(adjustedPreviousIndex!); |
| 544 | viewContainer.move(view!, currentIndex); |
| 545 | } |
| 546 | }, |
| 547 | ); |
| 548 | |
| 549 | // If the data itself changes, but keeps the same trackBy, we need to update the templates' |
| 550 | // context to reflect the new object. |
| 551 | changes?.forEachIdentityChange((record: IterableChangeRecord<T>) => { |
| 552 | const newData = record.item; |
| 553 | if (record.currentIndex != undefined) { |
| 554 | const view = viewContainer.get(record.currentIndex); |
| 555 | (view as EmbeddedViewRef<any>).context.$implicit = newData; |
| 556 | } |
| 557 | }); |
| 558 | |
| 559 | // Note: we only `detectChanges` from a top-level call, otherwise we risk overflowing |
| 560 | // the call stack since this method is called recursively (see #29733.) |
| 561 | // TODO: change to `this._changeDetectorRef.markForCheck()`, |
| 562 | // or just switch this component to use signals. |
| 563 | if (parentData) { |
| 564 | this._changeDetectorRef.markForCheck(); |
| 565 | } else { |
| 566 | this._changeDetectorRef.detectChanges(); |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | /** |
no test coverage detected