UNPKG

first-npm-package-nicule

Version:

This isi first npm package

311 lines (251 loc) 10.3 kB
import { AfterViewInit, Component, ContentChild, ElementRef, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewChildren } from '@angular/core'; import { HypermediaAction, HypermediaField, LabelingService } from 'first-npm-package-nicule/core'; import { NgForm } from '@angular/forms'; import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { FormFieldsComponent } from '../form-fields.component'; import { PARENT_FORM } from '../parent-form'; import { FormFieldComponent } from '../form-field.component'; @Component({ selector: 'hm-form', templateUrl: 'form.component.html', styleUrls: ['form.component.scss'], providers: [{provide: PARENT_FORM, useExisting: HypermediaForm}], viewProviders: [{provide: PARENT_FORM, useExisting: HypermediaForm}], exportAs: 'hmForm' }) export class HypermediaForm implements AfterViewInit, OnChanges { @Input() action: HypermediaAction; @Input() tokenOverride: string; @Input() preventSubmissionOnEnter = true; @Input() disabled = false; // this overwrites all inputs @Input() mode: 'regular' | 'narrow' | 'wide' = 'regular'; // this overwrites all inputs @Input() additionalData: any; @Output() actionSuccess = new EventEmitter(); @Output() actionError = new EventEmitter(); @Output() init = new EventEmitter(); @Output() valueChanged = new EventEmitter(); @Output() actionSubmit = new EventEmitter(); @Output() actionComplete = new EventEmitter(); @Output() escapeKeydown = new EventEmitter(); @ViewChild('actionForm', { static: false }) ngForm: NgForm; @ViewChild('actionForm', {read: ElementRef, static: false }) formElement: ElementRef; @ViewChildren('fallbackField', {read: FormFieldComponent }) fallbackFields: FormFieldComponent; _formFields: FormFieldsComponent; get formFields(): FormFieldsComponent { return this._formFields; } @ContentChild(FormFieldsComponent, { static: false }) set formFields(newValue: FormFieldsComponent) { this._formFields = newValue; } isSubmiting = false; error: string; fields: Array<HypermediaField & { fieldInputs?: any }> = []; get value(): any { return this.ngForm && this.ngForm.value || {}; } get isValid(): boolean { return this.ngForm && this.ngForm.valid; } private valueChangeSubscription: Subscription; ngOnChanges(changes: SimpleChanges): void { if ('action' in changes && this.action) { const {action} = this as any; this.fields = action && this.cloneFields(action.fields) || []; // this should be made in a imutable } } cloneFields(source: Array<HypermediaField>): Array<HypermediaField> { return source.map(({fields, options, flex, ...rest}) => ({ ...rest, fields: fields && fields.map(field => ({...field})), options: options && options.map(option => ({...option})), flex: flex && Array.isArray(flex) ? flex.map(flexItem => flexItem) : flex })); } constructor( private translate: TranslateService, private labelingService: LabelingService, private _injector: Injector ) { } submit(formValues?: any): void { if (this.isSubmiting === true) { return; } this.isSubmiting = true; if (formValues && Object.keys(this.ngForm.form.controls).length > 0) { this.ngForm.setValue({...this.ngForm.value, ...formValues}); } this.ngForm.onSubmit(new MouseEvent('')); } onSuccess(event = {} as any): void { this.isSubmiting = false; this.actionSuccess.emit(event); } onError(event = {} as any): void { this.isSubmiting = false; if (typeof event === 'string') { this.error = event; } this.actionError.emit(event); } onComplete(): void { this.isSubmiting = false; this.actionComplete.emit(); const errors = []; Object.keys(this.ngForm.controls) .forEach(key => { const currentField = this.ngForm.controls[key]; if (!currentField.valid) { errors.push(key); } }); if (errors.length) { this.scrollToError(errors); } } formSubmission(event): void { this.actionSubmit.emit(); if (!this.preventSubmissionOnEnter) { return; } event.stopPropagation(); // stop the event from propagating on the form, preventing submit } showErrors(onlyPrefilledFields = false): void { const errors = []; Object.keys(this.ngForm.controls) .filter(key => !onlyPrefilledFields || this.ngForm.controls[key].value) .forEach(key => { const currentField = this.ngForm.controls[key]; this.ngForm.controls[key].markAsDirty(); this.ngForm.controls[key].markAsTouched(); if (!currentField.valid) { errors.push(key); } }); if (errors.length) { this.scrollToError(errors); } } scrollToError(errors): void { if (this.formFields && this.formFields.orderer) { for (const formFieldComponent of this.formFields.orderer.ordered) { let name = formFieldComponent.named; const type = formFieldComponent.baseField.type; let selector; let foundInGroup = false; if (type === 'group') { const fieldsInGroup = formFieldComponent.baseField.fields.map(i => i.name); foundInGroup = errors.some(r => { if (fieldsInGroup.indexOf(r) >= 0) { name = r; return true; } return false; }); } if (errors.includes(name) || foundInGroup) { switch (type) { case 'boolean': selector = `[name=${name}]`; break; case 'options': selector = `mat-select[id^=${name}-]`; break; default: selector = `[id^=form-field-${name}-]`; } const elem = this.formElement.nativeElement.querySelector(selector); if (elem) { elem.scrollIntoView({behavior: 'smooth', block: 'center'}); } break; } } } } reset(resetForm = true): any { this.error = ''; if (!this.ngForm) { return; } if (resetForm) { this.ngForm.reset(); } this.ngForm.control.setErrors({}, {emitEvent: true}); this.reinitializeFields(this.action.fields); } triggerSubmit(): void { this.actionSubmit.emit(); } private reinitializeFields(fields: Array<HypermediaField> = []): void { fields.forEach(field => { if (field.type === 'group') { this.reinitializeFields(field.fields); } else { this.reinitializeField(field); } }); } private reinitializeField({name, value}: HypermediaField): void { const control = this.ngForm.controls[name]; if (control) { control.setValue(value); } } setValue(value: any): any { if (!value) { return; } Object .keys(value) .forEach(name => { const control = this.ngForm.controls[name]; if (control) { control.setValue(value[name]); } }); } ngDestroy(): void { this.valueChangeSubscription.unsubscribe(); } ngAfterViewInit(): void { const form = this.ngForm; if (form !== undefined) { this.init.emit(); this.valueChangeSubscription = form.valueChanges.subscribe(newValue => { if (this.action && this.action['$$validator']) { const validationResponse = this.action['$$validator'](newValue); if (validationResponse) { const propertyKeys = Object.keys(validationResponse); propertyKeys.forEach(propertyKey => { const control = form.controls[propertyKey]; if (control) { const modelErrors = validationResponse[propertyKey]; const defaultErrors = control.validator(control); const combinedErrors = {...defaultErrors, ...modelErrors}; const hasErrors = Object.keys(combinedErrors).length > 0; if (hasErrors) { control.setErrors(combinedErrors); } else { control.setErrors(undefined); } } }); } } this.valueChanged.emit(newValue); }); } } getField(named: string): any { return this.fields.find(({name}) => name === named); } getSelectedOptionName(fieldName: string): string { const field = this.fields .find(f => f.name === fieldName); return field.options .find(option => option.value === field.value) .name; } }