From 710d1da4edfa56134676b8a67ba78345ef87b17e Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Wed, 31 Aug 2022 10:51:05 -0700 Subject: [PATCH] refactor(common): create an `NgFor` alias for `NgForOf` directive (#47309) This commit adds a re-export of the `NgForOf` class as `NgFor` to improve the DX for cases when the directive is used as standalone. Developers can import `NgFor` class, which better matches the `ngFor` attribute used in a template. PR Close #47309 --- goldens/public-api/common/index.md | 4 +++- packages/common/src/common.ts | 2 +- packages/common/src/directives/index.ts | 4 +++- packages/common/src/directives/ng_for_of.ts | 5 +++++ .../common/test/directives/ng_for_spec.ts | 21 ++++++++++++++++++- .../missing_control_flow_directive/index.ts | 2 +- .../instructions/element_validation.ts | 2 +- 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/goldens/public-api/common/index.md b/goldens/public-api/common/index.md index 2974590afcce0..92f535578d68a 100644 --- a/goldens/public-api/common/index.md +++ b/goldens/public-api/common/index.md @@ -450,7 +450,7 @@ export class NgComponentOutlet implements OnChanges, OnDestroy { } // @public -export class NgForOf = NgIterable> implements DoCheck { +class NgForOf = NgIterable> implements DoCheck { constructor(_viewContainer: ViewContainerRef, _template: TemplateRef>, _differs: IterableDiffers); ngDoCheck(): void; set ngForOf(ngForOf: U & NgIterable | undefined | null); @@ -464,6 +464,8 @@ export class NgForOf = NgIterable> implements DoCh // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration, never>; } +export { NgForOf as NgFor } +export { NgForOf } // @public (undocumented) export class NgForOfContext = NgIterable> { diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index cce944b08ef53..2bd0c424f8d86 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -20,7 +20,7 @@ export {registerLocaleData} from './i18n/locale_data'; export {Plural, NumberFormatStyle, FormStyle, Time, TranslationWidth, FormatWidth, NumberSymbol, WeekDay, getNumberOfCurrencyDigits, getCurrencySymbol, getLocaleDayPeriods, getLocaleDayNames, getLocaleMonthNames, getLocaleId, getLocaleEraNames, getLocaleWeekEndRange, getLocaleFirstDayOfWeek, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocalePluralCase, getLocaleTimeFormat, getLocaleNumberSymbol, getLocaleNumberFormat, getLocaleCurrencyCode, getLocaleCurrencyName, getLocaleCurrencySymbol, getLocaleDirection} from './i18n/locale_data_api'; export {parseCookieValue as ɵparseCookieValue} from './cookie'; export {CommonModule} from './common_module'; -export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index'; +export {NgClass, NgFor, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index'; export {DOCUMENT} from './dom_tokens'; export {AsyncPipe, DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index'; export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id'; diff --git a/packages/common/src/directives/index.ts b/packages/common/src/directives/index.ts index d700bd6787b66..a66db917a5e29 100644 --- a/packages/common/src/directives/index.ts +++ b/packages/common/src/directives/index.ts @@ -7,9 +7,10 @@ */ import {Provider} from '@angular/core'; + import {NgClass} from './ng_class'; import {NgComponentOutlet} from './ng_component_outlet'; -import {NgForOf, NgForOfContext} from './ng_for_of'; +import {NgFor, NgForOf, NgForOfContext} from './ng_for_of'; import {NgIf, NgIfContext} from './ng_if'; import {NgPlural, NgPluralCase} from './ng_plural'; import {NgStyle} from './ng_style'; @@ -19,6 +20,7 @@ import {NgTemplateOutlet} from './ng_template_outlet'; export { NgClass, NgComponentOutlet, + NgFor, NgForOf, NgForOfContext, NgIf, diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index 5508d45d50f47..205581224db0a 100644 --- a/packages/common/src/directives/ng_for_of.ts +++ b/packages/common/src/directives/ng_for_of.ts @@ -290,6 +290,11 @@ export class NgForOf = NgIterable> implements DoCh } } +// Also export the `NgForOf` class as `NgFor` to improve the DX for +// cases when the directive is used as standalone, so the class name +// matches the CSS selector (*ngFor). +export {NgForOf as NgFor}; + function applyViewChange( view: EmbeddedViewRef>, record: IterableChangeRecord) { view.context.$implicit = record.item; diff --git a/packages/common/test/directives/ng_for_spec.ts b/packages/common/test/directives/ng_for_spec.ts index 2daa88ddef906..0928c94771d44 100644 --- a/packages/common/test/directives/ng_for_spec.ts +++ b/packages/common/test/directives/ng_for_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CommonModule, NgForOf} from '@angular/common'; +import {CommonModule, NgFor, NgForOf} from '@angular/common'; import {Component} from '@angular/core'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; @@ -408,6 +408,25 @@ let thisArg: any; expect(fixture.nativeElement.textContent).toBe('1|2|3|'); }); + + it('should be available as a standalone directive using an `NgFor` alias', () => { + @Component({ + selector: 'test-component', + imports: [NgFor], + template: ` + {{ item }}| + `, + standalone: true, + }) + class TestComponent { + items = [1, 2, 3]; + } + + const fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('1|2|3|'); + }); }); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_control_flow_directive/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_control_flow_directive/index.ts index 47230bbb3caa3..a0e0c89033feb 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_control_flow_directive/index.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_control_flow_directive/index.ts @@ -24,7 +24,7 @@ import {TemplateCheckFactory, TemplateCheckWithVisitor, TemplateContext} from '. * `CommonModule` is included, the `ngSwitch` would also be covered. */ export const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([ - ['ngIf', 'NgIf'], ['ngFor', 'NgForOf'], ['ngSwitchCase', 'NgSwitchCase'], + ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'], ['ngSwitchDefault', 'NgSwitchDefault'] ]); diff --git a/packages/core/src/render3/instructions/element_validation.ts b/packages/core/src/render3/instructions/element_validation.ts index bd907d984c3e2..570abc77e9363 100644 --- a/packages/core/src/render3/instructions/element_validation.ts +++ b/packages/core/src/render3/instructions/element_validation.ts @@ -282,7 +282,7 @@ function getTemplateLocationDetails(lView: LView): string { * that the `CommonModule` should also be included. */ export const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([ - ['ngIf', 'NgIf'], ['ngFor', 'NgForOf'], ['ngSwitchCase', 'NgSwitchCase'], + ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'], ['ngSwitchDefault', 'NgSwitchDefault'] ]); /**