Created
December 15, 2020 10:33
-
-
Save takuya-nakayasu/54cd459cadfa6c737be8ebe73fcb76b5 to your computer and use it in GitHub Desktop.
ng_if.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright Google LLC All Rights Reserved. | |
* | |
* Use of this source code is governed by an MIT-style license that can be | |
* found in the LICENSE file at https://angular.io/license | |
*/ | |
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstringify as stringify} from '@angular/core'; | |
/** | |
* A structural directive that conditionally includes a template based on the value of | |
* an expression coerced to Boolean. | |
* When the expression evaluates to true, Angular renders the template | |
* provided in a `then` clause, and when false or null, | |
* Angular renders the template provided in an optional `else` clause. The default | |
* template for the `else` clause is blank. | |
* | |
* A [shorthand form](guide/structural-directives#the-asterisk--prefix) of the directive, | |
* `*ngIf="condition"`, is generally used, provided | |
* as an attribute of the anchor element for the inserted template. | |
* Angular expands this into a more explicit version, in which the anchor element | |
* is contained in an `<ng-template>` element. | |
* | |
* Simple form with shorthand syntax: | |
* | |
* ``` | |
* <div *ngIf="condition">Content to render when condition is true.</div> | |
* ``` | |
* | |
* Simple form with expanded syntax: | |
* | |
* ``` | |
* <ng-template [ngIf]="condition"><div>Content to render when condition is | |
* true.</div></ng-template> | |
* ``` | |
* | |
* Form with an "else" block: | |
* | |
* ``` | |
* <div *ngIf="condition; else elseBlock">Content to render when condition is true.</div> | |
* <ng-template #elseBlock>Content to render when condition is false.</ng-template> | |
* ``` | |
* | |
* Shorthand form with "then" and "else" blocks: | |
* | |
* ``` | |
* <div *ngIf="condition; then thenBlock else elseBlock"></div> | |
* <ng-template #thenBlock>Content to render when condition is true.</ng-template> | |
* <ng-template #elseBlock>Content to render when condition is false.</ng-template> | |
* ``` | |
* | |
* Form with storing the value locally: | |
* | |
* ``` | |
* <div *ngIf="condition as value; else elseBlock">{{value}}</div> | |
* <ng-template #elseBlock>Content to render when value is null.</ng-template> | |
* ``` | |
* | |
* @usageNotes | |
* | |
* The `*ngIf` directive is most commonly used to conditionally show an inline template, | |
* as seen in the following example. | |
* The default `else` template is blank. | |
* | |
* {@example common/ngIf/ts/module.ts region='NgIfSimple'} | |
* | |
* ### Showing an alternative template using `else` | |
* | |
* To display a template when `expression` evaluates to false, use an `else` template | |
* binding as shown in the following example. | |
* The `else` binding points to an `<ng-template>` element labeled `#elseBlock`. | |
* The template can be defined anywhere in the component view, but is typically placed right after | |
* `ngIf` for readability. | |
* | |
* {@example common/ngIf/ts/module.ts region='NgIfElse'} | |
* | |
* ### Using an external `then` template | |
* | |
* In the previous example, the then-clause template is specified inline, as the content of the | |
* tag that contains the `ngIf` directive. You can also specify a template that is defined | |
* externally, by referencing a labeled `<ng-template>` element. When you do this, you can | |
* change which template to use at runtime, as shown in the following example. | |
* | |
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'} | |
* | |
* ### Storing a conditional result in a variable | |
* | |
* You might want to show a set of properties from the same object. If you are waiting | |
* for asynchronous data, the object can be undefined. | |
* In this case, you can use `ngIf` and store the result of the condition in a local | |
* variable as shown in the the following example. | |
* | |
* {@example common/ngIf/ts/module.ts region='NgIfAs'} | |
* | |
* This code uses only one `AsyncPipe`, so only one subscription is created. | |
* The conditional statement stores the result of `userStream|async` in the local variable `user`. | |
* You can then bind the local `user` repeatedly. | |
* | |
* The conditional displays the data only if `userStream` returns a value, | |
* so you don't need to use the | |
* safe-navigation-operator (`?.`) | |
* to guard against null values when accessing properties. | |
* You can display an alternative template while waiting for the data. | |
* | |
* ### Shorthand syntax | |
* | |
* The shorthand syntax `*ngIf` expands into two separate template specifications | |
* for the "then" and "else" clauses. For example, consider the following shorthand statement, | |
* that is meant to show a loading page while waiting for data to be loaded. | |
* | |
* ``` | |
* <div class="hero-list" *ngIf="heroes else loading"> | |
* ... | |
* </div> | |
* | |
* <ng-template #loading> | |
* <div>Loading...</div> | |
* </ng-template> | |
* ``` | |
* | |
* You can see that the "else" clause references the `<ng-template>` | |
* with the `#loading` label, and the template for the "then" clause | |
* is provided as the content of the anchor element. | |
* | |
* However, when Angular expands the shorthand syntax, it creates | |
* another `<ng-template>` tag, with `ngIf` and `ngIfElse` directives. | |
* The anchor element containing the template for the "then" clause becomes | |
* the content of this unlabeled `<ng-template>` tag. | |
* | |
* ``` | |
* <ng-template [ngIf]="heroes" [ngIfElse]="loading"> | |
* <div class="hero-list"> | |
* ... | |
* </div> | |
* </ng-template> | |
* | |
* <ng-template #loading> | |
* <div>Loading...</div> | |
* </ng-template> | |
* ``` | |
* | |
* The presence of the implicit template object has implications for the nesting of | |
* structural directives. For more on this subject, see | |
* [Structural Directives](https://angular.io/guide/structural-directives#one-per-element). | |
* | |
* @ngModule CommonModule | |
* @publicApi | |
*/ | |
@Directive({selector: '[ngIf]'}) | |
export class NgIf<T = unknown> { | |
private _context: NgIfContext<T> = new NgIfContext<T>(); | |
private _thenTemplateRef: TemplateRef<NgIfContext<T>>|null = null; | |
private _elseTemplateRef: TemplateRef<NgIfContext<T>>|null = null; | |
private _thenViewRef: EmbeddedViewRef<NgIfContext<T>>|null = null; | |
private _elseViewRef: EmbeddedViewRef<NgIfContext<T>>|null = null; | |
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>) { | |
this._thenTemplateRef = templateRef; | |
} | |
/** | |
* The Boolean expression to evaluate as the condition for showing a template. | |
*/ | |
@Input() | |
set ngIf(condition: T) { | |
this._context.$implicit = this._context.ngIf = condition; | |
this._updateView(); | |
} | |
/** | |
* A template to show if the condition expression evaluates to true. | |
*/ | |
@Input() | |
set ngIfThen(templateRef: TemplateRef<NgIfContext<T>>|null) { | |
assertTemplate('ngIfThen', templateRef); | |
this._thenTemplateRef = templateRef; | |
this._thenViewRef = null; // clear previous view if any. | |
this._updateView(); | |
} | |
/** | |
* A template to show if the condition expression evaluates to false. | |
*/ | |
@Input() | |
set ngIfElse(templateRef: TemplateRef<NgIfContext<T>>|null) { | |
assertTemplate('ngIfElse', templateRef); | |
this._elseTemplateRef = templateRef; | |
this._elseViewRef = null; // clear previous view if any. | |
this._updateView(); | |
} | |
private _updateView() { | |
if (this._context.$implicit) { | |
if (!this._thenViewRef) { | |
this._viewContainer.clear(); | |
this._elseViewRef = null; | |
if (this._thenTemplateRef) { | |
this._thenViewRef = | |
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context); | |
} | |
} | |
} else { | |
if (!this._elseViewRef) { | |
this._viewContainer.clear(); | |
this._thenViewRef = null; | |
if (this._elseTemplateRef) { | |
this._elseViewRef = | |
this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); | |
} | |
} | |
} | |
} | |
/** @internal */ | |
public static ngIfUseIfTypeGuard: void; | |
/** | |
* Assert the correct type of the expression bound to the `ngIf` input within the template. | |
* | |
* The presence of this static field is a signal to the Ivy template type check compiler that | |
* when the `NgIf` structural directive renders its template, the type of the expression bound | |
* to `ngIf` should be narrowed in some way. For `NgIf`, the binding expression itself is used to | |
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`. | |
*/ | |
static ngTemplateGuard_ngIf: 'binding'; | |
/** | |
* Asserts the correct type of the context for the template that `NgIf` will render. | |
* | |
* The presence of this method is a signal to the Ivy template type-check compiler that the | |
* `NgIf` structural directive renders its template with a specific context type. | |
*/ | |
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): | |
ctx is NgIfContext<Exclude<T, false|0|''|null|undefined>> { | |
return true; | |
} | |
} | |
/** | |
* @publicApi | |
*/ | |
export class NgIfContext<T = unknown> { | |
public $implicit: T = null!; | |
public ngIf: T = null!; | |
} | |
function assertTemplate(property: string, templateRef: TemplateRef<any>|null): void { | |
const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); | |
if (!isTemplateRefOrNull) { | |
throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment