<!DOCTYPE html>
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script data-require="lodash.js@*" data-semver="4.11.1" src="https://cdn.jsdelivr.net/lodash/4.11.1/lodash.js"></script>
<script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
<script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
<script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
<script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
/**
* PLUNKER VERSION (based on systemjs.config.js in angular.io)
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function(global) {
var ngVer = '@2.0.0-rc.5'; // lock in the angular package version; do not let it float to current!
var routerVer = '@3.0.0-rc.1'; // lock router version
var formsVer = '@0.3.0'; // lock forms version
var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides
//map tells the System loader where to look for things
var map = {
'app': 'app',
'@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
'@angular/router': 'https://npmcdn.com/@angular/router' + routerVer,
'@angular/forms': 'https://npmcdn.com/@angular/forms' + formsVer,
'@angular/router-deprecated': 'https://npmcdn.com/@angular/router-deprecated' + routerDeprecatedVer,
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6',
'ts': 'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js',
'typescript': 'https://npmcdn.com/typescript@1.9.0-dev.20160409/lib/typescript.js',
};
//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'upgrade',
];
// Add map entries for each angular package
// only because we're pinning the version with `ngVer`.
ngPackageNames.forEach(function(pkgName) {
map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
});
// Add package entries for angular packages
ngPackageNames.concat(['forms', 'router', 'router-deprecated']).forEach(function(pkgName) {
// Bundled (~40 requests):
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js' };
// Individual files (~300 requests):
//packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
"exports": "ts"
}
},
map: map,
packages: packages
};
System.config(config);
})(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
}
}
/**
* Created by Eyal on 8/16/2016.
*/
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {AppComponent, NgControlStatusGroup} from "./app.component";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
@NgModule({
// components, directives & pipes
declarations:[AppComponent,NgControlStatusGroup],
bootstrap :[AppComponent],
imports :[
BrowserModule,
FormsModule,
ReactiveFormsModule
]
})
export class AppModule{}
platformBrowserDynamic()
.bootstrapModule(AppModule);
/**
* Created by Eyal on 8/16/2016.
*/
import {Component, Directive, Self} from "@angular/core";
import {
FormArray, FormControl, FormGroup, AbstractControl, ControlContainer,
AbstractControlDirective
} from "@angular/forms";
import {isPresent} from "@angular/platform-browser-dynamic/src/facade/lang";
export class AbstractControlStatus {
private _cd: AbstractControlDirective;
constructor(cd: AbstractControlDirective) { this._cd = cd; }
get ngClassUntouched(): boolean {
return isPresent(this._cd.control) ? this._cd.control.untouched : false;
}
get ngClassTouched(): boolean {
return isPresent(this._cd.control) ? this._cd.control.touched : false;
}
get ngClassPristine(): boolean {
return isPresent(this._cd.control) ? this._cd.control.pristine : false;
}
get ngClassDirty(): boolean {
return isPresent(this._cd.control) ? this._cd.control.dirty : false;
}
get ngClassValid(): boolean {
return isPresent(this._cd.control) ? this._cd.control.valid : false;
}
get ngClassInvalid(): boolean {
return isPresent(this._cd.control) ? this._cd.control.invalid : false;
}
}
@Directive({
selector:
'[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]',
host: {
'[class.ng-untouched]': 'ngClassUntouched',
'[class.ng-touched]': 'ngClassTouched',
'[class.ng-pristine]': 'ngClassPristine',
'[class.ng-dirty]': 'ngClassDirty',
'[class.ng-valid]': 'ngClassValid',
'[class.ng-invalid]': 'ngClassInvalid'
}
})
export class NgControlStatusGroup extends AbstractControlStatus {
constructor(@Self() cd: ControlContainer) { super(cd); }
}
@Component({
selector: 'my-app',
directives:[],
styles: [
`
.box{
border: 1px solid black;
}
input.ng-invalid {
background-color: #FA787E;
}
input.ng-valid {
background-color: #78FA89;
}
.ng-invalid{
border: 2px solid red;
padding: 5px;
}
.left{
text-align: left;
}
li{
margin: 3px;
}
`
],
template: `
<div>
<h3> Custom Group Validation</h3>
<p>
This custom validation (distinct) check the you don't add the same name twice.
</p>
<form [formGroup]="myForm">
name : <input type="text" formControlName="inputName" >
<button (click)="add()">Add</button><br>
<hr>
<div class="left">
names: <br>
<ul formArrayName="names">
<li *ngFor="let item of namesArray.controls; let i = index">
<input type="text" [formControlName]="i">
<button (click)="removeAt(i)">X</button><br>
</li>
</ul>
<div *ngIf="namesArray.hasError('duplicate')">
duplicate entries
</div>
</div>
</form>
</div>
`
})
export class AppComponent {
add(){
this.namesArray.push(
new FormControl(this.myForm.get('inputName').value)
);
}
removeAt(i:number){
this.namesArray.removeAt(i);
}
namesArray:FormArray = new FormArray([], this.customGroupValidation );
myForm:FormGroup = new FormGroup({
inputName: new FormControl(''),
names: this.namesArray
});
customGroupValidation (formArray) {
let isError = false;
var result = _.groupBy( formArray.controls , c => c.value );
for (let prop in result) {
if (result[prop].length > 1) {
isError = true;
_.forEach(result[prop], function (item) {
item._status = "INVALID";
//item._errors['eyal']={'duplicate':true};
//item.ctrl.$setValidity('distinct', false);
});
} else {
result[prop][0]._status = 'VALID';
//result[prop][0].ctrl.$setValidity('distinct', true);
}
}
if(isError){ return {'duplicate':'duplicate entries'}}
}
}