/** * @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, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core'; export class SwitchView { private _created = false; constructor( private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {} create(): void { this._created = true; this._viewContainerRef.createEmbeddedView(this._templateRef); } destroy(): void { this._created = false; this._viewContainerRef.clear(); } enforceState(created: boolean) { if (created && !this._created) { this.create(); } else if (!created && this._created) { this.destroy(); } } } /** * @ngModule CommonModule * * @description A structural directive that adds or removes templates (displaying or hiding views) * when the next match expression matches the switch expression. * * The `[ngSwitch]` directive on a container specifies an expression to match against. * The expressions to match are provided by `ngSwitchCase` directives on views within the container. * - Every view that matches is rendered. * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` * or `ngSwitchDefault` directive are preserved at the location. * * @usageNotes * Define a container element for the directive, and specify the switch expression * to match against as an attribute: * * ``` * * ``` * * Within the container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * * ``` * * ... * ... * ... * * ``` * * ### Usage Examples * * The following example shows how to use more than one case to display the same view: * * ``` * * * ... * ... * ... * * ... * * ``` * * The following example shows how cases can be nested: * ``` * * ... * ... * ... * * * * * * ... * * ``` * * @publicApi * @see `NgSwitchCase` * @see `NgSwitchDefault` * @see [Stuctural Directives](guide/structural-directives) * */ @Directive({selector: '[ngSwitch]'}) export class NgSwitch { // TODO(issue/24571): remove '!'. private _defaultViews !: SwitchView[]; private _defaultUsed = false; private _caseCount = 0; private _lastCaseCheckIndex = 0; private _lastCasesMatched = false; private _ngSwitch: any; @Input() set ngSwitch(newValue: any) { this._ngSwitch = newValue; if (this._caseCount === 0) { this._updateDefaultCases(true); } } /** @internal */ _addCase(): number { return this._caseCount++; } /** @internal */ _addDefault(view: SwitchView) { if (!this._defaultViews) { this._defaultViews = []; } this._defaultViews.push(view); } /** @internal */ _matchCase(value: any): boolean { const matched = value == this._ngSwitch; this._lastCasesMatched = this._lastCasesMatched || matched; this._lastCaseCheckIndex++; if (this._lastCaseCheckIndex === this._caseCount) { this._updateDefaultCases(!this._lastCasesMatched); this._lastCaseCheckIndex = 0; this._lastCasesMatched = false; } return matched; } private _updateDefaultCases(useDefault: boolean) { if (this._defaultViews && useDefault !== this._defaultUsed) { this._defaultUsed = useDefault; for (let i = 0; i < this._defaultViews.length; i++) { const defaultView = this._defaultViews[i]; defaultView.enforceState(useDefault); } } } } /** * @ngModule CommonModule * * @description * Provides a switch case expression to match against an enclosing `ngSwitch` expression. * When the expressions match, the given `NgSwitchCase` template is rendered. * If multiple match expressions match the switch expression value, all of them are displayed. * * @usageNotes * * Within a switch container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * * ``` * * ... * ... * ... * * ``` * * Each switch-case statement contains an in-line HTML template or template reference * that defines the subtree to be selected if the value of the match expression * matches the value of the switch expression. * * Unlike JavaScript, which uses strict equality, Angular uses loose equality. * This means that the empty string, `""` matches 0. * * @publicApi * @see `NgSwitch` * @see `NgSwitchDefault` * */ @Directive({selector: '[ngSwitchCase]'}) export class NgSwitchCase implements DoCheck { private _view: SwitchView; /** * Stores the HTML template to be selected on match. */ @Input() ngSwitchCase: any; constructor( viewContainer: ViewContainerRef, templateRef: TemplateRef, @Host() private ngSwitch: NgSwitch) { ngSwitch._addCase(); this._view = new SwitchView(viewContainer, templateRef); } /** * Performs case matching. For internal use only. */ ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); } } /** * @ngModule CommonModule * * @description * * Creates a view that is rendered when no `NgSwitchCase` expressions * match the `NgSwitch` expression. * This statement should be the final case in an `NgSwitch`. * * @publicApi * @see `NgSwitch` * @see `NgSwitchCase` * */ @Directive({selector: '[ngSwitchDefault]'}) export class NgSwitchDefault { constructor( viewContainer: ViewContainerRef, templateRef: TemplateRef, @Host() ngSwitch: NgSwitch) { ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); } }