Skip to content

Commit 186c874

Browse files
committed
fix(material/schematics): support standalone projects in navigation schematic
Updates the `ng generate navigation` schematic to support standalone projects. (cherry picked from commit 8897b64)
1 parent 313348e commit 186c874

File tree

5 files changed

+75
-21
lines changed

5 files changed

+75
-21
lines changed

src/material/schematics/ng-generate/navigation/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { LayoutModule } from '@angular/cdk/layout';
21
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
2+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';<% if(!standalone) { %>
43
import { MatButtonModule } from '@angular/material/button';
54
import { MatIconModule } from '@angular/material/icon';
65
import { MatListModule } from '@angular/material/list';
76
import { MatSidenavModule } from '@angular/material/sidenav';
8-
import { MatToolbarModule } from '@angular/material/toolbar';
7+
import { MatToolbarModule } from '@angular/material/toolbar';<% } %>
98

109
import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component';
1110

@@ -14,17 +13,17 @@ describe('<%= classify(name) %>Component', () => {
1413
let fixture: ComponentFixture<<%= classify(name) %>Component>;
1514

1615
beforeEach(waitForAsync(() => {
17-
TestBed.configureTestingModule({
16+
TestBed.configureTestingModule({<% if(standalone) { %>
17+
imports: [NoopAnimationsModule]<% } else { %>
1818
declarations: [<%= classify(name) %>Component],
1919
imports: [
2020
NoopAnimationsModule,
21-
LayoutModule,
2221
MatButtonModule,
2322
MatIconModule,
2423
MatListModule,
2524
MatSidenavModule,
2625
MatToolbarModule,
27-
]
26+
]<% } %>
2827
}).compileComponents();
2928
}));
3029

src/material/schematics/ng-generate/navigation/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { Component<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
2-
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
1+
import { Component, inject<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
2+
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';<% if(standalone) { %>
3+
import { MatToolbarModule } from '@angular/material/toolbar';
4+
import { MatButtonModule } from '@angular/material/button';
5+
import { MatSidenavModule } from '@angular/material/sidenav';
6+
import { MatListModule } from '@angular/material/list';
7+
import { MatIconModule } from '@angular/material/icon';<% } %>
38
import { Observable } from 'rxjs';
49
import { map, shareReplay } from 'rxjs/operators';
510

@@ -14,16 +19,22 @@ import { map, shareReplay } from 'rxjs/operators';
1419
`]<% } else { %>
1520
styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
1621
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
17-
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
22+
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %><% if(standalone) { %>,
23+
standalone: true,
24+
imports: [
25+
MatToolbarModule,
26+
MatButtonModule,
27+
MatSidenavModule,
28+
MatListModule,
29+
MatIconModule
30+
]<% } %>
1831
})
1932
export class <%= classify(name) %>Component {
33+
private breakpointObserver = inject(BreakpointObserver);
2034

2135
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
2236
.pipe(
2337
map(result => result.matches),
2438
shareReplay()
2539
);
26-
27-
constructor(private breakpointObserver: BreakpointObserver) {}
28-
2940
}

src/material/schematics/ng-generate/navigation/index.spec.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ describe('material-navigation-schematic', () => {
1818

1919
function expectNavigationSchematicModuleImports(tree: UnitTestTree) {
2020
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
21-
expect(moduleContent).toMatch(/LayoutModule,\s+/);
2221
expect(moduleContent).toMatch(/MatToolbarModule,\s+/);
2322
expect(moduleContent).toMatch(/MatButtonModule,\s+/);
2423
expect(moduleContent).toMatch(/MatSidenavModule,\s+/);
2524
expect(moduleContent).toMatch(/MatIconModule,\s+/);
2625
expect(moduleContent).toMatch(/MatListModule\s+],/);
27-
expect(moduleContent).toContain(`import { LayoutModule } from '@angular/cdk/layout';`);
2826
expect(moduleContent).toContain(`import { MatButtonModule } from '@angular/material/button';`);
2927
expect(moduleContent).toContain(`import { MatIconModule } from '@angular/material/icon';`);
3028
expect(moduleContent).toContain(`import { MatListModule } from '@angular/material/list';`);
@@ -71,6 +69,44 @@ describe('material-navigation-schematic', () => {
7169
).toBeRejectedWithError(/required property 'name'/);
7270
});
7371

72+
describe('standalone option', () => {
73+
it('should generate a standalone component', async () => {
74+
const app = await createTestApp(runner);
75+
const tree = await runner.runSchematic('navigation', {...baseOptions, standalone: true}, app);
76+
const module = getFileContent(tree, '/projects/material/src/app/app.module.ts');
77+
const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts');
78+
const requiredModules = [
79+
'MatToolbarModule',
80+
'MatButtonModule',
81+
'MatSidenavModule',
82+
'MatListModule',
83+
'MatIconModule',
84+
];
85+
86+
requiredModules.forEach(name => {
87+
expect(module).withContext('Module should not import dependencies').not.toContain(name);
88+
expect(component).withContext('Component should import dependencies').toContain(name);
89+
});
90+
91+
expect(module).not.toContain('FooComponent');
92+
expect(component).toContain('standalone: true');
93+
expect(component).toContain('imports: [');
94+
});
95+
96+
it('should infer the standalone option from the project structure', async () => {
97+
const app = await createTestApp(runner, {standalone: true});
98+
const tree = await runner.runSchematic('navigation', baseOptions, app);
99+
const componentContent = getFileContent(
100+
tree,
101+
'/projects/material/src/app/foo/foo.component.ts',
102+
);
103+
104+
expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false);
105+
expect(componentContent).toContain('standalone: true');
106+
expect(componentContent).toContain('imports: [');
107+
});
108+
});
109+
74110
describe('style option', () => {
75111
it('should respect the option value', async () => {
76112
const tree = await runner.runSchematic(

src/material/schematics/ng-generate/navigation/index.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
addModuleImportToModule,
1212
buildComponent,
1313
findModuleFromOptions,
14+
isStandaloneSchematic,
1415
} from '@angular/cdk/schematics';
1516
import {Schema} from './schema';
1617

@@ -38,12 +39,15 @@ export default function (options: Schema): Rule {
3839
*/
3940
function addNavModulesToModule(options: Schema) {
4041
return async (host: Tree) => {
41-
const modulePath = (await findModuleFromOptions(host, options))!;
42-
addModuleImportToModule(host, modulePath, 'LayoutModule', '@angular/cdk/layout');
43-
addModuleImportToModule(host, modulePath, 'MatToolbarModule', '@angular/material/toolbar');
44-
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material/button');
45-
addModuleImportToModule(host, modulePath, 'MatSidenavModule', '@angular/material/sidenav');
46-
addModuleImportToModule(host, modulePath, 'MatIconModule', '@angular/material/icon');
47-
addModuleImportToModule(host, modulePath, 'MatListModule', '@angular/material/list');
42+
const isStandalone = await isStandaloneSchematic(host, options);
43+
44+
if (!isStandalone) {
45+
const modulePath = (await findModuleFromOptions(host, options))!;
46+
addModuleImportToModule(host, modulePath, 'MatToolbarModule', '@angular/material/toolbar');
47+
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material/button');
48+
addModuleImportToModule(host, modulePath, 'MatSidenavModule', '@angular/material/sidenav');
49+
addModuleImportToModule(host, modulePath, 'MatIconModule', '@angular/material/icon');
50+
addModuleImportToModule(host, modulePath, 'MatListModule', '@angular/material/list');
51+
}
4852
};
4953
}

src/material/schematics/ng-generate/navigation/schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
"type": "boolean",
4040
"alias": "t"
4141
},
42+
"standalone": {
43+
"description": "Whether the generated component is standalone.",
44+
"type": "boolean"
45+
},
4246
"viewEncapsulation": {
4347
"description": "Specifies the view encapsulation strategy.",
4448
"enum": ["Emulated", "None"],

0 commit comments

Comments
 (0)