/** * @license * Copyright Google Inc. 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 `` element. * * Simple form with shorthand syntax: * * ``` *
Content to render when condition is true.
* ``` * * Simple form with expanded syntax: * * ``` *
Content to render when condition is * true.
* ``` * * Form with an "else" block: * * ``` *
Content to render when condition is true.
* Content to render when condition is false. * ``` * * Shorthand form with "then" and "else" blocks: * * ``` *
* Content to render when condition is true. * Content to render when condition is false. * ``` * * Form with storing the value locally: * * ``` *
{{value}}
* Content to render when value is null. * ``` * * @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 `` 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 `` 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](guide/template-syntax#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. * * ``` *
* ... *
* * *
Loading...
*
* ``` * * You can see that the "else" clause references the `` * 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 `` tag, with `ngIf` and `ngIfElse` directives. * The anchor element containing the template for the "then" clause becomes * the content of this unlabeled `` tag. * * ``` * *
* ... *
*
* * *
Loading...
*
* ``` * * 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 { private _context: NgIfContext = new NgIfContext(); private _thenTemplateRef: TemplateRef>|null = null; private _elseTemplateRef: TemplateRef>|null = null; private _thenViewRef: EmbeddedViewRef>|null = null; private _elseViewRef: EmbeddedViewRef>|null = null; constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef>) { 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>|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>|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(dir: NgIf, ctx: any): ctx is NgIfContext> { return true; } } /** * @publicApi */ export class NgIfContext { public $implicit: T = null !; public ngIf: T = null !; } function assertTemplate(property: string, templateRef: TemplateRef| null): void { const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); if (!isTemplateRefOrNull) { throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`); } }