<!DOCTYPE html>
<html lang="en">
<head>
<title>Angular2: Using contentchildren to display messages</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://npmcdn.com/es6-shim@0.35.0/es6-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>
<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>
### Recreating ng-messages using contentchildren
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
//map tells the System loader where to look for things
map: {
app: "./src"
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
}
}
});
//main entry point
import {bootstrap} from '@angular/platform-browser-dynamic';
import {App} from './app';
bootstrap(App, [])
.catch(err => console.error(err));
//our root app component
import {Component} from '@angular/core'
import {SomeForm} from './formdemo.component'
@Component({
selector: 'my-app',
providers: [],
template: `
<div class="well" style="height:230px">
<some-form></some-form>
</div>
`,
directives: [SomeForm]
})
export class App {
constructor() {
}
}
import {Component, OnInit} from "@angular/core";
import {FormBuilder, AbstractControl, FORM_DIRECTIVES, ControlGroup, Validators} from "@angular/common";
import {ExtendedInput} from "./extended-input.component"
import {InputError} from "./input-error.component";
@Component({
selector: 'some-form',
template: `
<extended-input [labelText]="'Some number'"
[isError]="!someNumber.valid">
<input class="form-control"
[ngFormControl]="someNumber">
<input-errors>
<input-error class="help-block"
*ngIf="someNumber.hasError('required')">
A number is required
</input-error>
<input-error class="help-block" *
*ngIf="someNumber.hasError('divisibleByTen')">
The number should be divisible by 10
</input-error>
<input-error class="help-block"
*ngIf="someNumber.hasError('minlength')">
The number should be at least 7 digits
</input-error>
</input-errors>
</extended-input>
`
directives: [FORM_DIRECTIVES,ExtendedInput,InputError]
})
export class SomeForm implements OnInit {
someFormHandle:ControlGroup;
someNumber:AbstractControl;
constructor(private formBuilder:FormBuilder) {
}
divisibleByTen(control:Control) {
return parseInt(control.value) % 10 == 0 ? null : {
divisibleByTen: true
}
}
ngOnInit():void {
this.someFormHandle = this.formBuilder.group({
'someNumber': ['', Validators.compose([Validators.required,
Validators.minLength(7),
this.divisibleByTen])]
});
this.someNumber = this.someFormHandle.find('someNumber');
}
}
import {Component} from "@angular/core";
import {CORE_DIRECTIVES} from "@angular/common";
@Component({
selector: 'input-error',
template: `<span *ngIf="showErrorFlag" class="help-block">
<ng-content></ng-content>
</span>
`,
directives: [CORE_DIRECTIVES]
})
export class InputError {
showErrorFlag:boolean = true;
hideError():void {
this.showErrorFlag = false;
}
showError():void {
this.showErrorFlag = true;
}
}
import {Component, Input, ContentChildren, QueryList, DoCheck} from "@angular/core";
import {CORE_DIRECTIVES} from "@angular/common";
import {InputError} from "./input-error.component";
@Component({
selector: 'extended-input',
template: `<div class="form-group"
[ngClass]="{'has-error':isError}">
<label class="control-label">{{labelText}}
<ng-content select="input"></ng-content>
</label>
<ng-content select="input-errors"></ng-content>
</div>
`,
directives: [CORE_DIRECTIVES, InputError]
})
export class ExtendedInput {
@Input()
labelText:string = '';
@Input()
isError:boolean = false;
@ContentChildren(InputError)
errors:QueryList<InputError>;
ngDoCheck():void {
if (this.errors) {
this.errors.toArray().forEach(
(error:QaInputError, i:number) => {
if (i == 0) {
error.showError();
} else {
error.hideError();
}
});
}
}
}
(function(global) {
var ngVer = '@2.0.0-rc.1';
var map = {
'app': 'src',
'@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
'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.8.10/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': { defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'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.forEach(function(pkgName) {
// Bundled (~40 requests):
packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
});
var config = {
transpiler: 'typescript',
typescriptOptions: {
emitDecoratorMetadata: true
},
map: map,
packages: packages
}
System.config(config);
})(this);