Skip to content

Commit

Permalink
feat(common): make the CommonModule directives standalone
Browse files Browse the repository at this point in the history
This commit updates the directives presents in the `CommonModule` and annotates them with the `standalone: true` flag. With that flag, the directives can now be imported individually, as well as imported via the `CommonModule`.
  • Loading branch information
AndrewKushnir committed Jun 25, 2022
1 parent bdf57ab commit 992d694
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 36 deletions.
24 changes: 12 additions & 12 deletions goldens/public-api/common/index.md
Expand Up @@ -55,7 +55,7 @@ export class CommonModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<CommonModule>;
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<CommonModule, [typeof i1.NgClass, typeof i2.NgComponentOutlet, typeof i3.NgForOf, typeof i4.NgIf, typeof i5.NgTemplateOutlet, typeof i6.NgStyle, typeof i7.NgSwitch, typeof i7.NgSwitchCase, typeof i7.NgSwitchDefault, typeof i8.NgPlural, typeof i8.NgPluralCase], [typeof i9.AsyncPipe, typeof i10.UpperCasePipe, typeof i10.LowerCasePipe, typeof i11.JsonPipe, typeof i12.SlicePipe, typeof i13.DecimalPipe, typeof i13.PercentPipe, typeof i10.TitleCasePipe, typeof i13.CurrencyPipe, typeof i14.DatePipe, typeof i15.I18nPluralPipe, typeof i16.I18nSelectPipe, typeof i17.KeyValuePipe], [typeof i1.NgClass, typeof i2.NgComponentOutlet, typeof i3.NgForOf, typeof i4.NgIf, typeof i5.NgTemplateOutlet, typeof i6.NgStyle, typeof i7.NgSwitch, typeof i7.NgSwitchCase, typeof i7.NgSwitchDefault, typeof i8.NgPlural, typeof i8.NgPluralCase, typeof i9.AsyncPipe, typeof i10.UpperCasePipe, typeof i10.LowerCasePipe, typeof i11.JsonPipe, typeof i12.SlicePipe, typeof i13.DecimalPipe, typeof i13.PercentPipe, typeof i10.TitleCasePipe, typeof i13.CurrencyPipe, typeof i14.DatePipe, typeof i15.I18nPluralPipe, typeof i16.I18nSelectPipe, typeof i17.KeyValuePipe]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<CommonModule, never, [typeof i1.NgClass, typeof i2.NgComponentOutlet, typeof i3.NgForOf, typeof i4.NgIf, typeof i5.NgTemplateOutlet, typeof i6.NgStyle, typeof i7.NgSwitch, typeof i7.NgSwitchCase, typeof i7.NgSwitchDefault, typeof i8.NgPlural, typeof i8.NgPluralCase, typeof i9.AsyncPipe, typeof i10.UpperCasePipe, typeof i10.LowerCasePipe, typeof i11.JsonPipe, typeof i12.SlicePipe, typeof i13.DecimalPipe, typeof i13.PercentPipe, typeof i10.TitleCasePipe, typeof i13.CurrencyPipe, typeof i14.DatePipe, typeof i15.I18nPluralPipe, typeof i16.I18nSelectPipe, typeof i17.KeyValuePipe], [typeof i1.NgClass, typeof i2.NgComponentOutlet, typeof i3.NgForOf, typeof i4.NgIf, typeof i5.NgTemplateOutlet, typeof i6.NgStyle, typeof i7.NgSwitch, typeof i7.NgSwitchCase, typeof i7.NgSwitchDefault, typeof i8.NgPlural, typeof i8.NgPluralCase, typeof i9.AsyncPipe, typeof i10.UpperCasePipe, typeof i10.LowerCasePipe, typeof i11.JsonPipe, typeof i12.SlicePipe, typeof i13.DecimalPipe, typeof i13.PercentPipe, typeof i10.TitleCasePipe, typeof i13.CurrencyPipe, typeof i14.DatePipe, typeof i15.I18nPluralPipe, typeof i16.I18nSelectPipe, typeof i17.KeyValuePipe]>;
}

