From 640ec7828fb0cfd15a0767f33228f0f79ed043f3 Mon Sep 17 00:00:00 2001 From: va-stefanek Date: Wed, 21 Apr 2021 21:34:34 +0200 Subject: [PATCH] refactor(common): change missing NgSwitch provider error message (#41704) change error message of invalid NgSwitch use and add corner cases tests PR Close #41704 --- packages/common/src/directives/ng_switch.ts | 23 ++++++++++++++++--- .../common/test/directives/ng_switch_spec.ts | 22 ++++++++++++++++-- packages/core/src/core_private_export.ts | 1 + packages/core/src/render3/error_code.ts | 1 + 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/common/src/directives/ng_switch.ts b/packages/common/src/directives/ng_switch.ts index 5847340383..cb67f7583e 100644 --- a/packages/common/src/directives/ng_switch.ts +++ b/packages/common/src/directives/ng_switch.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core'; +import {Directive, DoCheck, Host, Input, Optional, TemplateRef, ViewContainerRef, ɵRuntimeError as RuntimeError, ɵRuntimeErrorCode as RuntimeErrorCode} from '@angular/core'; export class SwitchView { private _created = false; @@ -197,7 +197,11 @@ export class NgSwitchCase implements DoCheck { constructor( viewContainer: ViewContainerRef, templateRef: TemplateRef, - @Host() private ngSwitch: NgSwitch) { + @Optional() @Host() private ngSwitch: NgSwitch) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError('ngSwitchCase', 'NgSwitchCase'); + } + ngSwitch._addCase(); this._view = new SwitchView(viewContainer, templateRef); } @@ -228,7 +232,20 @@ export class NgSwitchCase implements DoCheck { export class NgSwitchDefault { constructor( viewContainer: ViewContainerRef, templateRef: TemplateRef, - @Host() ngSwitch: NgSwitch) { + @Optional() @Host() ngSwitch: NgSwitch) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError('ngSwitchDefault', 'NgSwitchDefault'); + } + ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); } } + +function throwNgSwitchProviderNotFoundError(attrName: string, directiveName: string): never { + throw new RuntimeError( + RuntimeErrorCode.TEMPLATE_STRUCTURE_ERROR, + `An element with the "${attrName}" attribute ` + + `(matching the "${ + directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` + + `(matching "NgSwitch" directive)`); +} diff --git a/packages/common/test/directives/ng_switch_spec.ts b/packages/common/test/directives/ng_switch_spec.ts index 425ea6fd7a..e7c8bd5983 100644 --- a/packages/common/test/directives/ng_switch_spec.ts +++ b/packages/common/test/directives/ng_switch_spec.ts @@ -7,8 +7,8 @@ */ import {CommonModule} from '@angular/common'; -import {Attribute, Component, Directive, TemplateRef, ViewChild} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {Attribute, Component, Directive, TemplateRef, ViewChild,} from '@angular/core'; +import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; { @@ -176,6 +176,24 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; detectChangesAndExpectText('when b1;when b2;'); }); + it('should throw error when ngSwitchCase is used outside of ngSwitch', waitForAsync(() => { + const template = '
' + + '
'; + + expect(() => createTestComponent(template)) + .toThrowError( + 'NG0305: An element with the "ngSwitchCase" attribute (matching the "NgSwitchCase" directive) must be located inside an element with the "ngSwitch" attribute (matching "NgSwitch" directive)'); + })); + + it('should throw error when ngSwitchDefault is used outside of ngSwitch', waitForAsync(() => { + const template = '
' + + '
'; + + expect(() => createTestComponent(template)) + .toThrowError( + 'NG0305: An element with the "ngSwitchDefault" attribute (matching the "NgSwitchDefault" directive) must be located inside an element with the "ngSwitch" attribute (matching "NgSwitch" directive)'); + })); + it('should support nested NgSwitch on ng-container with ngTemplateOutlet', () => { fixture = TestBed.createComponent(ComplexComponent); detectChangesAndExpectText('Foo'); diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 39e2d396f8..f7ae0203ad 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -24,6 +24,7 @@ export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} fr export {clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, resolveComponentResources as ɵresolveComponentResources} from './metadata/resource_loading'; export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities'; export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types'; +export {RuntimeError as ɵRuntimeError, RuntimeErrorCode as ɵRuntimeErrorCode} from './render3/error_code'; export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass'; export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer'; export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer'; diff --git a/packages/core/src/render3/error_code.ts b/packages/core/src/render3/error_code.ts index 80e478d166..84be7f56be 100644 --- a/packages/core/src/render3/error_code.ts +++ b/packages/core/src/render3/error_code.ts @@ -27,6 +27,7 @@ export const enum RuntimeErrorCode { PIPE_NOT_FOUND = '302', UNKNOWN_BINDING = '303', UNKNOWN_ELEMENT = '304', + TEMPLATE_STRUCTURE_ERROR = '305' // Styling Errors