mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(schematics): address form schematic (#11425)
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shipping-card {
|
||||
min-width: 120px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.mat-radio-button {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.col:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<form [formGroup]="addressForm" novalidate>
|
||||
<mat-card class="shipping-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Shipping Information</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="Company" formControlName="company">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="First name" formControlName="firstName">
|
||||
<mat-error *ngIf="addressForm.controls['firstName'].hasError('required')">
|
||||
First name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="Last name" formControlName="lastName">
|
||||
<mat-error *ngIf="addressForm.controls['lastName'].hasError('required')">
|
||||
Last name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<textarea matInput placeholder="Address" formControlName="address"></textarea>
|
||||
<mat-error *ngIf="addressForm.controls['address'].hasError('required')">
|
||||
Address is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="!hasUnitNumber">
|
||||
<div class="col">
|
||||
<button mat-button type="button" (click)="hasUnitNumber = !hasUnitNumber">
|
||||
+ Add C/O, Apt, Suite, Unit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="hasUnitNumber">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<textarea matInput placeholder="Address 2" formControlName="address2"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="City" formControlName="city">
|
||||
<mat-error *ngIf="addressForm.controls['city'].hasError('required')">
|
||||
City is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<mat-select placeholder="State" formControlName="state">
|
||||
<mat-option *ngFor="let state of states" [value]="state.abbreviation">
|
||||
{{ state.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="addressForm.controls['state'].hasError('required')">
|
||||
State is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput #postalCode maxlength="5" placeholder="Postal Code" type="number" formControlName="postalCode">
|
||||
<mat-hint align="end">{{postalCode.value.length}} / 5</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-radio-group formControlName="shipping">
|
||||
<mat-radio-button value="free">Free Shipping</mat-radio-button>
|
||||
<mat-radio-button value="priority">Priority Shipping</mat-radio-button>
|
||||
<mat-radio-button value="nextday">Next Day Shipping</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" type="submit">Submit</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</form>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component';
|
||||
|
||||
describe('<%= classify(name) %>Component', () => {
|
||||
let component: <%= classify(name) %>Component;
|
||||
let fixture: ComponentFixture<<%= classify(name) %>Component>;
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ <%= classify(name) %>Component ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(<%= classify(name) %>Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should compile', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
import { Component, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: '<%= selector %>',<% if(inlineTemplate) { %>
|
||||
template: `
|
||||
<form [formGroup]="addressForm" novalidate>
|
||||
<mat-card class="shipping-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Shipping Information</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="Company" formControlName="company">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="First name" formControlName="firstName">
|
||||
<mat-error *ngIf="addressForm.controls['firstName'].hasError('required')">
|
||||
First name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="Last name" formControlName="lastName">
|
||||
<mat-error *ngIf="addressForm.controls['lastName'].hasError('required')">
|
||||
Last name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<textarea matInput placeholder="Address" formControlName="address"></textarea>
|
||||
<mat-error *ngIf="addressForm.controls['address'].hasError('required')">
|
||||
Address is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="!hasUnitNumber">
|
||||
<div class="col">
|
||||
<button mat-button type="button" (click)="hasUnitNumber = !hasUnitNumber">
|
||||
+ Add C/O, Apt, Suite, Unit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" *ngIf="hasUnitNumber">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<textarea matInput placeholder="Address 2" formControlName="address2"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="City" formControlName="city">
|
||||
<mat-error *ngIf="addressForm.controls['city'].hasError('required')">
|
||||
City is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<mat-select placeholder="State" formControlName="state">
|
||||
<mat-option *ngFor="let state of states" [value]="state.abbreviation">
|
||||
{{ state.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="addressForm.controls['state'].hasError('required')">
|
||||
State is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput #postalCode maxlength="5" placeholder="Postal Code" type="number" formControlName="postalCode">
|
||||
<mat-hint align="end">{{postalCode.value.length}} / 5</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-radio-group formControlName="shipping">
|
||||
<mat-radio-button value="free">Free Shipping</mat-radio-button>
|
||||
<mat-radio-button value="priority">Priority Shipping</mat-radio-button>
|
||||
<mat-radio-button value="nextday">Next Day Shipping</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" type="submit">Submit</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</form>
|
||||
`,<% } else { %>
|
||||
templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %>
|
||||
styles: [
|
||||
`
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shipping-card {
|
||||
min-width: 120px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.mat-radio-button {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.col:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
`
|
||||
]<% } else { %>
|
||||
styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>,
|
||||
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
|
||||
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
|
||||
})
|
||||
export class <%= classify(name) %>Component {
|
||||
addressForm = this.fb.group({
|
||||
company: null,
|
||||
firstName: [null, Validators.required],
|
||||
lastName: [null, Validators.required],
|
||||
address: [null, Validators.required],
|
||||
address2: null,
|
||||
city: [null, Validators.required],
|
||||
state: [null, Validators.required],
|
||||
postalCode: [null, Validators.required, Validators.minLength(5), Validators.maxLength(5)],
|
||||
shipping: ['free', Validators.required]
|
||||
});
|
||||
|
||||
hasUnitNumber = false;
|
||||
|
||||
states = [
|
||||
{name:'Alabama', abbreviation:'AL'},
|
||||
{name:'Alaska', abbreviation:'AK'},
|
||||
{name:'American Samoa', abbreviation:'AS'},
|
||||
{name:'Arizona', abbreviation:'AZ'},
|
||||
{name:'Arkansas', abbreviation:'AR'},
|
||||
{name:'California', abbreviation:'CA'},
|
||||
{name:'Colorado', abbreviation:'CO'},
|
||||
{name:'Connecticut', abbreviation:'CT'},
|
||||
{name:'Delaware', abbreviation:'DE'},
|
||||
{name:'District Of Columbia', abbreviation:'DC'},
|
||||
{name:'Federated States Of Micronesia', abbreviation:'FM'},
|
||||
{name:'Florida', abbreviation:'FL'},
|
||||
{name:'Georgia', abbreviation:'GA'},
|
||||
{name:'Guam', abbreviation:'GU'},
|
||||
{name:'Hawaii', abbreviation:'HI'},
|
||||
{name:'Idaho', abbreviation:'ID'},
|
||||
{name:'Illinois', abbreviation:'IL'},
|
||||
{name:'Indiana', abbreviation:'IN'},
|
||||
{name:'Iowa', abbreviation:'IA'},
|
||||
{name:'Kansas', abbreviation:'KS'},
|
||||
{name:'Kentucky', abbreviation:'KY'},
|
||||
{name:'Louisiana', abbreviation:'LA'},
|
||||
{name:'Maine', abbreviation:'ME'},
|
||||
{name:'Marshall Islands', abbreviation:'MH'},
|
||||
{name:'Maryland', abbreviation:'MD'},
|
||||
{name:'Massachusetts', abbreviation:'MA'},
|
||||
{name:'Michigan', abbreviation:'MI'},
|
||||
{name:'Minnesota', abbreviation:'MN'},
|
||||
{name:'Mississippi', abbreviation:'MS'},
|
||||
{name:'Missouri', abbreviation:'MO'},
|
||||
{name:'Montana', abbreviation:'MT'},
|
||||
{name:'Nebraska', abbreviation:'NE'},
|
||||
{name:'Nevada', abbreviation:'NV'},
|
||||
{name:'New Hampshire', abbreviation:'NH'},
|
||||
{name:'New Jersey', abbreviation:'NJ'},
|
||||
{name:'New Mexico', abbreviation:'NM'},
|
||||
{name:'New York', abbreviation:'NY'},
|
||||
{name:'North Carolina', abbreviation:'NC'},
|
||||
{name:'North Dakota', abbreviation:'ND'},
|
||||
{name:'Northern Mariana Islands', abbreviation:'MP'},
|
||||
{name:'Ohio', abbreviation:'OH'},
|
||||
{name:'Oklahoma', abbreviation:'OK'},
|
||||
{name:'Oregon', abbreviation:'OR'},
|
||||
{name:'Palau', abbreviation:'PW'},
|
||||
{name:'Pennsylvania', abbreviation:'PA'},
|
||||
{name:'Puerto Rico', abbreviation:'PR'},
|
||||
{name:'Rhode Island', abbreviation:'RI'},
|
||||
{name:'South Carolina', abbreviation:'SC'},
|
||||
{name:'South Dakota', abbreviation:'SD'},
|
||||
{name:'Tennessee', abbreviation:'TN'},
|
||||
{name:'Texas', abbreviation:'TX'},
|
||||
{name:'Utah', abbreviation:'UT'},
|
||||
{name:'Vermont', abbreviation:'VT'},
|
||||
{name:'Virgin Islands', abbreviation:'VI'},
|
||||
{name:'Virginia', abbreviation:'VA'},
|
||||
{name:'Washington', abbreviation:'WA'},
|
||||
{name:'West Virginia', abbreviation:'WV'},
|
||||
{name:'Wisconsin', abbreviation:'WI'},
|
||||
{name:'Wyoming', abbreviation:'WY'}
|
||||
];
|
||||
|
||||
constructor(private fb: FormBuilder) {}
|
||||
|
||||
onSubmit() {
|
||||
alert('Thanks!');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import {chain, Rule, noop, Tree, SchematicContext} from '@angular-devkit/schematics';
|
||||
import {Schema} from './schema';
|
||||
import {addModuleImportToModule, findModuleFromOptions} from '../utils/ast';
|
||||
import {buildComponent} from '../utils/devkit-utils/component';
|
||||
|
||||
/**
|
||||
* Scaffolds a new table component.
|
||||
* Internally it bootstraps the base component schematic
|
||||
*/
|
||||
export default function(options: Schema): Rule {
|
||||
return chain([
|
||||
buildComponent({...options}),
|
||||
options.skipImport ? noop() : addFormModulesToModule(options)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the required modules to the relative module.
|
||||
*/
|
||||
function addFormModulesToModule(options: Schema) {
|
||||
return (host: Tree) => {
|
||||
const modulePath = findModuleFromOptions(host, options);
|
||||
addModuleImportToModule(host, modulePath, 'MatInputModule', '@angular/material');
|
||||
addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material');
|
||||
addModuleImportToModule(host, modulePath, 'MatSelectModule', '@angular/material');
|
||||
addModuleImportToModule(host, modulePath, 'MatRadioModule', '@angular/material');
|
||||
addModuleImportToModule(host, modulePath, 'MatCardModule', '@angular/material');
|
||||
addModuleImportToModule(host, modulePath, 'ReactiveFormsModule', '@angular/forms');
|
||||
return host;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "SchematicsMaterialAddressForm",
|
||||
"title": "Material Address Form Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path to create the component.",
|
||||
"visible": false
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"visible": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the component.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
}
|
||||
},
|
||||
"inlineStyle": {
|
||||
"description": "Specifies if the style will be in the ts file.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"alias": "s"
|
||||
},
|
||||
"inlineTemplate": {
|
||||
"description": "Specifies if the template will be in the ts file.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"alias": "t"
|
||||
},
|
||||
"viewEncapsulation": {
|
||||
"description": "Specifies the view encapsulation strategy.",
|
||||
"enum": ["Emulated", "Native", "None"],
|
||||
"type": "string",
|
||||
"alias": "v"
|
||||
},
|
||||
"changeDetection": {
|
||||
"description": "Specifies the change detection strategy.",
|
||||
"enum": ["Default", "OnPush"],
|
||||
"type": "string",
|
||||
"default": "Default",
|
||||
"alias": "c"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The prefix to apply to generated selectors.",
|
||||
"alias": "p"
|
||||
},
|
||||
"styleext": {
|
||||
"description": "The file extension to be used for style files.",
|
||||
"type": "string",
|
||||
"default": "css"
|
||||
},
|
||||
"spec": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies if a spec file is generated.",
|
||||
"default": true
|
||||
},
|
||||
"flat": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to indicate if a dir is created.",
|
||||
"default": false
|
||||
},
|
||||
"skipImport": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to skip the module import.",
|
||||
"default": false
|
||||
},
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The selector to use for the component."
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "Allows specification of the declaring module.",
|
||||
"alias": "m"
|
||||
},
|
||||
"export": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies if declaring module exports the component."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import {Schema as ComponentSchema} from '@schematics/angular/component/schema';
|
||||
|
||||
export interface Schema extends ComponentSchema {}
|
||||
@@ -28,7 +28,14 @@
|
||||
"description": "Create a component with a responsive sidenav for navigation",
|
||||
"factory": "./nav/index",
|
||||
"schema": "./nav/schema.json",
|
||||
"aliases": ["material-nav", "materialNav"]
|
||||
"aliases": [ "material-nav", "materialNav ]
|
||||
},
|
||||
// Creates a address form component
|
||||
"addressForm": {
|
||||
"description": "Create a component with a address form",
|
||||
"factory": "./address-form/index",
|
||||
"schema": "./address-form/schema.json",
|
||||
"aliases": ["address-form"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user