refactor(common): change missing NgSwitch provider error message (#41704)

change error message of invalid NgSwitch use and add corner cases tests

PR Close #41704
This commit is contained in:
va-stefanek 2021-04-21 21:34:34 +02:00 committed by Alex Rickabaugh
parent 2b939767fe
commit 640ec7828f
4 changed files with 42 additions and 5 deletions

View File

@ -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<Object>,
@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<Object>,
@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)`);
}

View File

@ -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 = '<div [ngSwitch]="switchValue"></div>' +
'<div *ngSwitchCase="\'a\'"></div>';
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 = '<div [ngSwitch]="switchValue"></div>' +
'<div *ngSwitchDefault></div>';
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');

View File

@ -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';

View File

@ -27,6 +27,7 @@ export const enum RuntimeErrorCode {
PIPE_NOT_FOUND = '302',
UNKNOWN_BINDING = '303',
UNKNOWN_ELEMENT = '304',
TEMPLATE_STRUCTURE_ERROR = '305'
// Styling Errors