/* ********************************************************************************************************************* * Dynaform Service, exposing 10 public methods * ********************************************************************************************************************* * * BUILD * ----- * This is the main method you'll use to build forms from models and metadata: * * build(model, meta = {}, createFromMeta = false) * * Takes a model and (lazy)metadata and returns an object containing a FormGroup and Modeled MetaData : * { * form: FormGroup, * meta: ModeledMetaData * } * * meta is optional, and if not supplied all the model fields will become Text inputs in the FormGroup, * with their labels set to the un-camel-cased property name * * createFromMeta is optional. * If true it will create new fields in the FormGroup even when they don't already exist in the model, * but exist in the metadata. This is NOT the default behaviour, except when an empty model is supplied. * i.e. It defaults to false, except when model == {} * * Usage * ----- * * build(model) - build everything from the model * build({}, meta) - build everything from the metadata * build(model, meta) - build by combining the model with metadata, lazyily (not every model field needs metadata, as sensible defaults) * build(model, meta, true) - build by combining model with metadata, creating new fields from metadata points that don't occur in the model * * * REGISTER * -------- * Registers callbacks attached to the form (e.g. to buttons), identified by strings. * * register({ * 'SAYHELLO': () => { alert('HELLO') }, * 'SEARCH': execSearch, * 'NEW': addNew, * }, varToBind); * * If varToBind is supplied it is bound as 'this' to the functions. * Typically you'd supply the component class instance, so that 'this' used in callbacks refers to the host component. * * * LOWER-LEVEL METHODS * ------------------- * * autoBuildFormGroupAndMeta(model, meta, createFromMeta) - synonym for build * autoBuildModeledMeta(model, meta, createFromMeta) - takes a model and (lazy)metadata and returns expanded metadata * * buildFormGroup(metadata) - builds FormGroups from modelled metdata, recursively if necessary * buildFieldSpecificMeta(metadata) - use field metadta models to fill out metadata * combineModelWithMeta(model, extraMeta) - automatically generated metadata for model then combines extra metadata * combineExtraMeta(metadata, extraMeta) - combine extra metadata into metatdata, lazyly and recursively * autoMeta(model) - generate basic metadata from a raw or mapped model, recursively if necessary * * * NOTES * ----- * This class acts as an injectable wraper around the exports of _formdata-utils.ts, * as well as creating a buildFormGroup function using the injected FormBuilder singleton * * * EXAMPLES * -------- * * TO ADD ... * */ import { Injectable, ComponentRef } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { autoMeta, combineModelWithMeta, combineExtraMeta, buildFieldSpecificMeta, buildFormGroupFunctionFactory } from './_formdata-utils'; export interface FormAndMeta { form: FormGroup; meta: StringMap; } export interface Callbacks { [index: string]: () => void } @Injectable() export class DynaformService { public buildFormGroup: (meta) => FormGroup; private callbacks: Callbacks = {}; constructor(private fb: FormBuilder) { this.buildFormGroup = buildFormGroupFunctionFactory(fb); } build(model: StringMap, meta = {}, createFromMeta = false): FormAndMeta { // Short name for autoBuildFormGroupAndMeta return this.autoBuildFormGroupAndMeta(model, meta, createFromMeta); } register(callbacks: Callbacks, cref: ComponentRef['instance']) { // Bind the component instance to the callback, so that 'this' has the context of the component if (cref) { Object.entries(callbacks).forEach(([key, fn]) => this.callbacks[key] = fn.bind(cref)); } } call(fnId: string) { console.log('Dynaform Service', fnId); // Handle callback events try { this.callbacks[fnId](); } catch(e) { console.error('Dynaform has no registered callback for', fnId); console.error(e); } } // ----------------------------------------------------------------------------------------------------------------- // Convenience methods combining several steps autoBuildFormGroupAndMeta(model: StringMap, meta = {}, createFromMeta = false): FormAndMeta { if (Object.keys(model).length === 0) { createFromMeta = true; } const modelWithMeta = this.autoBuildModeledMeta(model, meta, createFromMeta); return { form: this.buildFormGroup(modelWithMeta), meta: modelWithMeta }; } autoBuildModeledMeta(model: StringMap, meta = {}, createFromMeta = false) { const modelWithMeta = this.combineModelWithMeta(model, meta, createFromMeta); return this.buildFieldSpecificMeta(modelWithMeta); } // ----------------------------------------------------------------------------------------------------------------- // Build field-type-specific metadata using the form field models (see dynaform/models) buildFieldSpecificMeta(meta) { return buildFieldSpecificMeta(meta); } // ----------------------------------------------------------------------------------------------------------------- // Lower-level methods combineModelWithMeta(model: StringMap, meta, createFromMeta = false) { return combineModelWithMeta(model, meta, createFromMeta); } combineExtraMeta(meta, extraMeta, createFromExtra = false) { return combineExtraMeta(meta, extraMeta, createFromExtra); } autoMeta(model) { return autoMeta(model); } }