import { Inject , Injectable } from '@angular/core' ; import { FormBuilder } from '@angular/forms' ; import { Observable } from 'rxjs' ; import { concatMap , map } from 'rxjs/operators' ; import { IFormQLService } from '../interfaces/formql-service' ; import { FormComponent } from '../models/form-component.model' ; import { FormPage } from '../models/form-page.model' ; import { FormSection } from '../models/form-section.model' ; import { FormDataSource , FormState , FormWindow } from '../models/form-window.model' ; import { RuleLogic } from '../validators/rule-logic' ; import { ComponentResolverService } from './component-resolver.service' ; import { HelperService } from './helper.service' ; @ Injectable ( { providedIn : 'root' } ) export class FormService { private injectedService : IFormQLService ; private ruleLogic = new RuleLogic ( ) ; constructor ( @ Inject ( 'FormQLService' ) srv , private componentResolverService , private formBuilder ) { this . injectedService = srv ; } public getFormAndData ( formName , ids ) { if ( ids ) return this . injectedService . getForm ( formName ) . pipe ( map ( ( response ) => < FormWindow > response ) , concatMap ( ( model ) => this . injectedService . getData ( model . dataSource , ids ) . pipe ( map ( ( data ) => this . initialiseFormState ( model , data ) ) ) ) ) ; else return this . injectedService . getForm ( formName ) . pipe ( map ( ( model ) => this . initialiseFormState ( model , null ) ) ) ; } public saveForm < T , U > ( name , form ) { const updateForm = HelperService . deepCopy ( form ) ; updateForm . pages . forEach ( ( page ) => { page . sections . forEach ( ( section ) => { section . components . forEach ( ( component ) => { Object . keys ( component ) . filter ( ( key ) => component [ key ] === null ) . forEach ( ( key ) => delete component [ key ] ) ; delete component . value ; if ( component . rules ) if ( Object . keys ( component . rules ) . length === 0 && component . rules . constructor === Object ) delete component . rules ; else Object . keys ( component . rules ) . forEach ( ( p ) => { delete component . rules [ p ] . value ; } ) ; } ) ; } ) ; } ) ; return this . injectedService . saveForm ( name , updateForm ) . pipe ( map ( ( response ) => { return response ; } ) ) ; } public saveData < T > ( dataSource , ids , data ) { return this . injectedService . saveData ( dataSource , ids , data ) . pipe ( map ( ( result ) => { return result ; } ) ) ; } public updateComponent < T > ( component , formState , reset = false ) { const value = HelperService . resolveType ( component . value , component . type ) ; formState . data = HelperService . setValue ( component . schema , value , formState . data ) ; if ( reset ) this . resetComponentDependents ( formState ) ; if ( component . dependents ) component . dependents . forEach ( ( key ) => { formState . components [ key ] = this . resolveComponentRules ( formState . components [ key ] , formState ) ; } ) ; Object . keys ( formState . components ) . forEach ( ( key ) => { const c = formState . components [ key ] ; if ( c . schema === component . schema ) try { c . value = HelperService . getValue ( c . schema , formState . data , c . type ) ; } catch ( err ) { throw err ; } } ) ; return formState ; } public initialiseFormState < T > ( form , data ) { const reactiveFormStructure = HelperService . createReactiveFormStructure ( form , true , data ) ; const formState = < FormState > { components : reactiveFormStructure . components , data : { ... reactiveFormStructure . data } , form : form , formControls : reactiveFormStructure . formControls , reactiveForm : this . formBuilder . group ( reactiveFormStructure . pageGroup ) } ; return this . resolveConditions ( formState ) ; } private resetComponentDependents ( formState ) { if ( formState . components ) { Object . keys ( formState . components ) . forEach ( ( componentKey ) => delete formState . components [ componentKey ] . dependents ) ; Object . keys ( formState . components ) . forEach ( ( componentKey ) => { const component = formState . components [ componentKey ] ; if ( component . rules ) Object . keys ( component . rules ) . forEach ( ( ruleKey ) => { const rule = component . rules [ ruleKey ] ; this . ruleLogic . resetDependancies ( formState , rule . condition , component ) ; } ) ; } ) ; Object . keys ( formState . components ) . forEach ( ( componentKey ) => ( formState . components [ componentKey ] = this . resolveComponentRules ( formState . components [ componentKey ] , formState ) ) ) ; } } private getData < T > ( query , ids ) { return this . injectedService . getData ( query , ids ) . pipe ( map ( ( data ) => { if ( data ) return data ; else return { } ; } ) ) ; } private getForms < T > ( ) { return this . injectedService . getForms ( ) . pipe ( map ( ( data ) => { return data ; } ) ) ; } private getForm ( name ) { return this . injectedService . getForm ( name ) . pipe ( map ( ( data ) => { return data ; } ) ) ; } private resolveComponentRules < T > ( component , formState ) { if ( component . rules ) { let resetValidator = false ; Object . keys ( component . rules ) . forEach ( ( key ) => { const property = component . rules [ key ] ; if ( property . condition ) { resetValidator = true ; const evaluatedValue = this . ruleLogic . evaluate ( formState . data , property . condition ) ; if ( key === 'value' ) { const value = HelperService . resolveType ( evaluatedValue , component . type ) ; if ( component . value !== value ) { component . value = value as T ; formState = this . updateComponent ( component , formState ) ; } } property . value = evaluatedValue as boolean ; } else delete component . rules [ key ] ; } ) ; if ( resetValidator ) formState . formControls [ component . componentId ] = HelperService . setValidators ( this . componentResolverService , component , formState . formControls [ component . componentId ] ) ; } return component ; } public resolveConditions ( formState ) { if ( formState . components ) { const components = formState . components ; Object . keys ( components ) . forEach ( ( componentKey ) => ( components [ componentKey ] = this . resolveComponentRules ( components [ componentKey ] , formState ) ) ) ; } return formState ; } }