From 08219e0cdbc8370ff7213f5857b8fd7a1b976e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Exbrayat?= Date: Fri, 15 Jul 2022 09:37:52 +0200 Subject: [PATCH] fix(core): improve the missing control flow directive message Similarly to what has been done in #46846 for the extended diagnostics about missing control flow directive that was only mentioning that the `CommonModule` should be imported, this commit improves the validation done by the JiT compiler. Now that the control flow directives are available as standalone, the message mentions that directive itself can be imported. The message now also mentions which import should be used for the directive (as it can be tricky to figure out that `NgForOf` is the directive corresponding to `*ngFor`). --- .../render3/instructions/element_validation.ts | 15 +++++++++------ packages/core/test/acceptance/ng_module_spec.ts | 8 +++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/core/src/render3/instructions/element_validation.ts b/packages/core/src/render3/instructions/element_validation.ts index 9c44ff5eaac54..bd907d984c3e2 100644 --- a/packages/core/src/render3/instructions/element_validation.ts +++ b/packages/core/src/render3/instructions/element_validation.ts @@ -187,9 +187,11 @@ export function handleUnknownPropertyError( 'a part of an @NgModule where this component is declared'; if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) { // Most likely this is a control flow directive (such as `*ngIf`) used in - // a template, but the `CommonModule` is not imported. + // a template, but the directive or the `CommonModule` is not imported. + const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName); message += `\nIf the '${propName}' is an Angular control flow directive, ` + - `please make sure that the 'CommonModule' is ${importLocation}.`; + `please make sure that either the '${ + correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`; } else { // May be an Angular component, which is not imported/declared? message += `\n1. If '${tagName}' is an Angular component and it has the ` + @@ -275,13 +277,14 @@ function getTemplateLocationDetails(lView: LView): string { } /** - * The set of known control flow directives. + * The set of known control flow directives and their corresponding imports. * We use this set to produce a more precises error message with a note * that the `CommonModule` should also be included. */ -export const KNOWN_CONTROL_FLOW_DIRECTIVES = - new Set(['ngIf', 'ngFor', 'ngSwitch', 'ngSwitchCase', 'ngSwitchDefault']); - +export const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([ + ['ngIf', 'NgIf'], ['ngFor', 'NgForOf'], ['ngSwitchCase', 'NgSwitchCase'], + ['ngSwitchDefault', 'NgSwitchDefault'] +]); /** * Returns true if the tag name is allowed by specified schemas. * @param schemas Array of schemas diff --git a/packages/core/test/acceptance/ng_module_spec.ts b/packages/core/test/acceptance/ng_module_spec.ts index 6f739fae7d62f..81c4d4c8ca63d 100644 --- a/packages/core/test/acceptance/ng_module_spec.ts +++ b/packages/core/test/acceptance/ng_module_spec.ts @@ -634,7 +634,7 @@ describe('NgModule', () => { lines.forEach(line => expect(errorMessage).toMatch(line)); }); - KNOWN_CONTROL_FLOW_DIRECTIVES.forEach(directive => { + KNOWN_CONTROL_FLOW_DIRECTIVES.forEach((correspondingImport, directive) => { it(`should produce a warning when the '${directive}' directive ` + `is used in a template, but not imported in corresponding NgModule`, () => { @@ -663,7 +663,8 @@ describe('NgModule', () => { `NG0303: Can't bind to '${ directive}' since it isn't a known property of 'div' \\(used in the 'App' component template\\).`, `If the '${directive}' is an Angular control flow directive, please make sure ` + - `that the 'CommonModule' is a part of an @NgModule where this component is declared.` + `that either the '${ + correspondingImport}' directive or the 'CommonModule' is a part of an @NgModule where this component is declared.` ]; lines.forEach(line => expect(errorMessage).toMatch(line)); }); @@ -690,7 +691,8 @@ describe('NgModule', () => { `NG0303: Can't bind to '${ directive}' since it isn't a known property of 'div' \\(used in the 'App' component template\\).`, `If the '${directive}' is an Angular control flow directive, please make sure ` + - `that the 'CommonModule' is included in the '@Component.imports' of this component.` + `that either the '${ + correspondingImport}' directive or the 'CommonModule' is included in the '@Component.imports' of this component.` ]; lines.forEach(line => expect(errorMessage).toMatch(line)); });