From 693e01e73a7c7dd8f7b2055dd59acf6f2ccfa6eb Mon Sep 17 00:00:00 2001 From: Julian Jandl Date: Tue, 8 Nov 2022 14:50:34 +0100 Subject: [PATCH 1/2] fix(cdk): list-template-manager should always emit latest value --- libs/cdk/template/spec/list-manager.spec.ts | 36 ++++++++++++++----- .../template/src/lib/list-template-manager.ts | 15 +++++--- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/libs/cdk/template/spec/list-manager.spec.ts b/libs/cdk/template/spec/list-manager.spec.ts index f4c2c34728..34b4262061 100644 --- a/libs/cdk/template/spec/list-manager.spec.ts +++ b/libs/cdk/template/spec/list-manager.spec.ts @@ -23,7 +23,7 @@ import { } from '@rx-angular/cdk/template'; import { RxStrategyProvider } from '@rx-angular/cdk/render-strategies'; import { mockConsole } from '@test-helpers'; -import { ReplaySubject } from 'rxjs'; +import { ReplaySubject, Subject } from 'rxjs'; @Component({ selector: 'rx-angular-error-test', @@ -63,6 +63,7 @@ class ListTemplateManagerSpecComponent implements AfterViewInit { values$ = new ReplaySubject(1); latestRenderedValue: any; + latestRenderedValue$ = new Subject(); constructor( private iterableDiffers: IterableDiffers, @@ -97,6 +98,7 @@ class ListTemplateManagerSpecComponent implements AfterViewInit { }); this.listManager.render(this.values$).subscribe((v: any) => { this.latestRenderedValue = v; + this.latestRenderedValue$.next(v); }); } } @@ -124,13 +126,7 @@ const customErrorHandler: ErrorHandler = { }; let fixtureComponent: any; -let componentInstance: { - listManager: RxListManager; - errorHandler: ErrorHandler; - templateRef: TemplateRef; - values$: ReplaySubject; - latestRenderedValue: any; -}; +let componentInstance: ListTemplateManagerSpecComponent; let componentNativeElement: any; const setupListManagerComponent = (): void => { TestBed.configureTestingModule({ @@ -184,6 +180,30 @@ describe('list-manager', () => { expect(componentContent).toEqual('1'); }); + it('should report last rendered value', () => { + fixtureComponent.detectChanges(); + componentInstance.values$.next([1]); + fixtureComponent.detectChanges(); + expect(componentInstance.latestRenderedValue).toEqual([1]); + }); + + it('should report last rendered value without any changes', () => { + let renderedValueUpdate = 0; + let renderedValue; + componentInstance.latestRenderedValue$.subscribe((value) => { + renderedValueUpdate++; + renderedValue = value; + }); + const valuesToRender = [1]; + fixtureComponent.detectChanges(); + componentInstance.values$.next(valuesToRender); + fixtureComponent.detectChanges(); + componentInstance.values$.next(valuesToRender); + fixtureComponent.detectChanges(); + expect(renderedValue).toEqual([1]); + expect(renderedValueUpdate).toBe(2); + }); + describe('exception handling', () => { it('should capture errors with errorHandler', () => { fixtureComponent.detectChanges(); diff --git a/libs/cdk/template/src/lib/list-template-manager.ts b/libs/cdk/template/src/lib/list-template-manager.ts index 8920fe8ae0..3393203108 100644 --- a/libs/cdk/template/src/lib/list-template-manager.ts +++ b/libs/cdk/template/src/lib/list-template-manager.ts @@ -7,7 +7,13 @@ import { TemplateRef, TrackByFunction, } from '@angular/core'; -import { combineLatest, MonoTypeOperatorFunction, Observable, of } from 'rxjs'; +import { + combineLatest, + MonoTypeOperatorFunction, + NEVER, + Observable, + of, +} from 'rxjs'; import { catchError, distinctUntilChanged, @@ -119,8 +125,9 @@ export function createListTemplateManager< let changes: IterableChanges; if (differ) { if (partiallyFinished) { - const currentIterable = []; - for (let i = 0, ilen = viewContainerRef.length; i < ilen; i++) { + const length = viewContainerRef.length; + const currentIterable = new Array(viewContainerRef.length); + for (let i = 0; i < length; i++) { const viewRef = >viewContainerRef.get(i); currentIterable[i] = viewRef.context.$implicit; } @@ -137,7 +144,7 @@ export function createListTemplateManager< // Cancel old renders switchMap(({ changes, iterable, strategy }) => { if (!changes) { - return of([]); + return of(iterable); } const values = iterable || []; // TODO: we might want to treat other iterables in a more performant way than Array.from() From 2ef557216e193852db29d6608da9d9378320f4af Mon Sep 17 00:00:00 2001 From: Julian Jandl Date: Tue, 8 Nov 2022 15:45:38 +0100 Subject: [PATCH 2/2] test(template): fix rxFor strategy spec --- .../for/src/lib/tests/for.directive.strategy.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/template/for/src/lib/tests/for.directive.strategy.spec.ts b/libs/template/for/src/lib/tests/for.directive.strategy.spec.ts index 979aac4d6d..7568c8a6f0 100644 --- a/libs/template/for/src/lib/tests/for.directive.strategy.spec.ts +++ b/libs/template/for/src/lib/tests/for.directive.strategy.spec.ts @@ -1,6 +1,7 @@ import { ErrorHandler } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RxStrategyProvider } from '@rx-angular/cdk/render-strategies'; +import { take } from 'rxjs/operators'; import { ForModule } from '../for.module'; import { createTestComponent, TestComponent } from './fixtures'; @@ -48,7 +49,7 @@ describe('rxFor strategies', () => { ])('Strategy: %p', (strategy) => { it('should render with given strategy', (done) => { component.strategy = strategy; - component.renderedValue$.subscribe((v) => { + component.renderedValue$.pipe(take(1)).subscribe((v) => { expect(v).toEqual([1, 2]); expect(nativeElement.textContent).toBe('1;2;'); done(); @@ -57,7 +58,7 @@ describe('rxFor strategies', () => { }); it('should not affect primary strategy', (done) => { component.strategy = strategy; - component.renderedValue$.subscribe((v) => { + component.renderedValue$.pipe(take(1)).subscribe((v) => { expect(v).toEqual([1, 2]); expect(nativeElement.textContent).toBe('1;2;'); expect(strategyProvider.primaryStrategy).toBe(primaryStrategy);