field.model.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /* *********************************************************************************************************************
  2. * MetaData models for Form Fields
  3. * -------------------------------
  4. * Keep in one file for now, but maybe split if this grows too large
  5. ******************************************************************************************************************** */
  6. import { TemplateRef } from '@angular/core';
  7. import { ValidatorFn, AsyncValidatorFn } from '@angular/forms';
  8. import { ValueTransformer } from './../interfaces';
  9. import { standardModifiers, standardTransformer } from './../utils';
  10. interface SimpleFieldMetaData {
  11. name: string; // The FormControl name
  12. origin?: string; // Location in API-returned model - defaults to name
  13. type?: string; // The component type e.g. BasicInput, Checkbutton, Timepicker, etc
  14. label?: string; // The field label - defaults to unCamelCased name if not supplied
  15. value?: any; // The field value - defaults to empty string if not supplied
  16. default?: any; // Default value
  17. placeholder?: string; // Optional placeholder text
  18. class?: string | Array<string>; // CSS classes to apply
  19. id?: string; // CSS id to apply
  20. isDisabled?: boolean; // Whether field is initially disabled
  21. validators?: Array<ValidatorFn>; // Array of validator functions - following Angular FormControl API
  22. asyncValidators?: Array<AsyncValidatorFn>; // Array of async validator functions - following Angular FormControl API
  23. valFailureMsgs?: StringMap; // Validation failure messages - display appropriate message if validation fails
  24. }
  25. interface Option {
  26. label: string;
  27. value: string | number | boolean;
  28. }
  29. interface OptionsFieldMetaData extends SimpleFieldMetaData {
  30. options; // Array of Options - for select, radio-button-group and other 'multiple-choice' types
  31. horizontal?: boolean; // Whether to arrang radio buttons or checkboxes horizontally (default false)
  32. }
  33. // For components that include links to other pages
  34. interface Link {
  35. label: string;
  36. route: any[] | string;
  37. }
  38. interface DropdownModifiedInputFieldMetaData extends SimpleFieldMetaData {
  39. modifiers: string[];
  40. transform: ValueTransformer;
  41. }
  42. interface TimePickerFieldMetaData extends SimpleFieldMetaData {
  43. // TODO: Tighhten up on types
  44. value: Date;
  45. format: string;
  46. steps: StringMap;
  47. }
  48. // Utility to unCamelCase
  49. const unCamelCase = (str: string): string => str.replace(/([A-Z])/g, ' $1')
  50. .replace(/(\w)(\d)/g, '$1 $2')
  51. .replace(/^./, s => s.toUpperCase());
  52. // ---------------------------------------------------------------------------------------------------------------------
  53. // Form Field MetaData Models
  54. // ---------------------------------------------------------------------------------------------------------------------
  55. // Base Implementations
  56. abstract class SimpleField {
  57. type: string;
  58. name: string;
  59. origin?: string;
  60. label?: string;
  61. value;
  62. default = '';
  63. placeholder = '';
  64. class?: string | Array<string>;
  65. id?: string;
  66. isDisabled = false;
  67. validators: Array<ValidatorFn> = [];
  68. asyncValidators: Array<AsyncValidatorFn> = [];
  69. valFailureMsgs: StringMap = {};
  70. constructor(meta: SimpleFieldMetaData) {
  71. Object.assign(this, meta);
  72. if (!this.origin) {
  73. // If origin is not supplied it's the same as the name
  74. this.origin = this.name;
  75. }
  76. if (!this.label) {
  77. // If label is not supplied set it to the unCamelCased'n'Spaced name
  78. // e.g. supervisorCardNumber --> Supervisor Card Number
  79. this.label = unCamelCase(this.name);
  80. }
  81. }
  82. }
  83. class Option {
  84. // Can take a simple string value, a value-label pair [value, label],
  85. // or an Option of the form { label: string, value: string }
  86. constructor(opt: string | string[] | Option) {
  87. if (typeof opt === 'object') {
  88. if (Array.isArray(opt)) {
  89. this.label = opt[1];
  90. this.value = opt[0];
  91. } else {
  92. this.label = opt.label;
  93. this.value = opt.value;
  94. }
  95. } else if (typeof opt === 'string') {
  96. this.label = opt;
  97. this.value = opt;
  98. }
  99. }
  100. }
  101. abstract class OptionsField extends SimpleField {
  102. options: Option[] = [];
  103. constructor(meta: OptionsFieldMetaData) {
  104. super(meta);
  105. if (Array.isArray(meta.options)) {
  106. this.options = meta.options.reduce((acc, opt) => { acc.push(new Option(opt)); return acc; }, []);
  107. } else {
  108. this.options = [
  109. new Option({ label: 'Yes', value: true }),
  110. new Option({ label: 'No', value: false })
  111. ];
  112. }
  113. }
  114. }
  115. // ---------------------------------------------------------------------------------------------------------------------
  116. // Concrete Implementations - Native Form Components
  117. class TextField extends SimpleField {
  118. type = 'Text';
  119. link?: Link;
  120. }
  121. class TextareaField extends SimpleField {
  122. type = 'Textarea';
  123. }
  124. class PasswordField extends SimpleField {
  125. type = 'Password';
  126. }
  127. class SelectField extends OptionsField {
  128. type = 'Select';
  129. link?: Link;
  130. }
  131. class RadioField extends OptionsField {
  132. type = 'Radio';
  133. }
  134. class HiddenField extends SimpleField {
  135. type = 'Hidden';
  136. }
  137. // ---------------------------------------------------------------------------------------------------------------------
  138. // Concrete Implementations - Custom Form Components
  139. class CheckbuttonField extends SimpleField {
  140. type = 'Checkbutton';
  141. default: any = false;
  142. }
  143. class DropdownModifiedInputField extends SimpleField {
  144. type = 'DropdownModifiedInput';
  145. modifiers: string[] = standardModifiers;
  146. transform: ValueTransformer = standardTransformer;
  147. constructor(meta: DropdownModifiedInputFieldMetaData) {
  148. super(meta);
  149. }
  150. }
  151. // ---------------------------------------------------------------------------------------------------------------------
  152. // Concrete Implementations - Custom FormGroup Components (which render a group of FormControls)
  153. class CheckbuttonGroup {
  154. type = 'CheckbuttonGroup';
  155. name: string;
  156. label?: string;
  157. groupName: string;
  158. firstEnablesRest?;
  159. showAllOrNone?;
  160. meta: CheckbuttonField[] | { [key: string]: CheckbuttonField };
  161. constructor(groupmeta: any) {
  162. Object.assign(this, groupmeta);
  163. if (!this.label) {
  164. // If label is not supplied set it to the unCamelCased'n'Spaced name
  165. // e.g. supervisorCardNumber --> Supervisor Card Number
  166. this.label = unCamelCase(this.name);
  167. }
  168. // Can render as a FormArray or FormGroup depending on input data
  169. if (Array.isArray(groupmeta.meta)) {
  170. const arrayMembers = groupmeta.meta;
  171. this.meta = arrayMembers.map(cb => new CheckbuttonField(cb));
  172. } else {
  173. const groupMembers = groupmeta.meta;
  174. this.meta = Object.entries(groupMembers)
  175. .map( ([key, cb]) => [key, new CheckbuttonField(cb as SimpleFieldMetaData)] )
  176. .reduce((res, [key, cbf]) => { res[<string>key] = cbf; return res; }, {});
  177. }
  178. }
  179. }
  180. // ---------------------------------------------------------------------------------------------------------------------
  181. // Concrete Implementations - Kendo Form Components
  182. class TimepickerField<TimePickerFieldMetaData> extends SimpleField {
  183. type = 'Timepicker';
  184. value: Date = new Date();
  185. format = 'hh:mm a';
  186. steps = { hour: 1, minute: 15 };
  187. }
  188. class DatepickerField extends SimpleField {
  189. type = 'Datepicker';
  190. value: Date = new Date();
  191. }
  192. // ---------------------------------------------------------------------------------------------------------------------
  193. // Container
  194. class Container {
  195. type = 'Container';
  196. name: string;
  197. label = '';
  198. template?: TemplateRef<any>;
  199. meta: StringMap; // TODO: Tighten up on type with union type
  200. constructor(meta: StringMap) {
  201. console.log(meta);
  202. meta.meta ? Object.assign(this, meta) : this.meta = meta;
  203. if (!this.label) {
  204. this.label = unCamelCase(this.name);
  205. }
  206. }
  207. }
  208. // ---------------------------------------------------------------------------------------------------------------------
  209. // Button Group
  210. interface ButtonInterface {
  211. label: string;
  212. fnId: string;
  213. class?: string;
  214. icon?: string;
  215. }
  216. class Button implements ButtonInterface {
  217. label;
  218. fnId;
  219. class: string = 'btn-primary';
  220. constructor(buttonProps) {
  221. Object.assign(this, buttonProps);
  222. }
  223. }
  224. class ButtonGroup {
  225. type = 'ButtonGroup';
  226. name: string;
  227. label = '';
  228. meta: Button[];
  229. readonly noFormControls = true; // Indicates this has no FormControls associated with it
  230. constructor(meta) {
  231. Object.assign(this, meta);
  232. this.meta = this.meta.map(b => b instanceof Button ? b : new Button(b));
  233. }
  234. }
  235. // ---------------------------------------------------------------------------------------------------------------------
  236. // ---------------------------------------------------------------------------------------------------------------------
  237. // ---------------------------------------------------------------------------------------------------------------------
  238. // Exports
  239. export {
  240. SimpleField,
  241. TextField, TextareaField, PasswordField, SelectField, RadioField, HiddenField,
  242. CheckbuttonField, DropdownModifiedInputField,
  243. CheckbuttonGroup,
  244. TimepickerField, DatepickerField,
  245. Container, ButtonGroup
  246. };