// @public
Expand Down Expand Up @@ -407,7 +407,7 @@ export class NgClass implements DoCheck {
// (undocumented)
ngDoCheck(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgClass, "[ngClass]", never, { "klass": "class"; "ngClass": "ngClass"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgClass, "[ngClass]", never, { "klass": "class"; "ngClass": "ngClass"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgClass, never>;
}
Expand All @@ -430,7 +430,7 @@ export class NgComponentOutlet implements OnChanges, OnDestroy {
// (undocumented)
ngOnDestroy(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgComponentOutlet, "[ngComponentOutlet]", never, { "ngComponentOutlet": "ngComponentOutlet"; "ngComponentOutletInjector": "ngComponentOutletInjector"; "ngComponentOutletContent": "ngComponentOutletContent"; "ngComponentOutletNgModule": "ngComponentOutletNgModule"; "ngComponentOutletNgModuleFactory": "ngComponentOutletNgModuleFactory"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgComponentOutlet, "[ngComponentOutlet]", never, { "ngComponentOutlet": "ngComponentOutlet"; "ngComponentOutletInjector": "ngComponentOutletInjector"; "ngComponentOutletContent": "ngComponentOutletContent"; "ngComponentOutletNgModule": "ngComponentOutletNgModule"; "ngComponentOutletNgModuleFactory": "ngComponentOutletNgModuleFactory"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgComponentOutlet, never>;
}
Expand All @@ -446,7 +446,7 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh
get ngForTrackBy(): TrackByFunction<T>;
static ngTemplateContextGuard<T, U extends NgIterable<T>>(dir: NgForOf<T, U>, ctx: any): ctx is NgForOfContext<T, U>;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgForOf<any, any>, "[ngFor][ngForOf]", never, { "ngForOf": "ngForOf"; "ngForTrackBy": "ngForTrackBy"; "ngForTemplate": "ngForTemplate"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgForOf<any, any>, "[ngFor][ngForOf]", never, { "ngForOf": "ngForOf"; "ngForTrackBy": "ngForTrackBy"; "ngForTemplate": "ngForTemplate"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgForOf<any, any>, never>;
}
Expand Down Expand Up @@ -481,7 +481,7 @@ export class NgIf<T = unknown> {
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<Exclude<T, false | 0 | '' | null | undefined>>;
static ngTemplateGuard_ngIf: 'binding';
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgIf<any>, "[ngIf]", never, { "ngIf": "ngIf"; "ngIfThen": "ngIfThen"; "ngIfElse": "ngIfElse"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgIf<any>, "[ngIf]", never, { "ngIf": "ngIf"; "ngIfThen": "ngIfThen"; "ngIfElse": "ngIfElse"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgIf<any>, never>;
}
Expand Down Expand Up @@ -525,7 +525,7 @@ export class NgPlural {
// (undocumented)
set ngPlural(value: number);
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgPlural, "[ngPlural]", never, { "ngPlural": "ngPlural"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgPlural, "[ngPlural]", never, { "ngPlural": "ngPlural"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgPlural, never>;
}
Expand All @@ -536,7 +536,7 @@ export class NgPluralCase {
// (undocumented)
value: string;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgPluralCase, "[ngPluralCase]", never, {}, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgPluralCase, "[ngPluralCase]", never, {}, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgPluralCase, [{ attribute: "ngPluralCase"; }, null, null, { host: true; }]>;
}
Expand All @@ -551,7 +551,7 @@ export class NgStyle implements DoCheck {
[klass: string]: any;
} | null);
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgStyle, "[ngStyle]", never, { "ngStyle": "ngStyle"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgStyle, "[ngStyle]", never, { "ngStyle": "ngStyle"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgStyle, never>;
}
Expand All @@ -561,7 +561,7 @@ export class NgSwitch {
// (undocumented)
set ngSwitch(newValue: any);
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitch, "[ngSwitch]", never, { "ngSwitch": "ngSwitch"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitch, "[ngSwitch]", never, { "ngSwitch": "ngSwitch"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgSwitch, never>;
}
Expand All @@ -572,7 +572,7 @@ export class NgSwitchCase implements DoCheck {
ngDoCheck(): void;
ngSwitchCase: any;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitchCase, "[ngSwitchCase]", never, { "ngSwitchCase": "ngSwitchCase"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitchCase, "[ngSwitchCase]", never, { "ngSwitchCase": "ngSwitchCase"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgSwitchCase, [null, null, { optional: true; host: true; }]>;
}
Expand All @@ -581,7 +581,7 @@ export class NgSwitchCase implements DoCheck {
export class NgSwitchDefault {
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>, ngSwitch: NgSwitch);
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitchDefault, "[ngSwitchDefault]", never, {}, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgSwitchDefault, "[ngSwitchDefault]", never, {}, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgSwitchDefault, [null, null, { optional: true; host: true; }]>;
}
Expand All @@ -595,7 +595,7 @@ export class NgTemplateOutlet implements OnChanges {
ngTemplateOutletContext: Object | null;
ngTemplateOutletInjector: Injector | null;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<NgTemplateOutlet, "[ngTemplateOutlet]", never, { "ngTemplateOutletContext": "ngTemplateOutletContext"; "ngTemplateOutlet": "ngTemplateOutlet"; "ngTemplateOutletInjector": "ngTemplateOutletInjector"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgTemplateOutlet, "[ngTemplateOutlet]", never, { "ngTemplateOutletContext": "ngTemplateOutletContext"; "ngTemplateOutlet": "ngTemplateOutlet"; "ngTemplateOutletInjector": "ngTemplateOutletInjector"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<NgTemplateOutlet, never>;
}
Expand Down
8 changes: 1 addition & 7 deletions packages/common/src/common_module.ts
Expand Up @@ -21,16 +21,10 @@ import {COMMON_PIPES} from './pipes/index';
* Re-exported by `BrowserModule`, which is included automatically in the root
* `AppModule` when you create a new app with the CLI `new` command.
*
* * The `providers` options configure the NgModule's injector to provide
* localization dependencies to members.
* * The `exports` options make the declared directives and pipes available for import
* by other NgModules.
*
* @publicApi
*/
@NgModule({
imports: [COMMON_PIPES],
declarations: [COMMON_DIRECTIVES],
imports: [COMMON_DIRECTIVES, COMMON_PIPES],
exports: [COMMON_DIRECTIVES, COMMON_PIPES],
})
export class CommonModule {
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_class.ts
Expand Up @@ -37,7 +37,10 @@ type NgClassSupportedTypes = string[]|Set<string>|{[klass: string]: any}|null|un
*
* @publicApi
*/
@Directive({selector: '[ngClass]'})
@Directive({
selector: '[ngClass]',
standalone: true,
})
export class NgClass implements DoCheck {
private _iterableDiffer: IterableDiffer<string>|null = null;
private _keyValueDiffer: KeyValueDiffer<string, any>|null = null;
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_component_outlet.ts
Expand Up @@ -68,7 +68,10 @@ import {ComponentRef, createNgModuleRef, Directive, Injector, Input, NgModuleFac
* @publicApi
* @ngModule CommonModule
*/
@Directive({selector: '[ngComponentOutlet]'})
@Directive({
selector: '[ngComponentOutlet]',
standalone: true,
})
export class NgComponentOutlet implements OnChanges, OnDestroy {
@Input() ngComponentOutlet: Type<any>|null = null;

Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_for_of.ts
Expand Up @@ -129,7 +129,10 @@ export class NgForOfContext<T, U extends NgIterable<T> = NgIterable<T>> {
* @ngModule CommonModule
* @publicApi
*/
@Directive({selector: '[ngFor][ngForOf]'})
@Directive({
selector: '[ngFor][ngForOf]',
standalone: true,
})
export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCheck {
/**
* The value of the iterable expression, which can be used as a
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_if.ts
Expand Up @@ -148,7 +148,10 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
* @ngModule CommonModule
* @publicApi
*/
@Directive({selector: '[ngIf]'})
@Directive({
selector: '[ngIf]',
standalone: true,
})
export class NgIf<T = unknown> {
private _context: NgIfContext<T> = new NgIfContext<T>();
private _thenTemplateRef: TemplateRef<NgIfContext<T>>|null = null;
Expand Down
10 changes: 8 additions & 2 deletions packages/common/src/directives/ng_plural.ts
Expand Up @@ -44,7 +44,10 @@ import {SwitchView} from './ng_switch';
*
* @publicApi
*/
@Directive({selector: '[ngPlural]'})
@Directive({
selector: '[ngPlural]',
standalone: true,
})
export class NgPlural {
// TODO(issue/24571): remove '!'.
private _switchValue!: number;
Expand Down Expand Up @@ -104,7 +107,10 @@ export class NgPlural {
*
* @publicApi
*/
@Directive({selector: '[ngPluralCase]'})
@Directive({
selector: '[ngPluralCase]',
standalone: true,
})
export class NgPluralCase {
constructor(
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_style.ts
Expand Up @@ -44,7 +44,10 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer,
*
* @publicApi
*/
@Directive({selector: '[ngStyle]'})
@Directive({
selector: '[ngStyle]',
standalone: true,
})
export class NgStyle implements DoCheck {
private _ngStyle: {[key: string]: string}|null = null;
private _differ: KeyValueDiffer<string, string|number>|null = null;
Expand Down
15 changes: 12 additions & 3 deletions packages/common/src/directives/ng_switch.ts
Expand Up @@ -101,7 +101,10 @@ export class SwitchView {
* @see [Structural Directives](guide/structural-directives)
*
*/
@Directive({selector: '[ngSwitch]'})
@Directive({
selector: '[ngSwitch]',
standalone: true,
})
export class NgSwitch {
// TODO(issue/24571): remove '!'.
private _defaultViews!: SwitchView[];
Expand Down Expand Up @@ -189,7 +192,10 @@ export class NgSwitch {
* @see `NgSwitchDefault`
*
*/
@Directive({selector: '[ngSwitchCase]'})
@Directive({
selector: '[ngSwitchCase]',
standalone: true,
})
export class NgSwitchCase implements DoCheck {
private _view: SwitchView;
/**
Expand Down Expand Up @@ -231,7 +237,10 @@ export class NgSwitchCase implements DoCheck {
* @see `NgSwitchCase`
*
*/
@Directive({selector: '[ngSwitchDefault]'})
@Directive({
selector: '[ngSwitchDefault]',
standalone: true,
})
export class NgSwitchDefault {
constructor(
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
Expand Down
5 changes: 4 additions & 1 deletion packages/common/src/directives/ng_template_outlet.ts
Expand Up @@ -32,7 +32,10 @@ import {Directive, EmbeddedViewRef, Injector, Input, OnChanges, SimpleChanges, T
*
* @publicApi
*/
@Directive({selector: '[ngTemplateOutlet]'})
@Directive({
selector: '[ngTemplateOutlet]',
standalone: true,
})
export class NgTemplateOutlet implements OnChanges {
private _viewRef: EmbeddedViewRef<any>|null = null;

Expand Down
18 changes: 18 additions & 0 deletions packages/common/test/directives/ng_class_spec.ts
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {NgClass} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';

Expand Down Expand Up @@ -391,6 +392,23 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
expect(leading.className).toBe('foo');
expect(trailing.className).toBe('foo');
});

it('should be available as a standalone directive', () => {
@Component({
selector: 'test-component',
imports: [NgClass],
template: `<div trailing-space [ngClass]="{foo: applyClasses}"></div>`,
standalone: true,
})
class TestComponent {
applyClasses = true;
}

const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

expect(fixture.nativeElement.firstChild.className).toBe('foo');
});
});
});
}
Expand Down
26 changes: 26 additions & 0 deletions packages/common/test/directives/ng_component_outlet_spec.ts
Expand Up @@ -267,6 +267,32 @@ describe('insert/remove', () => {

expect(fixture.nativeElement).toHaveText('Value: child');
}));

it('should be available as a standalone directive', () => {
@Component({
standalone: true,
template: 'Hello World',
})
class HelloWorldComp {
}

@Component({
selector: 'test-component',
imports: [NgComponentOutlet],
template: `
<ng-container *ngComponentOutlet="component"></ng-container>
`,
standalone: true,
})
class TestComponent {
component = HelloWorldComp;
}

const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('Hello World');
});
});

const TEST_TOKEN = new InjectionToken('TestToken');
Expand Down
21 changes: 20 additions & 1 deletion packages/common/test/directives/ng_for_spec.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {CommonModule} from '@angular/common';
import {CommonModule, 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';
Expand Down Expand Up @@ -380,6 +380,25 @@ let thisArg: any;
detectChangesAndExpectText('efh');
}));
});

it('should be available as a standalone directive', () => {
@Component({
selector: 'test-component',
imports: [NgForOf],
template: `
<ng-container *ngFor="let item of items">{{ item }}|</ng-container>
`,
standalone: true,
})
class TestComponent {
items = [1, 2, 3];
}

const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('1|2|3|');
});
});
}

Expand Down
22 changes: 21 additions & 1 deletion packages/common/test/directives/ng_if_spec.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {CommonModule, ɵgetDOM as getDOM} from '@angular/common';
import {CommonModule, NgIf, ɵgetDOM as getDOM} 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';
Expand Down Expand Up @@ -250,6 +250,26 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('false');
}));

it('should be available as a standalone directive', () => {
@Component({
selector: 'test-component',
imports: [NgIf],
template: `
<div *ngIf="true">Hello</div>
<div *ngIf="false">World</div>
`,
standalone: true,
})
class TestComponent {
}

const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('Hello');
expect(fixture.nativeElement.textContent).not.toBe('World');
});
});

describe('Type guarding', () => {
Expand Down

0 comments on commit 992d694

Please sign in to comment.