<!DOCTYPE html>
<html>
<head>
<title>Simple Edit Page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.6.25?main=browser"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
<!--
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: '<simple-edit-form></simple-edit-form>'
})
export class AppComponent { }
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { SimpleEditFormComponent } from './simple-edit-form.component';
import {PhoneNumberPipe } from './PhoneNumber.pipe';
import {ManufacturerService} from './Manufacturer.service';
import {LookupService} from './Lookup.service';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
SimpleEditFormComponent,
PhoneNumberPipe
],
providers: [ManufacturerService, LookupService],
bootstrap: [ AppComponent ]
})
export class AppModule { }
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
// Compiles the module (asynchronously) with the runtime compiler
// which generates a compiled module factory in memory.
// Then bootstraps with that factory, targeting the browser.
platformBrowserDynamic().bootstrapModule(AppModule);
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
/**
* PLUNKER VERSION
* (based on systemjs.config.js in angular.io)
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
"exports": "ts"
}
},
paths: {
// paths serve as alias
'npm:': 'https://unpkg.com/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
'ts': 'npm:plugin-typescript@4.0.10/lib/plugin.js',
'typescript': 'npm:typescript@2.0.2/lib/typescript.js',
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
},
'angular-in-memory-web-api': {
main: './index.js',
defaultExtension: 'js'
}
}
});
})(this);
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
import {Component, OnInit } from '@angular/core';
import {Manufacturer} from './manufacturer';
import {LookupService} from './Lookup.service';
import {ManufacturerService} from './Manufacturer.service';
@Component({
moduleId: module.id,
selector: 'simple-edit-form',
templateUrl: 'simple-edit-form.component.html'
})
export class SimpleEditFormComponent implements OnInit{
pageMode : string;
statesList: string[];
manufacturer: Manufacturer = new Manufacturer();
manufacturerPrior: Manufacturer;
constructor(private manufacturerService: ManufacturerService,
private lookupService: LookupService){}
ngOnInit() : void {
this.pageMode = "viewMode";
this.statesList = this.lookupService.getStates();
//this.manufacturerService.getManufacturerAsync().then(manufacturer => this.manufacturer =manufacturer);
this.manufacturer = this.manufacturerService.getManufacturer();
this.manufacturerPrior = new Manufacturer();
}
editManufacturer() : void {
//note this is not a replacement for angular.copy().
//.assign() creates a *shallow* copy, meaning that any properties containing
//references and not simple types will simply have the reference copied to the new
//object, instead of copying the values within that reference (i.e. a deep copy)
//discussion: http://stackoverflow.com/questions/34688517/how-can-i-use-angular-copy-in-angular-2
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.assign(this.manufacturerPrior, this.manufacturer);
this.pageMode = "editMode";
}
newManufacturer() : void {
Object.assign(this.manufacturerPrior, this.manufacturer);
//this works without having assign empty property values
this.manufacturer = new Manufacturer();
this.pageMode = "editMode";
}
saveManufacturer() : void {
// for some reason, manufacturerForm.valid is not available here
// It works here: http://plnkr.co/edit/IElMhx2Kcos7VLrI2QfC?p=preview
// if we add: import { FORM_DIRECTIVES, ControlGroup, Control, Validators, FormBuilder, Validator, } from '@angular/common';
// and directives: [FORM_DIRECTIVES]
// but this doesn't work on the page (maybe need to add to app.module??)
if (manufacturerForm.checkValidity()) {
this.manufacturerPrior = new Manufacturer();
this.pageMode = "viewMode";
}
}
cancelEdit() : void {
Object.assign(this.manufacturer, this.manufacturerPrior);
this.manufacturerPrior = new Manufacturer();
this.pageMode = "viewMode";
}
}
<h1>Angular2 Simple Edit Form</h1>
<hr />
<h2>Manufacturer</h2>
<div class="container">
<form id="manufacturerForm" name="manufacturerForm" method="post" novalidate (ngSubmit)="saveManufacturer()" #manufacturerForm="ngForm">
<p>
<label for="nameInput">Name:</label>
<input type="text" id="nameInput" name="nameInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.name" required #nameInput="ngModel"/>
<span class="error" [hidden]="nameInput.valid || !nameInput.errors.required">Name field is required</span>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.name}}</span>
</p>
<p>
<label for="accountNumberInput">Account #:</label>
<input type="text" id="accountNumberInput" name="accountNumberInput" #accountNumberInput="ngModel" [ngClass]="pageMode" [(ngModel)]="manufacturer.accountNumber" required minlength="3" [maxlength]="15"/>
<span class="error" [hidden]="accountNumberInput.valid || !accountNumberInput.errors.required">Account Number is required</span>
<span class="error" [hidden]="accountNumberInput.valid || (!accountNumberInput.errors.minlength && !accountNumberInput.errors.maxlength)">Account Number must be between 3 and 15 chars</span>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.accountNumber}}</span>
</p>
<p>
<label for="credLimitInput">Credit Limit:</label>
<input type="number" id="creditLimitInput" name="creditLimitInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.creditLimit"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.creditLimit | currency }}</span>
</p>
<p>
<label for="addressLine1Input">Address 1:</label>
<input type="text" id="addressLine1Input" name="addressLine1Input" [ngClass]="pageMode" [(ngModel)]="manufacturer.addressLine1"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.addressLine1}}</span>
</p>
<p>
<label for="addressLine2Input">Address 2:</label>
<input type="text" id="addressLine2Input" name="addressLine2Input" [ngClass]="pageMode" [(ngModel)]="manufacturer.addressLine2"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.addressLine2}}</span>
</p>
<p>
<label for="addressCityInput">City:</label>
<input type="text" id="addressCityInput" name="addressCityInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.addressCity"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.addressCity}}</span>
</p>
<p>
<label for="addressStateSelect">State:</label>
<select id="addressStateSelect" name="addressStateSelect" [(ngModel)]="manufacturer.addressState" [ngClass]="pageMode">
<option *ngFor="let state of statesList" [value]="state">{{state}}</option>
</select>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.addressState}}</span>
</p>
<p>
<label for="addressPostalInput">Postal:</label>
<input type="text" id="addressPostalInput" name="addressPostalInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.addressPostal"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.addressPostal}}</span>
</p>
<p>
<label for="phoneNumberInput">Phone Number:</label>
<input type="text" id="phoneNumberInput" name="phoneNumberInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.phoneNumber"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.phoneNumber | phonenumber }}</span>
</p>
<p>
<label for="faxNumberInput">Fax Number:</label>
<input type="text" id="faxNumberInput" name="faxNumberInput" [ngClass]="pageMode" [(ngModel)]="manufacturer.faxNumber"/>
<span class="displayValue" [ngClass]="pageMode">{{manufacturer.faxNumber | phonenumber }}</span>
</p>
<p>
<label>Date Modified:</label>
<span>{{manufacturer.dateModified | date: 'short'}}</span>
</p>
<p>
<label>Date Created:</label>
<span>{{manufacturer.dateCreated | date: 'short'}}</span>
</p>
<button type="submit" class="editButton" [ngClass]="pageMode" form="manufacturerForm" [disabled]="manufacturerForm.invalid">Save</button>
<button class="editButton" [ngClass]="pageMode" (click)="cancelEdit()">Cancel</button>
</form>
<button class="viewButton" [ngClass]="pageMode" (click)="editManufacturer()">Edit</button>
<button class="viewButton" [ngClass]="pageMode" (click)="newManufacturer()">New</button>
</div>
export class Manufacturer {
constructor (
public manufacturerId: number,
public name: string,
public accountNumber: string,
public addressLine1: string,
public addressLine2: string,
public addressCity: string,
public addressPostal: string,
public addressState: string,
public phoneNumber: string,
public faxNumber: string,
public creditLimit: number,
public dateModified: Date,
public dateCreated: Date
){}
}
import {Manufacturer} from './manufacturer';
import {MANUFACTURER} from './Manufacturer-mock';
import {Injectable} from '@angular/core';
@Injectable()
export class ManufacturerService {
getManufacturerAsync(): Promise<Manufacturer> {
return Promise.resolve(MANUFACTURER);
}
getManufacturer() : Manufacturer {
return MANUFACTURER;
}
}
import {Manufacturer} from './manufacturer';
export var MANUFACTURER: Manufacturer =
{
manufacturerId:10000,
name:'Giro',
accountNumber:'4513A56',
addressLine1:'12326 Parkway Drive',
addressLine2:'',
addressCity:'Santa Cruz',
addressPostal:'84623',
addressState:'CA',
phoneNumber:'18004562335',
faxNumber:'9724513262',
creditLimit:40000.00,
dateModified: new Date('6/15/2016 9:03 AM'),
dateCreated: new Date('3/1/2016 3:00 PM')
};
import {Injectable} from '@angular/core';
@Injectable()
export class LookupService {
getStates(): string[] {
return ['IA', 'CA', 'MN','SD','NY','FL', 'AK'];
}
}
form label {
width: 150px;
padding-right: 20px;
display: inline-block;
text-align:right;
font-weight:bold;
}
form input[type=text] {
width: 250px;
display: inline-block;
}
form input.viewMode {
display: none;
}
form input.editMode {
display:auto;
}
form select.viewMode {
display:none;
}
form select.editMode {
display:auto;
}
form .displayValue.viewMode{
display:auto;
}
form .displayValue.editMode {
display:none;
}
.error {
color:red;
}
.viewButton.viewMode {
display:auto;
}
.viewButton.editMode {
display:none;
}
.editButton.viewMode {
display:none;
}
.editButton.editMode {
display:auto;
}
import {Pipe, PipeTransform} from '@angular/core';
// sample filter via: http://stackoverflow.com/a/12728924/24892
@Pipe({name: 'phonenumber'})
export class PhoneNumberPipe implements PipeTransform {
transform(telephone: string): string {
if (!telephone)
return '';
var value = telephone.toString().trim().replace(/^\+/, '');
if (value.match(/[^0-9]/)) {
return telephone;
}
var country, city, number;
switch (value.length) {
case 10: // +1PPP####### -> C (PPP) ###-####
country = 1;
city = value.slice(0, 3);
number = value.slice(3);
break;
case 11: // +CPPP####### -> CCC (PP) ###-####
country = value[0];
city = value.slice(1, 4);
number = value.slice(4);
break;
case 12: // +CCCPP####### -> CCC (PP) ###-####
country = value.slice(0, 3);
city = value.slice(3, 5);
number = value.slice(5);
break;
default:
return telephone;
}
if (country == 1) {
country = "";
}
number = number.slice(0, 3) + '-' + number.slice(3);
return (country + " (" + city + ") " + number).trim();
}
}