angular-jsf-components
Version:
JSF Compliant Angular Components
201 lines (168 loc) • 5.61 kB
text/typescript
import {ContentChildren, ElementRef, EventEmitter, Input, Output, QueryList,} from '@angular/core';
import {ControlValueAccessor, NgModel} from '@angular/forms';
import {isNull, isUndefined} from 'lodash';
import {FValidateRegexComponent} from '../components/f-validate-regex/f-validate-regex.component';
import {ValidationResponse} from '../objects/validation-response';
import {HFormService} from '../services/h-form.service';
import {MessageService} from '../services/message.service';
import {JsfOutput} from './jsf-output';
export abstract class JsfInput extends JsfOutput implements ControlValueAccessor {
validatorMessage: string;
converterMessage: string;
requiredMessage: string;
required: boolean;
disabled: boolean;
maxLength: number;
validator: (elem: JsfInput) => Promise<ValidationResponse>;
onchange = new EventEmitter<any>();
protected abstract model: NgModel;
private regexValidators: QueryList<FValidateRegexComponent>;
private changeListener = [];
private touchListener = [];
private converterError = false;
constructor(hFromService: HFormService, private messageService: MessageService,
elementRef: ElementRef) {
super(hFromService, elementRef);
}
// same as in JsfOutput but for some reason angular won't use the getter
get value(): any {
if (this.converter && this.innerValue) {
return this.converter.transform(this.innerValue);
}
return this.innerValue;
}
set value(value: any) {
if (this.innerValue !== value) {
// converter given? then convert
if (this.converter) {
try {
this.innerValue = this.converter.transformToObject(value);
this.converterError = false;
} catch (e) {
// we have to save the value anyway, otherwise the input of the user is deleted
this.innerValue = value;
this.converterError = true;
}
} else {
this.innerValue = value;
}
this.changeListener.forEach(f => f(this.innerValue));
}
}
async jsfOnRender(): Promise<void> {
try {
await super.jsfOnRender();
await this.validate();
} catch (e) {
console.error(e);
}
}
/**
*
* add more events if required
* don't forget to use them in the actual .html File
*
*/
async callAjax(event: string): Promise<void> {
for (const ajax of this.ajax.toArray()) {
if (ajax.event === event) {
// if input is invalid and ajax is not set to immediate exec
// prevent call
let valid = true;
if (!ajax.immediate) {
valid = await this.validate();
}
if (valid) {
ajax.call(this);
}
}
}
}
async onBlur() {
try {
await this.validate();
await this.callAjax('blur');
} catch (e) {
console.error(e);
}
}
async onChange() {
this.onchange.emit(this.innerValue);
try {
await this.callAjax('change');
} catch (e) {
console.error(e);
}
}
/**
*
* @returns {Promise<boolean>} true when input ok
*/
async validate(): Promise<boolean> {
let valid = true;
let message = '';
// check for converter error
if (this.converterError) {
valid = false;
message = this.converterMessage;
}
// check for regex validators
for (const validator of this.regexValidators.toArray()) {
if (!validator.validate(this.innerValue)) {
valid = false;
message = this.validatorMessage;
}
}
// check for required
if ((isNull(this.innerValue) || isUndefined(this.innerValue) ||
this.innerValue === '') &&
this.required && valid) {
valid = false;
message = this.requiredMessage;
}
// check for additional custom validators
if (valid && this.validator) {
const result = await this.validator(this);
if (result.error) {
valid = false;
message = result.message;
}
}
if (valid && this.maxLength && typeof this.innerValue === 'string' &&
this.innerValue.length > this.maxLength) {
valid = false;
message = `Input is too long. Maximum ${this.maxLength} characters allowed`;
}
if (valid) {
const result = await this.triggerEvent('postValidate');
valid = !result.error;
message = result.message;
}
this.messageService.submitError(this.simpleId, !valid, message);
return valid;
}
/**
* ng specific input functions
*/
touch() {
this.touchListener.forEach(f => f());
}
writeValue(value: any) {
this.innerValue = value;
}
registerOnChange(fn: (value: any) => void) {
this.changeListener.push(fn);
}
registerOnTouched(fn: () => void) {
this.touchListener.push(fn);
}
}