ngx-mat-dynamic-form-builder
Version:
Build dynamic forms in Angular Material using Reactive forms.
793 lines (766 loc) • 40.2 kB
JavaScript
import { ɵɵdefineInjectable, Injectable, Component, EventEmitter, Input, Output, ViewChild, NgModule } from '@angular/core';
import { FormControl, Validators, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { startWith, map, tap } from 'rxjs/operators';
import { isString } from 'lodash';
import { ENTER, COMMA } from '@angular/cdk/keycodes';
import { DateAdapter } from '@angular/material/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatBadgeModule } from '@angular/material/badge';
class NgxMatDynamicFormBuilderService {
constructor() { }
}
NgxMatDynamicFormBuilderService.ɵprov = ɵɵdefineInjectable({ factory: function NgxMatDynamicFormBuilderService_Factory() { return new NgxMatDynamicFormBuilderService(); }, token: NgxMatDynamicFormBuilderService, providedIn: "root" });
NgxMatDynamicFormBuilderService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
NgxMatDynamicFormBuilderService.ctorParameters = () => [];
class NgxMatDynamicFormBuilderComponent {
constructor() { }
ngOnInit() {
}
}
NgxMatDynamicFormBuilderComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-dynamic-form-builder',
template: `
<p>
ngx-mat-dynamic-form-builder works!
</p>
`
},] }
];
NgxMatDynamicFormBuilderComponent.ctorParameters = () => [];
class AutoCompleteComponent {
constructor() {
this.options = [];
this.hint = '';
this.appearance = 'standard';
this.disabled = false;
this.autoClear = false;
this.output = new EventEmitter();
}
ngOnInit() {
this.setupValidators();
}
ngOnChanges(changes) {
if (changes.options && this.options) {
if (!this.stateCtrl)
this.setupValidators();
this.filteredOptions = this.stateCtrl.valueChanges
.pipe(startWith(''), map(state => state ? this._filterOptions(state) : this.options.slice()));
if (this.defaultOption && this.defaultOptionKey) {
this.stateCtrl.setValue(this.options.find(o => o[this.defaultOptionKey] === this.defaultOption));
}
}
}
setupValidators() {
if (this.stateCtrl)
return;
if (this.validators) {
let isRequired = false;
this.validators.forEach(vd => {
if (vd.name === 'required')
isRequired = true;
});
if (isRequired) {
this.stateCtrl = new FormControl(null, [Validators.required, this.valueSelected()]);
}
else {
this.stateCtrl = new FormControl(null, [this.valueSelected()]);
}
}
else {
this.stateCtrl = new FormControl(null, [this.valueSelected()]);
}
}
displayFn(value) {
if (value)
return value[this.displayKey];
}
setValue(event) {
this.output.emit(event.option.value);
if (this.autoClear) {
this.stateCtrl.reset();
}
}
clearValue() {
this.output.emit(undefined);
this.stateCtrl.reset();
}
_filterOptions(value) {
if (isString(value) && value.length >= 1) {
const filterValue = value.toLowerCase();
return this.options.filter(state => state[this.filterKey ? this.filterKey : this.displayKey].toLowerCase().includes(filterValue));
}
return this.options;
}
valueSelected() {
return (c) => {
// if value is set and it is not an object, valid option not selected
if (c.value && typeof c.value !== 'object') {
return { match: true };
}
else {
return null;
}
};
}
}
AutoCompleteComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-auto-complete',
template: "<mat-form-field [appearance]=\"appearance\" fxFlex>\r\n <mat-label>{{label}}</mat-label>\r\n <input matInput [placeholder]=\"placeholder\" [readonly]=\"disabled\" aria-label=\"AutoComplete\" [matAutocomplete]=\"auto\"\r\n [formControl]=\"stateCtrl\">\r\n <mat-autocomplete #auto=\"matAutocomplete\" autoActiveFirstOption (optionSelected)=\"setValue($event)\"\r\n [displayWith]=\"displayFn.bind(this)\">\r\n <mat-option *ngFor=\"let option of filteredOptions | async\" [value]=\"option\">\r\n <span>{{option[displayKey]}}</span>\r\n </mat-option>\r\n </mat-autocomplete>\r\n <button mat-button *ngIf=\"stateCtrl.value && !disabled\" matSuffix mat-icon-button aria-label=\"Clear\"\r\n (click)=\"clearValue()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n <mat-hint>{{hint}}</mat-hint>\r\n <mat-error *ngIf=\"!stateCtrl.valid && stateCtrl.touched\">Select valid option</mat-error>\r\n</mat-form-field>",
styles: [""]
},] }
];
AutoCompleteComponent.ctorParameters = () => [];
AutoCompleteComponent.propDecorators = {
options: [{ type: Input }],
defaultOption: [{ type: Input }],
defaultOptionKey: [{ type: Input }],
label: [{ type: Input }],
placeholder: [{ type: Input }],
displayKey: [{ type: Input }],
filterKey: [{ type: Input }],
hint: [{ type: Input }],
appearance: [{ type: Input }],
disabled: [{ type: Input }],
autoClear: [{ type: Input }],
validators: [{ type: Input }],
output: [{ type: Output }]
};
class ChipSelectorComponent {
constructor() {
this.options = [];
this.hint = "";
this.appearance = 'standard';
this.disabled = false;
this.customChip = false;
this.output = new EventEmitter();
this.visible = true;
this.selectable = true;
this.separatorKeysCodes = [ENTER, COMMA];
this.selectedObjects = [];
}
ngOnInit() {
this.setupValidators();
}
ngOnChanges(changes) {
if (changes.options && this.options) {
if (!this.formControl)
this.setupValidators();
this.filteredObjects = this.formControl.valueChanges.pipe(startWith([]), map((obj) => {
return obj ? this._filter(obj) : this.options.slice();
}));
if (this.defaultOptions && this.defaultOptionsKey) {
this.addDefaultSelected(this.defaultOptions);
}
}
}
setupValidators() {
if (this.formControl)
return;
if (this.validators) {
let isRequired = false;
this.validators.forEach(vd => {
if (vd.name === 'required')
isRequired = true;
});
if (isRequired) {
this.formControl = new FormControl(this.selectedObjects, [Validators.required, this.valueSelected()]);
}
else {
this.formControl = new FormControl(this.selectedObjects, [this.valueSelected()]);
}
}
else if (!this.customChip) {
this.formControl = new FormControl(this.selectedObjects, [this.valueSelected()]);
}
else {
this.formControl = new FormControl();
}
}
add(event) {
// Add fruit only when MatAutocomplete is not open
// To make sure this does not conflict with OptionSelected Event
if (!this.matAutocomplete.isOpen) {
const input = event.input;
const value = event.value;
if (this.customChip && (value || '').trim()) {
this.selectedObjects.push({ [this.displayKey]: value.trim() });
this.output.emit(this.selectedObjects);
}
// Reset the input value
if (input) {
input.value = '';
}
}
this.formControl.setValue(this.selectedObjects);
this.formControl.updateValueAndValidity();
}
remove(item) {
this.updateFilteredObjects();
const index = this.selectedObjects.indexOf(item);
if (index >= 0) {
this.selectedObjects.splice(index, 1);
this.output.emit(this.selectedObjects);
}
this.formControl.updateValueAndValidity();
}
updateFilteredObjects() {
this.filteredObjects = this.filteredObjects.pipe(map(data => data.filter(obj => !this.selectedObjects.includes(obj))));
}
selected(event) {
this.selectedObjects.push(event.option.value);
this.output.emit(this.selectedObjects);
this.updateFilteredObjects();
this.formInput.nativeElement.value = '';
this.formInput.nativeElement.blur();
this.formControl.setValue(this.selectedObjects);
}
addDefaultSelected(values) {
if (!Array.isArray(values)) {
console.error('Default values not an array');
return;
}
this.selectedObjects = this.options.filter(i => values.some(t => t === i[this.defaultOptionsKey]));
this.output.emit(this.selectedObjects);
this.updateFilteredObjects();
this.formControl.setValue(this.selectedObjects);
}
_filter(value) {
if (isString(value) && value.length >= 1) {
let filterValue = value.toLowerCase();
return this.options.filter(obj => obj[this.filterKey ? this.filterKey : this.displayKey].
toLowerCase().includes(filterValue));
}
return this.options;
}
valueSelected() {
return (c) => {
// if value is set and it is not an object, valid option not selected
if (c.value && typeof c.value !== 'object') {
return { match: true };
}
else {
return null;
}
};
}
}
ChipSelectorComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-chip-selector',
template: "<mat-form-field [appearance]=\"appearance\" fxFlex>\r\n <mat-label>{{label}}</mat-label>\r\n <mat-chip-list #chipList>\r\n <mat-chip *ngFor=\"let obj of selectedObjects\" [selectable]=\"selectable\" [removable]=\"!disabled\"\r\n (removed)=\"remove(obj)\">\r\n {{obj[displayKey]}}\r\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\r\n </mat-chip>\r\n <input [placeholder]=\"placeholder\" [readonly]=\"disabled\" #formInput [matAutocomplete]=\"auto\"\r\n [matChipInputFor]=\"chipList\" [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\r\n (matChipInputTokenEnd)=\"add($event)\" [formControl]=\"formControl\">\r\n <mat-error *ngIf=\"!formControl.valid && formControl.touched\">Select valid option</mat-error>\r\n </mat-chip-list>\r\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"selected($event)\">\r\n <mat-option *ngFor=\"let obj of filteredObjects | async\" [value]=\"obj\">\r\n {{obj[displayKey]}}\r\n </mat-option>\r\n </mat-autocomplete>\r\n <mat-hint>{{hint}} </mat-hint>\r\n</mat-form-field>",
styles: [""]
},] }
];
ChipSelectorComponent.propDecorators = {
options: [{ type: Input }],
defaultOptions: [{ type: Input }],
defaultOptionsKey: [{ type: Input }],
label: [{ type: Input }],
placeholder: [{ type: Input }],
displayKey: [{ type: Input }],
filterKey: [{ type: Input }],
hint: [{ type: Input }],
appearance: [{ type: Input }],
disabled: [{ type: Input }],
customChip: [{ type: Input }],
validators: [{ type: Input }],
output: [{ type: Output }],
formInput: [{ type: ViewChild, args: ['formInput',] }],
matAutocomplete: [{ type: ViewChild, args: ['auto',] }]
};
class QuestionControlService {
constructor() { }
toFormGroup(questions) {
let group = {};
questions.some(question => {
if (question.controlType === 'spacer') {
return;
}
group[question.key] = question.validators ? new FormControl({ value: question.value !== undefined || question.value !== null ? question.value : undefined, disabled: question.disabled }, question.validators)
: new FormControl({ value: question.value !== undefined || question.value !== null ? question.value : undefined, disabled: question.disabled });
});
return new FormGroup(group);
}
}
QuestionControlService.decorators = [
{ type: Injectable }
];
QuestionControlService.ctorParameters = () => [];
class DynamicFormComponent {
constructor(qcs) {
this.qcs = qcs;
this.questions = [];
this.emitOnlyOnChange = false;
this.formResult = new EventEmitter();
}
ngOnInit() {
if (!this.questions || this.questions.length === 0) {
console.error('Questions are null or empty.');
return;
}
this.form = this.qcs.toFormGroup(this.questions);
// Emit if the form is valid from begining but not if on change flag is set.
if (!this.emitOnlyOnChange && this.form.valid) {
this.emitForm();
}
this._buttonText = this.buttonText || 'Save';
this.prepareConditionalControls();
this.prepareFilteredOptions();
this.form.statusChanges.subscribe(status => {
if (status === 'VALID') {
this.emitForm();
}
else {
this.formResult.emit(null);
}
;
});
}
emitForm() {
let validForm = this.form.getRawValue();
this.questions.forEach((q) => {
if (q.type && q.type === 'number') {
if (validForm[q.key])
validForm[q.key] = Number(validForm[q.key]);
}
});
this.formResult.emit(validForm);
}
prepareConditionalControls() {
this.changeSubscriptions = [];
// Listen for changes on other controlls
this.questions.forEach(q => {
if (q.conditional) {
// Subscribe to changes
const targetControl = this.form.controls[q.conditional.controlKey];
this.changeSubscriptions.push(targetControl.valueChanges.subscribe(value => {
if (Array.isArray(value)) {
value.includes(q.conditional.value) ? q.show = true : q.show = false;
}
else {
q.conditional.value === value ? q.show = true : q.show = false;
}
}));
// If values are set check them now
if (Array.isArray(targetControl.value)) {
targetControl.value.includes(q.conditional.value) ? q.show = true : q.show = false;
}
else if (targetControl.value && targetControl.value === q.conditional.value) {
q.show = true;
}
}
});
}
prepareFilteredOptions() {
this.filterSubscriptions = [];
this.questions.forEach((q) => {
if (q.selectionFilter) {
const targetControl = this.form.controls[q.selectionFilter.controlKey];
// If the target value changes, subscribe to load new selections
this.filterSubscriptions.push(targetControl.valueChanges.subscribe(value => {
q.selectionFilter.options$(value).subscribe(res => {
q.options$.next(res);
});
}));
// if target is set, then load them immediately
if (targetControl.value) {
q.selectionFilter.options$(targetControl.value).subscribe(res => {
q.options$.next(res);
});
}
}
});
}
ngOnDestroy() {
if (this.changeSubscriptions) {
this.changeSubscriptions.forEach(s => {
s.unsubscribe;
});
}
if (this.filterSubscriptions) {
this.filterSubscriptions.forEach(s => {
s.unsubscribe;
});
}
}
}
DynamicFormComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-dynamic-form',
template: "<form [formGroup]=\"form\" fxLayout=\"column\">\n <div fxLayout=\"row wrap\" fxLayoutAlign=\"start center\">\n <div *ngFor=\"let question of questions; let i = index\" [fxFlex.xs]=\"question.xsFlex\"\n [fxFlex.gt-xs]=\"question.flex\">\n <ngx-mat-dynamic-form-question *ngIf=\"question.show\" [question]=\"question\" [form]=\"form\">\n </ngx-mat-dynamic-form-question>\n </div>\n <ng-content></ng-content>\n </div>\n</form>",
providers: [QuestionControlService],
styles: [""]
},] }
];
DynamicFormComponent.ctorParameters = () => [
{ type: QuestionControlService }
];
DynamicFormComponent.propDecorators = {
questions: [{ type: Input }],
buttonText: [{ type: Input }],
emitOnlyOnChange: [{ type: Input }],
formResult: [{ type: Output }]
};
class DynamicFormQuestionComponent {
constructor(adapter) {
this.adapter = adapter;
this.loading = false;
this.adapter.setLocale('en-GB');
}
get isValid() { return this.form.controls[this.question.key].valid; }
ngOnInit() {
if (this.question.selectionFilter) {
console.log('TODO: Do some loading here...');
}
else if (this.question.options$) {
this.loading = true;
this.question.options$ = this.question.options$.pipe(tap(_ => setTimeout(() => {
this.loading = false;
})));
}
}
setMutlipleValues(event) {
if (this.question.customChip) {
this.form.controls[this.question.key].setValue(event);
}
else {
this.form.controls[this.question.key].setValue(this.question.emitObject ? event : event.map(val => val[this.question.selection.key]));
}
}
setSingleValue(event) {
// Clear input of auto complete
if (!event) {
this.form.controls[this.question.key].setValue(undefined);
}
else {
this.form.controls[this.question.key].setValue(this.question.emitObject ? event : event[this.question.selection.key]);
}
}
dateTimeChange(event) {
if (this.question.hourControl.value) {
event.value.setHours(this.question.hourControl.value);
}
if (this.question.minuteControl.value) {
event.value.setMinutes(this.question.minuteControl.value);
}
this.form.controls[this.question.key].setValue(event.value);
}
dateChange(event) {
this.form.controls[this.question.key].setValue(event.value);
}
setFile(file) {
this.form.controls[this.question.key].setValue(file);
}
}
DynamicFormQuestionComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-dynamic-form-question',
template: "<div [formGroup]=\"form\" fxFlex style=\"margin: 5px;\" fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n\r\n <ng-container [ngSwitch]=\"question.controlType\">\r\n\r\n <span *ngSwitchCase=\"'spacer'\" fxFlex></span>\r\n\r\n <mat-form-field [appearance]=\"question.appearance\" *ngSwitchCase=\"'textbox'\" fxFlex>\r\n <mat-label>{{question.label}}</mat-label>\r\n <span *ngIf=\"question.prefix\" matPrefix>{{question.prefix}} </span>\r\n <mat-icon *ngIf=\"question.prefixIcon\" matPrefix>{{question.prefixIcon}}</mat-icon>\r\n <input matInput [placeholder]=\"question.placeholder\" [formControlName]=\"question.key\" [type]=\"question.type\">\r\n <mat-hint *ngIf=\"question.hint\">{{question.hint}}</mat-hint>\r\n <mat-icon *ngIf=\"question.suffixIcon\" matSuffix>{{question.suffixIcon}}</mat-icon>\r\n <span *ngIf=\"question.suffix\" matSuffix> {{question.suffix}}</span>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </mat-form-field>\r\n\r\n <mat-form-field [appearance]=\"question.appearance\" *ngSwitchCase=\"'textarea'\" fxFlex>\r\n <mat-label>{{question.label}}</mat-label>\r\n <span *ngIf=\"question.prefix\" matPrefix>{{question.prefix}} </span>\r\n <mat-icon *ngIf=\"question.prefixIcon\" matPrefix>{{question.prefixIcon}}</mat-icon>\r\n <textarea matInput [placeholder]=\"question.placeholder\" [formControlName]=\"question.key\" [type]=\"question.type\"\r\n [cdkAutosizeMinRows]=\"question.minRows\" [cdkAutosizeMaxRows]=\"question.maxRows\"\r\n [cdkTextareaAutosize]=\"question.autoSize\"></textarea>\r\n <mat-hint *ngIf=\"question.hint\">{{question.hint}}</mat-hint>\r\n <mat-icon *ngIf=\"question.suffixIcon\" matSuffix>{{question.suffixIcon}}</mat-icon>\r\n <span *ngIf=\"question.suffix\" matSuffix> {{question.suffix}}</span>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </mat-form-field>\r\n\r\n <mat-form-field [appearance]=\"question.appearance\" *ngSwitchCase=\"'dropdown'\" fxFlex>\r\n <mat-label>{{question.label}}</mat-label>\r\n <mat-select [formControlName]=\"question.key\">\r\n <ng-container *ngIf=\"question.options$\">\r\n <mat-option *ngIf=\"question.defaultValue\">None</mat-option>\r\n <mat-option *ngFor=\"let opt of question.options$ | async\" [value]=\"opt[question.selection.key]\">\r\n {{opt[question.selection.value]}}\r\n </mat-option>\r\n </ng-container>\r\n <ng-container *ngIf=\"question.options\">\r\n <mat-option *ngFor=\"let opt of question.options\" [value]=\"opt[question.selection.key]\">\r\n {{opt[question.selection.value]}}\r\n </mat-option>\r\n </ng-container>\r\n </mat-select>\r\n <mat-hint *ngIf=\"question.hint\">{{question.hint}}</mat-hint>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </mat-form-field>\r\n\r\n <div *ngSwitchCase=\"'chipSelector'\" fxFlex>\r\n <ngx-mat-chip-selector [options]=\"question.options$ | async\" [defaultOptions]=\"question.value\"\r\n [defaultOptionsKey]=\"question.selection.key\" [placeholder]=\"question.placeholder\" [disabled]=\"question.disabled\"\r\n [label]=\"question.label\" [displayKey]=\"question.selection.value\" [filterKey]=\"question.selection.value\"\r\n [appearance]=\"question.appearance\" [hint]=\"question.hint\" [validators]=\"question.validators\"\r\n [customChip]=\"question.customChip\" (output)=\"setMutlipleValues($event)\" fxFlex>\r\n </ngx-mat-chip-selector>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'autoComplete'\" fxFlex>\r\n <ngx-mat-auto-complete [options]=\"question.options$ | async\" [defaultOption]=\"question.value\"\r\n [defaultOptionKey]=\"question.selection.key\" [placeholder]=\"question.placeholder\" [disabled]=\"question.disabled\"\r\n [label]=\"question.label\" [displayKey]=\"question.selection.value\" [filterKey]=\"question.selection.value\"\r\n [appearance]=\"question.appearance\" [hint]=\"question.hint\" [validators]=\"question.validators\"\r\n (output)=\"setSingleValue($event)\" [autoClear]=\"question.autoClear\" fxFlex>\r\n </ngx-mat-auto-complete>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'date-time'\" fxLayout=\"row wrap\" fxFlex>\r\n <mat-form-field fxFlex.gt-xs fxFlex [appearance]=\"question.appearance\">\r\n <mat-label>{{question.label}}</mat-label>\r\n <input matInput [min]=\"question.minDate\" [max]=\"question.maxDate\" [matDatepicker]=\"picker1\"\r\n (dateChange)=\"dateTimeChange($event)\" [placeholder]=\"question.placeholder\"\r\n [formControl]=\"question.dateControl\" readonly>\r\n <mat-datepicker-toggle matSuffix [for]=\"picker1\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker1 [disabled]=\"question.disabled\"></mat-datepicker>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </mat-form-field>\r\n <mat-form-field fxFlex.gt-xs=\"20\" fxFlex [appearance]=\"question.appearance\" style=\"margin-left: 5px;\">\r\n <mat-label>Hour</mat-label>\r\n <input type=\"number\" matInput [formControl]=\"question.hourControl\" placeholder=\"Hour\">\r\n </mat-form-field>\r\n <mat-form-field fxFlex.gt-xs=\"20\" fxFlex [appearance]=\"question.appearance\" style=\"margin-left: 5px;\">\r\n <mat-label>Minute</mat-label>\r\n <input type=\"number\" matInput [formControl]=\"question.minuteControl\" placeholder=\"Minute\">\r\n </mat-form-field>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'date'\" fxLayout=\"row wrap\" fxFlex>\r\n <mat-form-field fxFlex.gt-xs fxFlex [appearance]=\"question.appearance\">\r\n <mat-label>{{question.label}}</mat-label>\r\n <input matInput [min]=\"question.minDate\" [max]=\"question.maxDate\" [matDatepicker]=\"picker1\"\r\n (dateChange)=\"dateChange($event)\" [placeholder]=\"question.placeholder\" [formControl]=\"question.dateControl\"\r\n readonly>\r\n <mat-datepicker-toggle matSuffix [for]=\"picker1\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker1 [disabled]=\"question.disabled\"></mat-datepicker>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </mat-form-field>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'checkbox'\" fxFlex>\r\n <mat-checkbox [labelPosition]=\"question.labelPosition\" [checked]=\"question.checked\" [color]=\"question.color\"\r\n [formControlName]=\"question.key\">{{question.label}}</mat-checkbox>\r\n <mat-error>\r\n <ngx-mat-print-input-error [control]=\"form.controls[question.key]\"> </ngx-mat-print-input-error>\r\n </mat-error>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'file-upload'\" fxFlex>\r\n <ngx-mat-file-upload [prefixIcon]=\"question.prefixIcon\" [label]=\"question.label\" [color]=\"question.color\"\r\n [buttonType]=\"question.buttonType\" (file)=\"setFile($event)\">\r\n </ngx-mat-file-upload>\r\n </div>\r\n\r\n </ng-container>\r\n <div *ngIf=\"loading\" style=\"margin-left: 5px;\">\r\n <mat-spinner style=\"zoom:0.24\"></mat-spinner>\r\n </div>\r\n</div>",
styles: [""]
},] }
];
DynamicFormQuestionComponent.ctorParameters = () => [
{ type: DateAdapter }
];
DynamicFormQuestionComponent.propDecorators = {
question: [{ type: Input }],
form: [{ type: Input }]
};
class PrintInputErrorComponent {
constructor() {
this.console = console;
}
}
PrintInputErrorComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-print-input-error',
template: "<div class=\"text-danger\" *ngIf=\"control && control.errors && (control.dirty || control.touched)\">\r\n\r\n <div *ngIf=\"control.errors.required\">\r\n <small>Field is required</small>\r\n </div>\r\n\r\n <div *ngIf=\"control.errors.max\"><small>Maximum value is {{control.errors.max.max}} </small></div>\r\n <div *ngIf=\"control.errors.min\"><small>Minimum value is {{control.errors.min.min}} </small></div>\r\n\r\n <div *ngIf=\"control.errors.maxlength\"><small>Maximum number of characters is\r\n {{control.errors.maxlength.requiredLength}} </small></div>\r\n <div *ngIf=\"control.errors.minlength\"><small>Minimum number of characters is\r\n {{control.errors.minlength.requiredLength}} </small></div>\r\n\r\n <div *ngIf=\"control.errors.email\"><small>Invalid email address</small></div>\r\n\r\n\r\n <div *ngIf=\"control.errors.unique\"><small>{{control.errors.unique}}</small></div>\r\n <div *ngIf=\"control.errors.lessThen\"><small>{{control.errors.lessThen}}</small></div>\r\n <div *ngIf=\"control.errors.greaterThan\"><small>{{control.errors.greaterThan}}</small></div>\r\n <div *ngIf=\"control.errors.mobile\"><small>{{control.errors.mobile}}</small></div>\r\n <div *ngIf=\"control.errors.confirmPassword\"><small>{{control.errors.confirmPassword}}</small></div>\r\n</div>",
styles: [""]
},] }
];
PrintInputErrorComponent.propDecorators = {
control: [{ type: Input, args: ["control",] }]
};
class FileUploadComponent {
constructor() {
this.file = new EventEmitter();
}
ngOnInit() {
}
processFile(event) {
this.fileSelected = event.target.files[0];
this.file.emit(this.fileSelected);
}
}
FileUploadComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-mat-file-upload',
template: "<ng-container *ngIf=\"buttonType === 'icon'; else normalButton;\">\n <button mat-icon-button (click)=\"fileInput.click()\">\n <mat-icon matBadge=\"1\" [matBadgeHidden]=\"!fileSelected\" matBadgeSize=\"small\" matBadgeColor=\"accent\">\n {{prefixIcon}}</mat-icon>\n <input #fileInput type=\"file\" (change)=\"processFile($event)\" style=\"display:none;\" />\n </button>\n</ng-container>\n<ng-template #normalButton>\n <button mat-flat-button [color]=\"color ? color : 'primary'\" (click)=\"fileInput.click()\">\n <mat-icon *ngIf=\"prefixIcon && !fileSelected\" style=\"margin-left: -8px;\">{{prefixIcon}}</mat-icon>\n <mat-icon *ngIf=\"fileSelected\">check</mat-icon>\n <span>{{label}}</span>\n <input #fileInput type=\"file\" (change)=\"processFile($event)\" style=\"display:none;\" />\n </button>\n</ng-template>",
styles: [""]
},] }
];
FileUploadComponent.ctorParameters = () => [];
FileUploadComponent.propDecorators = {
buttonType: [{ type: Input }],
prefixIcon: [{ type: Input }],
suffixIcon: [{ type: Input }],
label: [{ type: Input }],
color: [{ type: Input }],
file: [{ type: Output }]
};
class NgxMatDynamicFormBuilderModule {
}
NgxMatDynamicFormBuilderModule.decorators = [
{ type: NgModule, args: [{
declarations: [
NgxMatDynamicFormBuilderComponent,
AutoCompleteComponent,
ChipSelectorComponent,
DynamicFormComponent,
DynamicFormQuestionComponent,
PrintInputErrorComponent,
FileUploadComponent
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
FlexLayoutModule,
MatInputModule,
MatChipsModule,
MatFormFieldModule,
MatAutocompleteModule,
MatProgressSpinnerModule,
MatDatepickerModule,
MatIconModule,
MatSelectModule,
MatCheckboxModule,
MatButtonModule,
MatBadgeModule
],
exports: [
AutoCompleteComponent,
ChipSelectorComponent,
DynamicFormComponent,
DynamicFormQuestionComponent,
PrintInputErrorComponent
]
},] }
];
/**
* Base question with no mat form field
*/
class Question {
constructor(options = {}) {
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.controlType = options.controlType || '';
this.validators = options.validators || null;
this.flex = options.flex || 100;
this.xsFlex = options.xsFlex || 100;
this.disabled = options.disabled || false;
this.conditional = options.conditional;
this.conditional ? this.show = false : this.show = true;
}
}
/**
* Base for all mat form field questions:
* Auto complete
* Chip Selector
* Date time
* Date
* Dropdown
* Select
* Text area
* Text Box
*/
class QuestionBase extends Question {
constructor(options = {}) {
super(options);
this.hint = options.hint || '';
this.placeholder = options.placeholder || '';
this.appearance = options.appearance || 'standard';
this.validators = options.validators || null;
this.prefix = options.prefix;
this.suffix = options.suffix;
this.prefixIcon = options.prefixIcon;
this.suffixIcon = options.suffixIcon;
}
}
/**
* Base class for any drop down of multiple select
*/
class SelectQuestion extends QuestionBase {
constructor(options = {}) {
super(options);
this.options = options['options'] || [];
this.options$ = options['options$'];
this.selection = options['selection'];
this.defaultValue = options['defaultValue'] != undefined ? options['defaultValue'] : false;
this.selectionFilter = options['selectionFilter'];
this.emitObject = options['emitObject'];
}
}
class AutoCompleteQuestion extends SelectQuestion {
constructor(options = {}) {
super(options);
this.controlType = 'autoComplete';
this.autoClear = options['autoClear'] || false;
}
}
class DropdownQuestion extends SelectQuestion {
constructor(options = {}) {
super(options);
this.controlType = 'dropdown';
}
}
class ChipSelectorQuestion extends SelectQuestion {
constructor(options = {}) {
super(options);
this.controlType = 'chipSelector';
this.customChip = options['customChip'];
}
}
class TextboxQuestion extends QuestionBase {
constructor(options = {}) {
super(options);
this.controlType = 'textbox';
this.type = options['type'] || '';
}
}
class CheckboxQuestion extends Question {
constructor(options = {}) {
super(options);
this.controlType = 'checkbox';
this.checked = options['value'] || options['value'] === true ? true : false;
this.value = this.checked;
this.color = options['color'] || 'primary';
this.labelPosition = options['labelPosition'] || 'after';
}
}
class TextAreaQuestion extends QuestionBase {
constructor(options = {}) {
super(options);
this.controlType = 'textarea';
this.type = options['type'] || '';
this.maxRows = options['maxRows'] || 5;
this.minRows = options['minRows'] || 2;
this.autoSize = options['autoSize'] || true;
}
}
class DateQuestion extends QuestionBase {
constructor(options = {}) {
super(options);
this.controlType = 'date';
this.dateControl = new FormControl();
this.maxDate = options['maxDate'];
this.minDate = options['minDate'];
if (options['value']) {
this.dateControl.setValue(options['value']);
}
}
}
class DateTimeQuestion extends DateQuestion {
constructor(options = {}) {
super(options);
this.controlType = 'date-time';
this.hourControl = new FormControl({ value: undefined, disabled: options['disabled'] != undefined ? options['disabled'] : false }, [Validators.min(0), Validators.max(23)]);
this.minuteControl = new FormControl({ value: undefined, disabled: options['disabled'] != undefined ? options['disabled'] : false }, [Validators.min(0), Validators.max(59)]);
if (this.dateControl.value) {
this.hourControl.setValue(this.dateControl.value.getHours());
this.minuteControl.setValue(this.dateControl.value.getMinutes());
}
this.hourControl.valueChanges.subscribe(hour => {
if (hour !== undefined || hour !== null && this.dateControl.value) {
this.dateControl.value.setHours(hour);
}
});
this.minuteControl.valueChanges.subscribe(min => {
if (min !== undefined || min !== null && this.dateControl.value) {
this.dateControl.value.setMinutes(min);
}
});
}
}
class FileUploadQuestion extends QuestionBase {
constructor(options = {}) {
super(options);
this.controlType = 'file-upload';
this.color = options['color'] || 'primary';
this.buttonType = options['buttonType'] || 'icon';
}
}
class Spacer extends QuestionBase {
constructor(options = {}) {
super(options);
this.controlType = 'spacer';
this.flex = options['flex'] || 100;
}
}
// @dynamic
class FormValidators {
static get(validatorString) {
const validator = this.validators.find(v => v.name === validatorString);
return validator ? validator.validators : [];
}
}
FormValidators.validators = [
{
name: 'required',
validators: [
Validators.required
]
},
{
name: 'integer',
validators: [
Validators.pattern(/^[0-9]*$/)
]
},
{
name: 'positiveInteger',
validators: [
Validators.min(0),
Validators.pattern(/^[0-9]*$/)
]
},
{
name: 'positiveNumber',
validators: [
Validators.min(0),
Validators.pattern(/^[0-9]*$/)
]
},
{
name: 'string',
validators: [
Validators.pattern(/^[A-Za-z]+$/)
]
}
];
/*
* Public API Surface of ngx-mat-dynamic-form-builder
*/
/**
* Generated bundle index. Do not edit.
*/
export { AutoCompleteComponent, AutoCompleteQuestion, CheckboxQuestion, ChipSelectorComponent, ChipSelectorQuestion, DateQuestion, DateTimeQuestion, DropdownQuestion, DynamicFormComponent, DynamicFormQuestionComponent, FileUploadQuestion, FormValidators, NgxMatDynamicFormBuilderComponent, NgxMatDynamicFormBuilderModule, NgxMatDynamicFormBuilderService, PrintInputErrorComponent, Question, QuestionBase, Spacer, TextAreaQuestion, TextboxQuestion, QuestionControlService as ɵa, FileUploadComponent as ɵb, SelectQuestion as ɵc };
//# sourceMappingURL=ngx-mat-dynamic-form-builder.js.map