Accessible Angular Input Component
import { environment } from '../environments/environment';
export class Logger {
static log(msg: string | object): void {
if (!environment.production) {
console.log(msg);
}
}
static error(msg: string): void {
if (!environment.production) {
console.error(msg);
}
}
}
<form>
<input-field labelText="First Name">
<input inputRef id="firstName" type="text" required />
</input-field>
<button type="submit">Submit</button>
</form>
<label *ngIf="showLabel()" [for]="input.element.id" [textContent]="labelText"></label>
<ng-content></ng-content>
import { AfterContentInit, AfterViewInit, Component, ContentChild, Input } from '@angular/core';
import { InputRefDirective } from './input-ref.directive';
import { Logger } from './app-logger';
@Component({
selector: 'input-field',
templateUrl: './input-field.component.html',
})
export class InputFieldComponent implements AfterViewInit, AfterContentInit {
@Input() labelText: string;
@Input() labelHidden = false;
@ContentChild(InputRefDirective) input: InputRefDirective;
constructor() {}
ngAfterContentInit() {
if (!this.input) {
Logger.error('Input required in input-field component. If input is present, add inputRef directive to element.');
}
}
ngAfterViewInit() {
if (!this.input.element.id) {
Logger.error('An input with a unique ID is required in input-field component:');
Logger.log(this.input);
}
if (!this.labelText) {
Logger.error('labelText input required in input-field component:');
Logger.log(this.input);
}
if (this.input && this.labelText && this.labelHidden) {
this.input.element.setAttribute('aria-label', this.labelText);
}
}
showLabel() {
return this.labelText && !this.labelHidden;
}
}
import { Directive, ElementRef, HostListener, OnInit } from '@angular/core';
import { NgControl } from '@angular/forms';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Directive({
selector: '[inputRef]'
})
export class InputRefDirective implements OnInit {
element: HTMLInputElement;
focus = false;
focusSubject = new BehaviorSubject<boolean>(null);
keyUpSubject = new BehaviorSubject<KeyboardEvent>(null);
constructor( private el: ElementRef, public control: NgControl ) { }
ngOnInit() {
this.element = this.el.nativeElement;
this.focusSubject.asObservable().subscribe((value) => {
this.focus = value;
});
}
hasError() {
return this.control.errors;
}
@HostListener('focus')
handleFocus() {
this.focusSubject.next(true);
}
@HostListener('blur')
handleBlur() {
this.focusSubject.next(false);
}
@HostListener('keyup', ['$event'])
handleKeyUp(event: KeyboardEvent) {
this.keyUpSubject.next(event);
}
}