lc-forms
Version:
Is a library for simplified object-based creation and validation of forms in Angular/Angular Universal. The library is inspired by [angular.io dynamic forms](https://angular.io/docs/ts/latest/cookbook/dynamic-form.html) and [Filip Lauc's](https://github.c
432 lines (419 loc) • 15.6 kB
JavaScript
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, HostBinding, Injectable, Input, NgModule, Output } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
class LCCustomValidators {
/**
* @param {?} key
* @return {?}
*/
static match(key) {
return (control) => {
if (control.value && control.root.controls) {
return control.root.controls[key].value !== control.value ?
{ 'match': { 'currentValue': control.value, 'requiredValue': control.root.controls[key].value, 'mustMatchField': key }
} : null;
}
else {
return null;
}
};
}
}
class LCFormService {
/**
* @param {?} elements
* @return {?}
*/
create(elements) {
const /** @type {?} */ temp = {}, /** @type {?} */ toReturn = {}, /** @type {?} */ matches = [];
elements.forEach(e => {
const /** @type {?} */ val = e.value || '';
let /** @type {?} */ validators = null;
if (e.validation) {
if (Array.isArray(e.validation)) {
validators = [];
e.validation.forEach(i => validators.push(setValidator(i, e)));
}
else {
validators = setValidator(e.validation);
}
}
temp[e.key] = new FormControl(val, validators);
});
toReturn['fbGroup'] = new FormGroup(temp);
// Add matches for watching if required
if (matches.length) {
toReturn['matches'] = matches;
}
return toReturn;
/**
* @param {?} item
* @param {?=} original
* @return {?}
*/
function setValidator(item, original) {
switch (item.type) {
case 'required': return Validators.required;
case 'minLength': return Validators.minLength(item.value);
case 'maxLength': return Validators.maxLength(item.value);
case 'pattern': return Validators.pattern(item.value);
case 'match':
matches.push({ toMatch: item.value, model: original.key });
return LCCustomValidators.match(item.value);
}
}
}
}
LCFormService.decorators = [
{ type: Injectable },
];
/**
* @nocollapse
*/
LCFormService.ctorParameters = () => [];
class LCFormComponent {
/**
* @param {?} _controlGroup
*/
constructor(_controlGroup) {
this._controlGroup = _controlGroup;
// Outputs
this.submitted = new EventEmitter();
this.changed = new EventEmitter();
}
/**
* @param {?} value
* @return {?}
*/
set lcFormData(value) {
this._data = value;
this._data.settings = this._setSettings(value.settings);
this.sortElements();
const /** @type {?} */ cg = this._controlGroup.create(this._data.elements);
this._form = cg.fbGroup;
this._matches = cg.matches;
this.comp = {
data: this._data,
form: this._form,
settings: {
singleErrorMessage: this._data.settings.singleErrorMessage,
errorOnDirty: this._data.settings.errorOnDirty,
showValidation: this._data.settings.showValidation,
extraValidation: this._data.settings.submitButtonExtraValidation || true
}
};
}
/**
* @return {?}
*/
submit() { this.submitted.emit(this._form.value); }
/**
* @param {?} event
* @return {?}
*/
onElementValueChange(event) {
if (this._matches) {
// console.log('this._matches: ', this._matches);
const /** @type {?} */ key = Object.keys(event)[0], /** @type {?} */
// See if we should check for matches
mat = this._matches[0].find(a => a.toMatch === key);
// Update the cg if we found a matcher
if (mat) {
this._form.controls[mat.model].updateValueAndValidity();
}
}
this.changed.emit(event);
}
/**
* @return {?}
*/
sortElements() { this._data.elements.sort((a, b) => a.order - b.order); }
/**
* @param {?} settings
* @return {?}
*/
_setSettings(settings) {
const /** @type {?} */ defaultSettings = {
submitButton: true,
submitButtonText: 'Submit',
submitButtonExtraValidation: null,
noteText: null,
noteLabel: null,
showValidation: true,
singleErrorMessage: true,
errorOnDirty: true
};
// Add received settings
if (settings) {
for (const /** @type {?} */ p in defaultSettings) {
if (settings.hasOwnProperty(p)) {
defaultSettings[p] = settings[p];
}
else {
defaultSettings[p] = defaultSettings[p];
}
}
}
return defaultSettings;
}
}
LCFormComponent.decorators = [
{ type: Component, args: [{
template: `
<form [formGroup]="comp.form" [ngClass]="comp.data.classes?.form">
<lc-element *ngFor="let e of comp.data.elements" [info]="{element: e, form: comp.form, settings: comp.settings, classes: comp.data.classes}" (valueChange)="onElementValueChange($event)"></lc-element>
<div class="row">
<div *ngIf="comp.data.settings.submitButton" [ngClass]="comp.data.classes?.submit">
<button type="submit" [disabled]="!comp.form.valid && comp.settings.extraValidation" [ngClass]="comp.data.classes?.submitButton">{{comp.data.settings.submitButtonText}}</button>
</div>
<div *ngIf="comp.data.settings.noteText" [ngClass]="comp.data.classes.note">
<span *ngIf="comp.data.settings.noteLabel" [ngClass]="comp.data.classes.noteLabel">{{comp.data.settings.noteLabel}}</span>
{{comp.data.settings.noteText}}
</div>
</div>
</form>
`,
// tslint:disable-next-line:component-selector
selector: 'lc-form'
},] },
];
/**
* @nocollapse
*/
LCFormComponent.ctorParameters = () => [
{ type: LCFormService, },
];
LCFormComponent.propDecorators = {
'lcFormData': [{ type: Input },],
'submitted': [{ type: Output },],
'changed': [{ type: Output },],
};
class LCElementComponent {
constructor() {
this.valueChange = new EventEmitter();
this.checkboxIsRequired = false;
}
/**
* @return {?}
*/
get toSet() {
return this.element && this.element.classes && this.element.classes.wrapper ? this.element.classes.wrapper : '';
}
/**
* @param {?} value
* @return {?}
*/
set info(value) {
this.element = value.element;
this.form = value.form;
this.settings = value.settings;
this.classes = value.classes;
if (this.element.type === 'checkbox') {
this.element.value = !this.element.value ? [] : this.element.value;
this.checkboxIsRequired = this.element.validation && this.element.validation[0].type === 'required';
}
if (this.classes.errors) {
if (!this.element.classes.error) {
this.element.classes.error = this.classes.errors;
}
else {
this.element.classes.error = this.element.classes.error.concat(this.classes.errors);
}
}
}
/**
* @return {?}
*/
get showErrorMsg() {
return this.settings.errorOnDirty ?
!this.form.controls[this.element.key].valid && !this.form.controls[this.element.key].dirty :
!this.form.controls[this.element.key].valid;
}
/**
* @return {?}
*/
errors() {
if (this.element.validation && !this.form.controls[this.element.key].valid) {
const /** @type {?} */ temp = [], /** @type {?} */ errors = this.form.controls[this.element.key].errors, /** @type {?} */ errorKeys = Object.keys(errors);
if (this.settings.singleErrorMessage) {
temp.push(this._setError(errorKeys[errorKeys.length - 1], errors));
}
else {
errorKeys.forEach(a => temp.push(this._setError(a, errors)));
}
return temp;
}
}
/**
* @param {?} option
* @return {?}
*/
setRadio(option) {
this.form.controls[this.element.key].setValue(option.value);
this.onValueChange(option.value);
}
/**
* @param {?} option
* @return {?}
*/
setCheckbox(option) {
const /** @type {?} */ index = this.element.value.indexOf(option.value);
if (index !== -1) {
this.element.value.splice(index, 1);
}
else {
this.element.value.push(option.value);
}
this.form.controls[this.element.key].setValue(this.element.value);
this.onValueChange(this.element.value);
}
/**
* @return {?}
*/
chackboxValueChange() {
if (this.checkboxIsRequired) {
if (this.element.value.length === 1) {
this.element.options.find(a => a.value === this.element.value[0]).disabled = true;
}
else {
this.element.options.forEach(a => a.disabled = false);
}
}
}
/**
* @param {?} event
* @return {?}
*/
onValueChange(event) {
if (this.element.emitChanges !== false) {
this.valueChange.emit({ [this.element.key]: event });
}
}
/**
* @param {?} option
* @return {?}
*/
isSelectActive(option) { return this.element.value.find(a => a === option.value) ? true : false; }
/**
* @param {?} item
* @param {?} errors
* @return {?}
*/
_setError(item, errors) {
let /** @type {?} */ errorMsg = this.element.validation.find(a => a.type.toLowerCase() === item).message;
const /** @type {?} */ tag = this.element.label || this.element.key;
if (!errorMsg) {
switch (item) {
// Set error messages
case 'required':
errorMsg = `${tag} is required.`;
break;
case 'minlength':
errorMsg = `${tag} has to be at least ${errors[item].requiredLength} characters long.`;
break;
case 'maxlength':
errorMsg = `${tag} can't be longer then ${errors[item].requiredLength} characters.`;
break;
case 'pattern':
errorMsg = `${tag} must match this pattern: ${errors[item].requiredPattern}.`;
break;
case 'match':
errorMsg = `${tag} must match the ${errors[item].mustMatchField} field.`;
break;
}
}
return errorMsg;
}
}
LCElementComponent.decorators = [
{ type: Component, args: [{
// tslint:disable-next-line:component-selector
selector: 'lc-element',
template: `
<div [formGroup]="form" [ngClass]="element.classes?.group">
<label *ngIf="element.label"
[ngClass]="element.classes?.label"
[attr.for]="element.key">
{{element.label}}
</label>
<div [ngSwitch]="element.type">
<select *ngSwitchCase="'dropdown'"
[formControlName]="element.key"
(ngModelChange)="onValueChange($event)"
[ngClass]="element.classes?.element"
[id]="element.key">
<option *ngFor="let o of element.options" [value]="o.value">{{o.name ? o.name : o.value}}</option>
</select>
<div *ngSwitchCase="'checkbox'" [ngClass]="element.classes?.element">
<div class="checkbox" *ngFor="let o of element.options">
<input [type]="element.type"
[formControlName]="element.key"
[name]="element.key"
[value]="o.value"
[checked]="isSelectActive(o)"
(change)="chackboxValueChange()"
(click)="setCheckbox(o)">
<span>{{o.name ? o.name : o.value}}</span>
</div>
</div>
<textarea *ngSwitchCase="'textarea'"
[formControlName]="element.key"
(ngModelChange)="onValueChange($event)"
[id]="element.key"
[attr.placeholder]="element.placeholder"
[attr.rows]="element.rows"
[ngClass]="element.classes?.element"></textarea>
<div *ngSwitchCase="'radio'" [ngClass]="element.classes?.element">
<div class="radio" *ngFor="let o of element.options">
<input [type]="element.type"
[formControlName]="element.key"
[name]="element.key"
[value]="o.value"
[checked]="element.value === o.value"
(click)="setRadio(o)">
<span>{{o.name ? o.name : o.value}}</span>
</div>
</div>
<input *ngSwitchDefault
[formControlName]="element.key"
[attr.placeholder]="element.placeholder"
[type]="element.type"
(ngModelChange)="onValueChange($event)"
[ngClass]="element.classes?.element"
[id]="element.key">
</div>
<div *ngIf="settings.showValidation" [hidden]="showErrorMsg">
<span *ngFor="let e of errors()" [ngClass]="element.classes?.error">{{e}}</span>
</div>
</div>
`
},] },
];
/**
* @nocollapse
*/
LCElementComponent.ctorParameters = () => [];
LCElementComponent.propDecorators = {
'toSet': [{ type: HostBinding, args: ['class',] },],
'info': [{ type: Input },],
'valueChange': [{ type: Output },],
};
class LCFormsModule {
}
LCFormsModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule, ReactiveFormsModule],
declarations: [LCFormComponent, LCElementComponent],
exports: [LCFormComponent, LCElementComponent],
providers: [LCFormService]
},] },
];
/**
* @nocollapse
*/
LCFormsModule.ctorParameters = () => [];
/**
* Generated bundle index. Do not edit.
*/
export { LCFormsModule, LCElementComponent as ɵc, LCFormComponent as ɵa, LCFormService as ɵb };
//# sourceMappingURL=lc-forms.js.map