@ -1,10 +0,0 @@ | |||
import { Component, Input } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-page-heading', | |||
template: `<h4>{{text}}</h4>` | |||
}) | |||
export class PageHeadingComponent { | |||
@Input() text: string; | |||
constructor() { } | |||
} |
@ -1,41 +0,0 @@ | |||
import { | |||
fakeAsync, | |||
tick, | |||
TestBed | |||
} from '@angular/core/testing'; | |||
import { Component } from '@angular/core'; | |||
import { By } from '@angular/platform-browser/src/dom/debug/by'; | |||
// Load the implementations that should be tested | |||
import { XLargeDirective } from './x-large.directive'; | |||
describe('x-large directive', () => { | |||
// Create a test component to test directives | |||
@Component({ | |||
template: '<div x-large>Content</div>' | |||
}) | |||
class TestComponent { } | |||
beforeEach(() => { | |||
TestBed.configureTestingModule({ | |||
declarations: [ | |||
XLargeDirective, | |||
TestComponent | |||
] | |||
}); | |||
}); | |||
it('should sent font-size to x-large', fakeAsync(() => { | |||
TestBed.compileComponents().then(() => { | |||
const fixture = TestBed.createComponent(TestComponent); | |||
fixture.detectChanges(); | |||
tick(); | |||
const element = fixture.debugElement.query(By.css('div')); | |||
// expect(element.nativeElement.style.fontSize).toBe('x-large'); | |||
}); | |||
})); | |||
}); |
@ -1,18 +0,0 @@ | |||
import { Directive, ElementRef, Renderer } from '@angular/core'; | |||
/* | |||
* Directive | |||
* XLarge is a simple directive to show how one is made | |||
*/ | |||
@Directive({ | |||
selector: '[appdXlarge]' // using [ ] means selecting attributes | |||
}) | |||
export class XLargeDirective { | |||
constructor(element: ElementRef, renderer: Renderer) { | |||
// simple DOM manipulation to set font size to x-large | |||
// `nativeElement` is the direct reference to the DOM element | |||
// element.nativeElement.style.fontSize = 'x-large'; | |||
// for server/webworker support use the renderer | |||
renderer.setElementStyle(element.nativeElement, 'fontSize', 'x-large'); | |||
} | |||
} |
@ -1,36 +0,0 @@ | |||
export class ControlBase<T>{ | |||
value: T; | |||
key: string; | |||
label: string; | |||
placeholder: string; | |||
required: boolean; | |||
minlength: number; | |||
maxlength: number; | |||
order: number; | |||
type: string; | |||
class: string; | |||
constructor(options: { | |||
value?: T, | |||
key?: string, | |||
label?: string, | |||
placeholder?: string, | |||
required?: boolean, | |||
minlength?: number, | |||
maxlength?: number, | |||
order?: number, | |||
type?: string, | |||
class?: string; | |||
} = {}) { | |||
this.value = options.value; | |||
this.key = options.key || ''; | |||
this.label = options.label || ''; | |||
this.placeholder = options.placeholder || ''; | |||
this.required = !!options.required; | |||
this.minlength = options.minlength; | |||
this.maxlength = options.maxlength; | |||
this.order = options.order === undefined ? 1 : options.order; | |||
this.type = options.type || ''; | |||
this.class = options.class || ''; | |||
} | |||
} |
@ -1,11 +0,0 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlCheckbox extends ControlBase<string> { | |||
type: string; | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = 'checkbox'; | |||
this.value = options.value || false; | |||
} | |||
} |
@ -1,11 +0,0 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlDropdown extends ControlBase<string> { | |||
options: { key: string, value: string }[] = []; | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = 'dropdown'; | |||
this.options = options.options || []; | |||
} | |||
} |
@ -1,8 +0,0 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlTextbox extends ControlBase<boolean> { | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = options.type || 'textbox'; | |||
} | |||
} |
@ -1,26 +0,0 @@ | |||
<!--{{f.controls[control.key] | json}}--> | |||
<div #f="ngForm" [formGroup]="form" [ngSwitch]="control.type" class="form-group {{control.class}}" [class.has-danger]="invalid" | |||
[class.has-success]="valid" [class.form-check]="control.type === 'checkbox'"> | |||
<label *ngSwitchCase="'dropdown'" [attr.for]="control.key" class="col-form-label">{{control.label}}</label> | |||
<select *ngSwitchCase="'dropdown'" [id]="control.key" [formControlName]="control.key" [class.form-control-success]="valid" | |||
[class.form-control-danger]="invalid" class="form-control"> | |||
<option *ngFor="let opt of control.options" [value]="opt.key">{{opt.value}}</option> | |||
</select> | |||
<label *ngSwitchCase="'checkbox'" [attr.for]="control.key" class="form-check-label"> | |||
<input #ck *ngSwitchCase="'checkbox'" (change)="control.value = ck.checked" [id]="control.key" [formControlName]="control.key" | |||
[type]="control.type" class="form-check-input"> | |||
{{control.label}} | |||
</label> | |||
<!--This is not the switch case because of multiple control types--> | |||
<label *ngIf="control.type === 'textbox' || control.type === 'email' || control.type === 'password'" [attr.for]="control.key" class="col-form-label">{{control.label}}</label> | |||
<input *ngIf="control.type === 'textbox' || control.type === 'email' || control.type === 'password'" [id]="control.key" [formControlName]="control.key" [type]="control.type" | |||
[placeholder]="control.placeholder" [class.form-control-success]="valid" [class.form-control-danger]="invalid" class="form-control"> | |||
<appc-control-error-message [form]="f" [control]="control"></appc-control-error-message> | |||
</div> |
@ -1,26 +0,0 @@ | |||
import { Component, Input } from '@angular/core'; | |||
import { FormGroup } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ErrorMessageComponent } from './error-message.component'; | |||
@Component({ | |||
selector: 'appc-dynamic-control', | |||
templateUrl: './dynamic-form-control.component.html' | |||
}) | |||
export class DynamicFormControlComponent { | |||
@Input() control; | |||
@Input() form; | |||
constructor() { | |||
this.control = undefined; | |||
this.form = undefined; | |||
} | |||
get valid() { | |||
return this.form.controls[this.control.key].valid; | |||
} | |||
get invalid() { | |||
return !this.form.controls[this.control.key].valid && this.form.controls[this.control.key].touched; | |||
} | |||
} |
@ -1,5 +0,0 @@ | |||
<form class="{{formClass}}" (ngSubmit)="onSubmit()" [formGroup]="form" novalidate role="form"> | |||
<appc-dynamic-control *ngFor="let ctrl of controls" [control]="ctrl" [form]="form"></appc-dynamic-control> | |||
<button type="submit" class="btn btn-primary pull-right" [disabled]="!form.valid">{{btnText}}</button> | |||
</form> |
@ -1,30 +0,0 @@ | |||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; | |||
import { FormGroup } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { FormControlService } from './form-control.service'; | |||
@Component({ | |||
selector: 'appc-dynamic-form', | |||
templateUrl: './dynamic-form.component.html' | |||
}) | |||
export class DynamicFormComponent implements OnInit { | |||
@Input() controls: ControlBase<any>[] = []; | |||
@Input() btnText: string = 'Submit'; // Default value at least | |||
@Input() formClass: string = 'form-horizontal'; | |||
// Note: don't keep name of output events as same as native events such as submit etc. | |||
@Output() formsubmit: EventEmitter<any> = new EventEmitter<any>(); | |||
form: FormGroup; | |||
constructor(private _controlService: FormControlService) { } | |||
ngOnInit() { | |||
let sortedControls = this.controls.sort((a, b) => a.order - b.order); | |||
this.form = this._controlService.toControlGroup(sortedControls); | |||
} | |||
onSubmit() { | |||
this.formsubmit.emit(this.form.value); | |||
} | |||
} |
@ -1,25 +0,0 @@ | |||
import { Component, Host, Input } from '@angular/core'; | |||
import { FormGroupDirective } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ValidationService } from './validation.service'; | |||
@Component({ | |||
selector: 'appc-control-error-message', | |||
template: `<div *ngIf="errorMessage" class="form-control-feedback"> {{errorMessage}} </div>` | |||
}) | |||
export class ErrorMessageComponent { | |||
@Input() control: ControlBase<any>; | |||
@Input() form: FormGroupDirective; | |||
constructor() { } | |||
get errorMessage() { | |||
let c = this.form.form.get(this.control.key); | |||
for (let propertyName in c.errors) { | |||
if (c.errors.hasOwnProperty(propertyName) && c.touched) { | |||
return ValidationService.getValidatorErrorMessage(propertyName, this.control.minlength || this.control.maxlength); | |||
} | |||
} | |||
return undefined; | |||
} | |||
} |
@ -1,7 +0,0 @@ | |||
<div class="alert alert-danger" *ngIf="errors?.length > 0"> | |||
<ul> | |||
<li *ngFor="let error of errors"> | |||
{{error}} | |||
</li> | |||
</ul> | |||
</div> |
@ -1,11 +0,0 @@ | |||
import { Component, Input } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-error-summary', | |||
templateUrl: './error-summary.component.html' | |||
}) | |||
export class ErrorSummaryComponent { | |||
@Input() errors: string | string[]; | |||
constructor() { } | |||
} |
@ -1,41 +0,0 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { FormControl, FormGroup, Validators } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ValidationService } from './validation.service'; | |||
@Injectable() | |||
export class FormControlService { | |||
constructor() { } | |||
toControlGroup(controls: ControlBase<any>[]) { | |||
let group: any = {}; | |||
controls.forEach(control => { | |||
let validators = []; | |||
// Required | |||
if (control.required) { | |||
validators.push(Validators.required); | |||
} | |||
// Minlength | |||
if (control.minlength) { | |||
validators.push(Validators.minLength(control.minlength)); | |||
} | |||
// Maxlength | |||
if (control.maxlength) { | |||
validators.push(Validators.minLength(control.maxlength)); | |||
} | |||
if (control.type === 'email') { | |||
validators.push(ValidationService.emailValidator); | |||
} | |||
// Password | |||
if (control.type === 'password') { | |||
validators.push(ValidationService.passwordValidator); | |||
} | |||
group[control.key] = new FormControl(control.value || '', validators); | |||
}); | |||
return new FormGroup(group); | |||
} | |||
} |
@ -1,42 +0,0 @@ | |||
export class ValidationService { | |||
static getValidatorErrorMessage(code: string, fieldLength: number) { | |||
let config: any = { | |||
'required': 'This is a required field', | |||
'minlength': 'Minimum length is ' + fieldLength, | |||
'maxlength': 'Maximum length is ' + fieldLength, | |||
'invalidCreditCard': 'Invalid credit card number', | |||
'invalidEmailAddress': 'Invalid email address', | |||
'invalidPassword': 'Password must be at least 6 characters long, and contain a number and special character.' | |||
}; | |||
return config[code]; | |||
} | |||
static creditCardValidator(control: any) { | |||
// Visa, MasterCard, American Express, Diners Club, Discover, JCB | |||
if (control.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidCreditCard': true }; | |||
} | |||
} | |||
static emailValidator(control: any) { | |||
// RFC 2822 compliant regex | |||
if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidEmailAddress': true }; | |||
} | |||
} | |||
static passwordValidator(control: any) { | |||
// {6,100} - Assert password is between 6 and 100 characters | |||
// (?=.*[0-9]) - Assert a string has at least one number | |||
if (control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!"@#$%^&*]{6,100}$/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidPassword': true }; | |||
} | |||
} | |||
} |
@ -1,25 +0,0 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
@Injectable() | |||
export class UtilityService { | |||
private _router: Router; | |||
constructor(router: Router) { | |||
this._router = router; | |||
} | |||
convertDateTime(date: Date) { | |||
let _formattedDate = new Date(date.toString()); | |||
return _formattedDate.toDateString(); | |||
} | |||
navigate(path: string) { | |||
this._router.navigate([path]); | |||
} | |||
navigateToSignIn() { | |||
this.navigate('/login'); | |||
} | |||
} |