feat(schematics): address form schematic (#11425)

This commit is contained in:
Austin
2018-06-12 12:48:23 -05:00
committed by Jeremy Elbourn
parent 81537af7ba
commit 9b80a4cbb5
8 changed files with 512 additions and 1 deletions
@@ -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;
}
@@ -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>
@@ -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();
});
});
@@ -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!');
}
}
+31
View File
@@ -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 {}
+8 -1
View File
@@ -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"]
}
}
}