Skip to content

Commit

Permalink
fix(material/schematics): support standalone projects in address from…
Browse files Browse the repository at this point in the history
… schematic

Updates the `ng generate address-form` schematic to support standalone projects.

(cherry picked from commit 5539d75)
  • Loading branch information
crisbeto committed Apr 25, 2023
1 parent f1350f8 commit f8fad4f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 16 deletions.
@@ -1,11 +1,11 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';<% if(!standalone) { %>
import { ReactiveFormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatInputModule } from '@angular/material/input';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSelectModule } from '@angular/material/select';<% } %>

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

Expand All @@ -14,8 +14,9 @@ describe('<%= classify(name) %>Component', () => {
let fixture: ComponentFixture<<%= classify(name) %>Component>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ <%= classify(name) %>Component ],
TestBed.configureTestingModule({<% if(standalone) { %>
imports: [NoopAnimationsModule]<% } else { %>
declarations: [<%= classify(name) %>Component],
imports: [
NoopAnimationsModule,
ReactiveFormsModule,
Expand All @@ -24,7 +25,7 @@ describe('<%= classify(name) %>Component', () => {
MatInputModule,
MatRadioModule,
MatSelectModule,
]
]<% } %>
}).compileComponents();
}));

Expand Down
@@ -1,5 +1,14 @@
import { Component<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
import { Component, inject<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
<% if(standalone) { %>
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatCardModule } from '@angular/material/card';
<% } else { %>
import { FormBuilder, Validators } from '@angular/forms';
<% } %>

@Component({
selector: '<%= selector %>',<% if(inlineTemplate) { %>
Expand All @@ -12,9 +21,19 @@ import { FormBuilder, Validators } from '@angular/forms';
`]<% } else { %>
styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %><% if(standalone) { %>,
standalone: true,
imports: [
MatInputModule,
MatButtonModule,
MatSelectModule,
MatRadioModule,
MatCardModule,
ReactiveFormsModule
]<% } %>
})
export class <%= classify(name) %>Component {
private fb = inject(FormBuilder);
addressForm = this.fb.group({
company: null,
firstName: [null, Validators.required],
Expand Down Expand Up @@ -93,8 +112,6 @@ export class <%= classify(name) %>Component {
{name: 'Wyoming', abbreviation: 'WY'}
];

constructor(private fb: FormBuilder) {}

onSubmit(): void {
alert('Thanks!');
}
Expand Down
39 changes: 39 additions & 0 deletions src/material/schematics/ng-generate/address-form/index.spec.ts
Expand Up @@ -50,6 +50,45 @@ describe('Material address-form schematic', () => {
).toBeRejectedWithError(/required property 'name'/);
});

describe('standalone option', () => {
it('should generate a standalone component', async () => {
const app = await createTestApp(runner);
const tree = await runner.runSchematic(
'address-form',
{...baseOptions, standalone: true},
app,
);
const module = getFileContent(tree, '/projects/material/src/app/app.module.ts');
const content = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts');
const requiredModules = [
'MatInputModule',
'MatButtonModule',
'MatSelectModule',
'MatRadioModule',
'ReactiveFormsModule',
];

requiredModules.forEach(name => {
expect(module).withContext('Module should not import dependencies').not.toContain(name);
expect(content).withContext('Component should import dependencies').toContain(name);
});

expect(module).not.toContain('FooComponent');
expect(content).toContain('standalone: true');
expect(content).toContain('imports: [');
});

it('should infer the standalone option from the project structure', async () => {
const app = await createTestApp(runner, {standalone: true});
const tree = await runner.runSchematic('address-form', baseOptions, app);
const component = getFileContent(tree, '/projects/material/src/app/foo/foo.component.ts');

expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false);
expect(component).toContain('standalone: true');
expect(component).toContain('imports: [');
});
});

describe('style option', () => {
it('should respect the option value', async () => {
const tree = await runner.runSchematic(
Expand Down
19 changes: 12 additions & 7 deletions src/material/schematics/ng-generate/address-form/index.ts
Expand Up @@ -11,6 +11,7 @@ import {
addModuleImportToModule,
buildComponent,
findModuleFromOptions,
isStandaloneSchematic,
} from '@angular/cdk/schematics';
import {Schema} from './schema';

Expand Down Expand Up @@ -38,12 +39,16 @@ export default function (options: Schema): Rule {
*/
function addFormModulesToModule(options: Schema) {
return async (host: Tree) => {
const modulePath = (await findModuleFromOptions(host, options))!;
addModuleImportToModule(host, modulePath, 'MatInputModule', '@angular/material/input');
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material/button');
addModuleImportToModule(host, modulePath, 'MatSelectModule', '@angular/material/select');
addModuleImportToModule(host, modulePath, 'MatRadioModule', '@angular/material/radio');
addModuleImportToModule(host, modulePath, 'MatCardModule', '@angular/material/card');
addModuleImportToModule(host, modulePath, 'ReactiveFormsModule', '@angular/forms');
const isStandalone = await isStandaloneSchematic(host, options);

if (!isStandalone) {
const modulePath = (await findModuleFromOptions(host, options))!;
addModuleImportToModule(host, modulePath, 'MatInputModule', '@angular/material/input');
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material/button');
addModuleImportToModule(host, modulePath, 'MatSelectModule', '@angular/material/select');
addModuleImportToModule(host, modulePath, 'MatRadioModule', '@angular/material/radio');
addModuleImportToModule(host, modulePath, 'MatCardModule', '@angular/material/card');
addModuleImportToModule(host, modulePath, 'ReactiveFormsModule', '@angular/forms');
}
};
}
4 changes: 4 additions & 0 deletions src/material/schematics/ng-generate/address-form/schema.json
Expand Up @@ -39,6 +39,10 @@
"type": "boolean",
"alias": "t"
},
"standalone": {
"description": "Whether the generated component is standalone.",
"type": "boolean"
},
"viewEncapsulation": {
"description": "Specifies the view encapsulation strategy.",
"enum": ["Emulated", "None"],
Expand Down

0 comments on commit f8fad4f

Please sign in to comment.