Skip to content

Commit 7883d0c

Browse files
committed
feat(cdk): enhance rxIfList with a template to render in falsy case
1 parent d4db443 commit 7883d0c

File tree

1 file changed

+110
-9
lines changed

1 file changed

+110
-9
lines changed

libs/cdk/template/src/lib/rx-if-list/rx-if-list.directive.ts

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,121 @@
1-
import {Directive, Input, NgModule, TemplateRef, ViewContainerRef} from '@angular/core';
1+
import {
2+
Directive,
3+
EmbeddedViewRef,
4+
Input,
5+
NgModule,
6+
TemplateRef,
7+
ViewContainerRef,
8+
ɵstringify as stringify
9+
} from '@angular/core';
210
import {CommonModule} from '@angular/common';
311

412
@Directive({
13+
// eslint-disable-next-line @angular-eslint/directive-selector
514
selector: '[rxIfList]',
615
})
716
export class RxIfListDirective {
8-
@Input() set rxIfList(value: ArrayLike<unknown> | null | undefined) {
9-
this.vcr.clear();
10-
if ((value?.length ?? []) > 0) {
11-
this.vcr.createEmbeddedView(this.tpl);
17+
private _context: RxIfListContext = new RxIfListContext();
18+
private _thenTemplateRef: TemplateRef<RxIfListContext>|null = null;
19+
private _elseTemplateRef: TemplateRef<RxIfListContext>|null = null;
20+
private _thenViewRef: EmbeddedViewRef<RxIfListContext>|null = null;
21+
private _elseViewRef: EmbeddedViewRef<RxIfListContext>|null = null;
22+
/** @internal */
23+
static rxIfListUseIfTypeGuard: void;
24+
25+
/**
26+
* Assert the correct type of the expression bound to the `rxIfList` input within the template.
27+
*
28+
* The presence of this static field is a signal to the Ivy template type check compiler that
29+
* when the `rxIfList` structural directive renders its template, the type of the expression bound
30+
* to `rxIfList` should be narrowed in some way. For `rxIfList`, the binding expression itself is used to
31+
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `rxIfList`.
32+
*/
33+
static ngTemplateGuard_rxIfList: 'binding';
34+
35+
/**
36+
* Asserts the correct type of the context for the template that `rxIfList` will render.
37+
*
38+
* The presence of this method is a signal to the Ivy template type-check compiler that the
39+
* `rxIfList` structural directive renders its template with a specific context type.
40+
*/
41+
static ngTemplateContextGuard<T>(dir: RxIfListDirective, ctx: any):
42+
ctx is RxIfListContext{
43+
return true;
44+
}
45+
46+
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<RxIfListContext>) {
47+
this._thenTemplateRef = templateRef;
48+
}
49+
50+
/**
51+
* The Boolean expression to evaluate as the condition for showing a template.
52+
*/
53+
@Input()
54+
set rxIfList(value: ArrayLike<unknown> | null | undefined) {
55+
this._context.$implicit = this._context.rxIfList = ((value?.length ?? []) > 0);
56+
this._updateView();
57+
}
58+
59+
/**
60+
* A template to show if the condition expression evaluates to true.
61+
*/
62+
@Input()
63+
set rxIfListThen(templateRef: TemplateRef<RxIfListContext>|null) {
64+
assertTemplate('rxIfListThen', templateRef);
65+
this._thenTemplateRef = templateRef;
66+
this._thenViewRef = null; // clear previous view if any.
67+
this._updateView();
68+
}
69+
70+
/**
71+
* A template to show if the condition expression evaluates to false.
72+
*/
73+
@Input()
74+
set rxIfListElse(templateRef: TemplateRef<RxIfListContext>|null) {
75+
assertTemplate('rxIfListElse', templateRef);
76+
this._elseTemplateRef = templateRef;
77+
this._elseViewRef = null; // clear previous view if any.
78+
this._updateView();
79+
}
80+
81+
private _updateView() {
82+
if (this._context.$implicit) {
83+
if (!this._thenViewRef) {
84+
this._viewContainer.clear();
85+
this._elseViewRef = null;
86+
if (this._thenTemplateRef) {
87+
this._thenViewRef =
88+
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
89+
}
90+
}
91+
} else {
92+
if (!this._elseViewRef) {
93+
this._viewContainer.clear();
94+
this._thenViewRef = null;
95+
if (this._elseTemplateRef) {
96+
this._elseViewRef =
97+
this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
98+
}
99+
}
12100
}
13101
}
14-
constructor(
15-
private readonly vcr: ViewContainerRef,
16-
private readonly tpl: TemplateRef<unknown>
17-
) {}
102+
103+
104+
}
105+
106+
/**
107+
* @publicApi
108+
*/
109+
export class RxIfListContext {
110+
$implicit = false;
111+
rxIfList = false;
112+
}
113+
114+
function assertTemplate(property: string, templateRef: TemplateRef<any>|null): void {
115+
const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView);
116+
if (!isTemplateRefOrNull) {
117+
throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`);
118+
}
18119
}
19120

20121
@NgModule({

0 commit comments

Comments
 (0)