Skip to content

Commit

Permalink
Closes #3344, #3345
Browse files Browse the repository at this point in the history
BREAKING CHANGES:

1. The context of `LetDirective` is strongly typed when `null` or
`undefined` is passed as input.

BEFORE:

```html
<p *ngrxLet="null as n">{{ n }}</p>
<p *ngrxLet="undefined as u">{{ u }}</p>
```

- The type of `n` is `any`.
- The type of `u` is `any`.

AFTER:

```html
<p *ngrxLet="null as n">{{ n }}</p>
<p *ngrxLet="undefined as u">{{ u }}</p>
```

- The type of `n` is `null`.
- The type of `u` is `undefined`.

---

2. Arrays, iterables, generator functions, and readable streams are
not treated as observable-like inputs anymore. To keep the same behavior
as in v13, convert the array/iterable/generator function/readable stream
to observable using the `from` function from the `rxjs` package
before passing it to the `LetDirective`/`PushPipe`.

BEFORE:

```ts
@component({
  template: `
    <p *ngrxLet="numbers as n">{{ n }}</p>
    <p>{{ numbers | ngrxPush }}</p>
  `,
})
export class NumbersComponent {
  numbers = [1, 2, 3];
}
```

AFTER:

```ts
@component({
  template: `
    <p *ngrxLet="numbers$ as n">{{ n }}</p>
    <p>{{ numbers$ | ngrxPush }}</p>
  `,
})
export class NumbersComponent {
  numbers$ = from([1, 2, 3]);
}
```
  • Loading branch information
markostanimirovic authored May 28, 2022
1 parent 5188b68 commit 70056a8
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 102 deletions.
26 changes: 13 additions & 13 deletions modules/component/spec/core/potential-observable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,30 @@ describe('fromPotentialObservable', () => {
};
}

function testNullishInput(input: null | undefined): void {
it(`should create observable from ${input}`, () => {
function testNonObservableInput(input: any, label = input): void {
it(`should create observable from ${label}`, () => {
const { testScheduler } = setup();

testScheduler.run(({ expectObservable }) => {
const obs$ = fromPotentialObservable(input);
expectObservable(obs$).toBe('(x|)', { x: input });
expectObservable(obs$).toBe('x', { x: input });
});
});
}

testNullishInput(null);
testNonObservableInput(null);

testNullishInput(undefined);
testNonObservableInput(undefined);

it('should create observable from array', () => {
const { testScheduler } = setup();
const array = [1, 2, 3];
testNonObservableInput(100, 'number');

testScheduler.run(({ expectObservable }) => {
const obs$ = fromPotentialObservable(array);
expectObservable(obs$).toBe('(xyz|)', { x: 1, y: 2, z: 3 });
});
});
testNonObservableInput('ngrx', 'string');

testNonObservableInput(true, 'boolean');

testNonObservableInput({ ngrx: 'component' }, 'object');

testNonObservableInput([1, 2, 3], 'array');

it('should create observable from promise', (done) => {
const promise = Promise.resolve(100);
Expand Down
7 changes: 7 additions & 0 deletions modules/component/spec/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function stripSpaces(str: string): string {
return str.replace(/[\n\r\s]+/g, '');
}

export function wrapWithSpace(str: string): string {
return ' ' + str + ' ';
}
80 changes: 47 additions & 33 deletions modules/component/spec/let/let.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import {
EMPTY,
interval,
NEVER,
Observable,
ObservableInput,
of,
switchMap,
take,
Expand All @@ -30,6 +28,7 @@ import {
} from 'rxjs';
import { LetDirective } from '../../src/let/let.directive';
import { MockChangeDetectorRef, MockErrorHandler } from '../fixtures/fixtures';
import { stripSpaces } from '../helpers';

@Component({
template: `
Expand All @@ -42,7 +41,7 @@ import { MockChangeDetectorRef, MockErrorHandler } from '../fixtures/fixtures';
`,
})
class LetDirectiveTestComponent {
value$!: Observable<number>;
value$: unknown;
}

@Component({
Expand Down Expand Up @@ -116,7 +115,7 @@ class LetDirectiveTestRecursionComponent {

let fixtureLetDirectiveTestComponent: ComponentFixture<LetDirectiveTestComponent>;
let letDirectiveTestComponent: {
value$: ObservableInput<any> | undefined | null;
value$: unknown;
};
let componentNativeElement: any;

Expand Down Expand Up @@ -257,6 +256,38 @@ describe('LetDirective', () => {
expect(componentNativeElement.textContent).toBe('null');
});

it('should render initially passed number', () => {
letDirectiveTestComponent.value$ = 10;
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('10');
});

it('should render initially passed string', () => {
letDirectiveTestComponent.value$ = 'ngrx';
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('"ngrx"');
});

it('should render initially passed boolean', () => {
letDirectiveTestComponent.value$ = true;
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('true');
});

it('should render initially passed object', () => {
letDirectiveTestComponent.value$ = { ngrx: 'component' };
fixtureLetDirectiveTestComponent.detectChanges();
expect(stripSpaces(componentNativeElement.textContent)).toBe(
'{"ngrx":"component"}'
);
});

it('should render initially passed array', () => {
letDirectiveTestComponent.value$ = [1, 2, 3];
fixtureLetDirectiveTestComponent.detectChanges();
expect(stripSpaces(componentNativeElement.textContent)).toBe('[1,2,3]');
});

it('should render undefined as value when initially of(undefined) was passed (as undefined was emitted)', () => {
letDirectiveTestComponent.value$ = of(undefined);
fixtureLetDirectiveTestComponent.detectChanges();
Expand Down Expand Up @@ -304,7 +335,7 @@ describe('LetDirective', () => {
expect(componentNativeElement.textContent).toBe('undefined');
});

it('should render new value as value when a new observable was passed', () => {
it('should render new value when a new observable was passed', () => {
letDirectiveTestComponent.value$ = of(42);
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('42');
Expand Down Expand Up @@ -339,40 +370,23 @@ describe('LetDirective', () => {
expect(componentNativeElement.textContent).toBe('2');
}));

it('should render new value as value when a new observable was passed', () => {
letDirectiveTestComponent.value$ = of(42);
it('should render non-observable value when it was passed after observable', () => {
letDirectiveTestComponent.value$ = of(100);
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('42');
letDirectiveTestComponent.value$ = of(45);
expect(componentNativeElement.textContent).toBe('100');
letDirectiveTestComponent.value$ = 200;
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('45');
expect(componentNativeElement.textContent).toBe('200');
});

it('should render the last value when a new observable was passed', () => {
letDirectiveTestComponent.value$ = of(42, 45);
it('should render non-observable value when it was passed after another non-observable', () => {
letDirectiveTestComponent.value$ = 'ngrx';
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('45');
});

it('should render values over time when a new observable was passed', fakeAsync(() => {
letDirectiveTestComponent.value$ = interval(1000).pipe(take(3));
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('');
tick(1000);
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('0');
tick(1000);
expect(componentNativeElement.textContent).toBe('"ngrx"');
letDirectiveTestComponent.value$ = 'component';
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('1');
tick(1000);
fixtureLetDirectiveTestComponent.detectChanges();
expect(componentNativeElement.textContent).toBe('2');

tick(1000);
fixtureLetDirectiveTestComponent.detectChanges();
// Remains at 2, since that was the last value.
expect(componentNativeElement.textContent).toBe('2');
}));
expect(componentNativeElement.textContent).toBe('"component"');
});
});

describe('when error', () => {
Expand Down
Loading

0 comments on commit 70056a8

Please sign in to comment.