UNPKG

ng2-encrm-components

Version:
253 lines (213 loc) 6.76 kB
import { forwardRef, Component, HostBinding, Input, SimpleChange, ViewChild, ElementRef, EventEmitter, Output } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BooleanFieldValue } from './../annotations/field-value'; import { Observable } from 'rxjs/Observable'; const noop = () => { }; export const EN_INPUT_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EnInputComponent), multi: true }; // Invalid input type. Using one of these will throw an MdInputUnsupportedTypeError. const EN_INPUT_INVALID_INPUT_TYPE = [ 'file', 'radio', 'checkbox', ]; @Component({ selector: 'en-input', template: require('./en-input.component.html'), styles: [require('./en-input.component.scss')], providers: [EN_INPUT_CONTROL_VALUE_ACCESSOR] }) export class EnInputComponent { private _focused: boolean = false; private _value: any = ''; /** Callback registered via registerOnTouched (ControlValueAccessor) */ private _onTouchedCallback: () => void = noop; /** Callback registered via registerOnChange (ControlValueAccessor) */ private _onChangeCallback: (_: any) => void = noop; /** * Aria related inputs. */ @Input('aria-label') ariaLabel: string; @Input('aria-labelledby') ariaLabelledBy: string; @Input('aria-disabled') ariaDisabled: boolean; @Input('aria-required') ariaRequired: boolean; @Input('aria-invalid') ariaInvalid: boolean; // /** // * Content directives. // */ // @ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder; // @ContentChildren(MdHint) _hintChildren: QueryList<MdHint>; /** Readonly properties. */ get focused() { return this._focused; } get empty() { return this._value == null || this._value === ''; } get characterCount(): number { return this.empty ? 0 : ('' + this._value).length; } get inputId(): string { return `${this.id}`; } get floatingPlaceholder(): boolean { return this._focused || !this.empty; } /** * Bindings. */ @Input() align: 'start' | 'end' = 'start'; // @Input() floatingPlaceholder: boolean = true; @Input() autoComplete: string; @Input() autoCorrect: string; @Input() autoCapitalize: string; @Input() @BooleanFieldValue() autoFocus: boolean = false; @Input() @BooleanFieldValue() disabled: boolean = false; @Input() id: string = ``; @Input() list: string = null; @Input() max: string | number = null; @Input() maxLength: number = null; @Input() min: string | number = null; @Input() minLength: number = null; @Input() placeholder: string = null; @Input() @BooleanFieldValue() readOnly: boolean = false; @Input() @BooleanFieldValue() required: boolean = false; @Input() @BooleanFieldValue() spellCheck: boolean = false; @Input() step: number = null; @Input() tabIndex: number = null; @Input() type: string = 'text'; @Input() name: string = null; @Input() @BooleanFieldValue() addonRight: boolean = false; private _blurEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>(); private _focusEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>(); @Output('blur') get onBlur(): Observable<FocusEvent> { return this._blurEmitter.asObservable(); } @Output('focus') get onFocus(): Observable<FocusEvent> { return this._focusEmitter.asObservable(); } get value(): any { return this._value; }; @Input() set value(v: any) { v = this._convertValueForInputType(v); if (v !== this._value) { this._value = v; this._onChangeCallback(v); } } @Output() btnClick: EventEmitter<any> = new EventEmitter(); _btnClickHandler(): void { this.btnClick.emit({ value: this.value }); } // This is to remove the `align` property of the `md-input` itself. Otherwise HTML5 // might place it as RTL when we don't want to. We still want to use `align` as an // Input though, so we use HostBinding. @HostBinding('attr.align') get _align(): any { return null; } @ViewChild('input') _inputElement: ElementRef; /** Set focus on input */ focus() { this._inputElement.nativeElement.focus(); } _handleFocus(event: FocusEvent) { this._focused = true; this._focusEmitter.emit(event); } _handleBlur(event: FocusEvent) { this._focused = false; this._onTouchedCallback(); this._blurEmitter.emit(event); } _handleChange(event: Event) { this.value = (<HTMLInputElement>event.target).value; this._onTouchedCallback(); } _labelClickHandler() { if (!this.floatingPlaceholder) { this._inputElement.nativeElement.focus(); } } // _hasPlaceholder(): boolean { // return !!this.placeholder || this._placeholderChild != null; // } /** * Implemented as part of ControlValueAccessor. * TODO: internal */ writeValue(value: any) { this._value = value; } /** * Implemented as part of ControlValueAccessor. * TODO: internal */ registerOnChange(fn: any) { this._onChangeCallback = fn; } /** * Implemented as part of ControlValueAccessor. * TODO: internal */ registerOnTouched(fn: any) { this._onTouchedCallback = fn; } /** TODO: internal */ ngAfterContentInit() { this._validateConstraints(); // Trigger validation when the hint children change. // this._hintChildren.changes.subscribe(() => { // this._validateConstraints(); // }); } /** TODO: internal */ ngOnChanges(changes: {[key: string]: SimpleChange}) { this._validateConstraints(); } /** * Convert the value passed in to a value that is expected from the type of the md-input. * This is normally performed by the *_VALUE_ACCESSOR in forms, but since the type is bound * on our internal input it won't work locally. * @private */ private _convertValueForInputType(v: any): any { switch (this.type) { case 'number': return parseFloat(v); default: return v; } } /** * Ensure that all constraints defined by the API are validated, or throw errors otherwise. * Constraints for now: * - type attribute is not one of the forbidden types (see constant at the top). * @private */ private _validateConstraints() { if (EN_INPUT_INVALID_INPUT_TYPE.indexOf(this.type) != -1) { throw new TypeError(`${this.type} is not a valid en-input type.`); } } }