Bladeren bron

Refactoring, and adding additional field types

Richard Knight 6 jaren geleden
bovenliggende
commit
a72b532920
44 gewijzigde bestanden met toevoegingen van 464 en 159 verwijderingen
  1. 2 2
      dynaform.code-workspace
  2. 74 0
      package-lock.json
  3. 4 0
      package.json
  4. 79 0
      src/app/_mock/testfields.ts
  5. 3 75
      src/app/app.component.ts
  6. 19 0
      src/app/dynaform/components/_abstract/native-input/native-input.component.ts
  7. 0 21
      src/app/dynaform/components/basicinput/basicinput.component.ts
  8. 0 0
      src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.html
  9. 0 0
      src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.scss
  10. 0 0
      src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.spec.ts
  11. 1 4
      src/app/dynaform/components/checkbutton-group/checkbutton-group.component.ts
  12. 0 0
      src/app/dynaform/components/custom/checkbutton/checkbutton.component.html
  13. 0 0
      src/app/dynaform/components/custom/checkbutton/checkbutton.component.scss
  14. 0 0
      src/app/dynaform/components/custom/checkbutton/checkbutton.component.spec.ts
  15. 0 0
      src/app/dynaform/components/custom/checkbutton/checkbutton.component.ts
  16. 0 0
      src/app/dynaform/components/custom/dropdown-modified-input/dropdown-modified-input.component.html
  17. 0 0
      src/app/dynaform/components/custom/dropdown-modified-input/dropdown-modified-input.component.spec.ts
  18. 1 1
      src/app/dynaform/components/dropdown-modified-input/dropdown-modified-input.component.ts
  19. 6 4
      src/app/dynaform/components/index.ts
  20. 5 0
      src/app/dynaform/components/native/password/password.component.html
  21. 0 0
      src/app/dynaform/components/native/password/password.component.scss
  22. 6 6
      src/app/dynaform/components/basicinput/basicinput.component.spec.ts
  23. 13 0
      src/app/dynaform/components/native/password/password.component.ts
  24. 3 0
      src/app/dynaform/components/native/radio/radio.component.html
  25. 0 0
      src/app/dynaform/components/native/radio/radio.component.scss
  26. 25 0
      src/app/dynaform/components/native/radio/radio.component.spec.ts
  27. 15 0
      src/app/dynaform/components/native/radio/radio.component.ts
  28. 3 0
      src/app/dynaform/components/native/select/select.component.html
  29. 0 0
      src/app/dynaform/components/native/select/select.component.scss
  30. 25 0
      src/app/dynaform/components/native/select/select.component.spec.ts
  31. 15 0
      src/app/dynaform/components/native/select/select.component.ts
  32. 1 1
      src/app/dynaform/components/basicinput/basicinput.component.html
  33. 0 0
      src/app/dynaform/components/native/text/text.component.scss
  34. 25 0
      src/app/dynaform/components/native/text/text.component.spec.ts
  35. 13 0
      src/app/dynaform/components/native/text/text.component.ts
  36. 6 0
      src/app/dynaform/components/native/textarea/textarea.component.html
  37. 0 0
      src/app/dynaform/components/native/textarea/textarea.component.scss
  38. 25 0
      src/app/dynaform/components/native/textarea/textarea.component.spec.ts
  39. 13 0
      src/app/dynaform/components/native/textarea/textarea.component.ts
  40. 2 0
      src/app/dynaform/interfaces/index.ts
  41. 11 0
      src/app/dynaform/libs/index.ts
  42. 66 30
      src/app/dynaform/models/index.ts
  43. 0 15
      src/app/pipes/keys.pipe.ts
  44. 3 0
      src/styles.scss

+ 2 - 2
dynaform.code-workspace

@@ -1,10 +1,10 @@
 {
 	"folders": [
 		{
-			"path": "."
+			"path": "src/app/dynaform"
 		},
 		{
-			"path": "src/app/dynaform"
+			"path": "src"
 		}
 	],
 	"settings": {}

+ 74 - 0
package-lock.json

@@ -338,6 +338,75 @@
         }
       }
     },
+    "@progress/kendo-angular-dateinputs": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-angular-dateinputs/-/kendo-angular-dateinputs-3.4.1.tgz",
+      "integrity": "sha512-w8kkQvUkHntCk7jVqDlnNBAN0oaRgNyZKioIxBiE8lyuq23k1It5hRfNA7BxBnazya/ZYZNXg6KBWNxj3Eu5XQ==",
+      "requires": {
+        "@progress/kendo-angular-popup": "^2.0.0",
+        "@progress/kendo-date-math": "^1.1.0",
+        "@progress/kendo-schematics": "^0.2.0",
+        "tslib": "^1.7.0"
+      }
+    },
+    "@progress/kendo-angular-intl": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-angular-intl/-/kendo-angular-intl-1.4.1.tgz",
+      "integrity": "sha512-Y8YyrAkgZxAPHoU/mV4mqR68De+uKISv9nmRURk+0jjt0cMcCMIcl+7ImZ4oWWHiYOTRErvHNKXCt53i+8fAXA==",
+      "requires": {
+        "@telerik/kendo-intl": "^1.3.0",
+        "tslib": "^1.7.0"
+      }
+    },
+    "@progress/kendo-angular-l10n": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-angular-l10n/-/kendo-angular-l10n-1.2.0.tgz",
+      "integrity": "sha512-XwZqk/MKS81AA2/7/9eGirpcppD3H5yKF9g9AlMydVmWdE54MPrTPR4OjRd3JN1d0I9lJOyae34v+BC33nHyGg==",
+      "requires": {
+        "tslib": "^1.7.0"
+      }
+    },
+    "@progress/kendo-angular-popup": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-angular-popup/-/kendo-angular-popup-2.4.1.tgz",
+      "integrity": "sha512-JcQ7flFAtkLJVcCt7iCKskuXzJyWnBC4UFqXpgAAN15PHpFtjgG8MvTXPgxMduamFjWujwZj5OY4UHVuY7kbIw==",
+      "requires": {
+        "@progress/kendo-popup-common": "^1.6.0",
+        "@progress/kendo-schematics": "^0.2.0",
+        "tslib": "^1.7.0"
+      }
+    },
+    "@progress/kendo-date-math": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-date-math/-/kendo-date-math-1.2.0.tgz",
+      "integrity": "sha512-SVj6lSajwgI/dxqn+Gm1Pe1T8aaQkMmf/1ALETx2pii6SMLDLdCH9tp6Kp+M5t+NDqppLVrdMYDXWng9buiU8A==",
+      "requires": {
+        "tslib": "^1.7.0"
+      }
+    },
+    "@progress/kendo-popup-common": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-popup-common/-/kendo-popup-common-1.6.0.tgz",
+      "integrity": "sha512-b560fcHaLrTmuap6POKpheoIQMJYbea+p7rKX2GOGZrxT2AGNxyWIHoi2nISxSbziCW2MB0R9LqAxAFXu4qymQ=="
+    },
+    "@progress/kendo-schematics": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-schematics/-/kendo-schematics-0.2.3.tgz",
+      "integrity": "sha512-W+6VNQWAdhH3a1/9f4Lbqq4Cy39V9ErNdA9R21xcAL49GPJPPGaZKsN6xOQvbs1NO41J/XUrPf3NIMWoEqDHug=="
+    },
+    "@progress/kendo-theme-bootstrap": {
+      "version": "2.13.5",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-theme-bootstrap/-/kendo-theme-bootstrap-2.13.5.tgz",
+      "integrity": "sha512-YNORkCZWBzBmrG5fmTn2KvQOoysoh51iRBKFEptrXNDPnhjnPhnm5DE5XeiLcqiEpPsy+p8vNWIu8w8lyamm4g==",
+      "requires": {
+        "@progress/kendo-theme-default": "^2.53.2"
+      }
+    },
+    "@progress/kendo-theme-default": {
+      "version": "2.53.2",
+      "resolved": "https://registry.npmjs.org/@progress/kendo-theme-default/-/kendo-theme-default-2.53.2.tgz",
+      "integrity": "sha512-i43rk88ipMAW9eHAoArTkJZcAGcUFqf7b55KLZLFh6LeAfUX3F+S8xznlcaYY2F6qYAMluS98kyXkv73R0S7KA=="
+    },
     "@schematics/angular": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.3.2.tgz",
@@ -377,6 +446,11 @@
         }
       }
     },
+    "@telerik/kendo-intl": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@telerik/kendo-intl/-/kendo-intl-1.4.1.tgz",
+      "integrity": "sha1-Rqficeuk7lkatk39yYobHgjxZUw="
+    },
     "@types/jasmine": {
       "version": "2.8.8",
       "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz",

+ 4 - 0
package.json

@@ -22,6 +22,10 @@
     "@angular/platform-browser-dynamic": "^5.2.0",
     "@angular/router": "^5.2.0",
     "@ng-bootstrap/ng-bootstrap": "^2.0.0",
+    "@progress/kendo-angular-dateinputs": "^3.4.1",
+    "@progress/kendo-angular-intl": "^1.4.1",
+    "@progress/kendo-angular-l10n": "^1.2.0",
+    "@progress/kendo-theme-bootstrap": "^2.13.5",
     "core-js": "^2.4.1",
     "json-formatter-js": "^2.2.0",
     "lodash": "^4.17.10",

+ 79 - 0
src/app/_mock/testfields.ts

@@ -0,0 +1,79 @@
+import { ValueTransformer } from './../dynaform/interfaces';
+import * as fmd from './../dynaform/models'; // fmd = Form Meta Data
+
+const basicTextField = new fmd.TextField({
+	name: 'basicTextField',
+	label: 'Field One',
+	placeholder: 'Type a value here'
+});
+
+const styledTextField = new fmd.TextField({
+	name: 'styledTextField',
+	placeholder: 'With a DOM id and CSS classes applied',
+	class: ['red', 'bgPaleBlue'],
+	id: 'yoyo'
+});
+
+const textareaField = new fmd.TextareaField({
+	name: 'textareaField',
+	placeholder: 'Type your long-winded comments here'
+});
+
+const passwordField = new fmd.PasswordField({
+	name: 'passwordField',
+	placeholder: 'It\'s a secret'
+});
+
+const checkButton = new fmd.CheckbuttonField({
+	name: 'checkButton',
+	value: '456'
+});
+
+const modifiers = ['Matches', 'Starts With', 'Contains'];
+const transformerFunctions: ValueTransformer = {
+	inputFn: val => {
+		let modifier = 'Starts With';
+		if (/^%.*?%$/.test(val)) {
+			modifier = 'Contains';
+		} else if (/^[^%].*?[^%]$/.test(val)) {
+			modifier = 'Matches';
+		} else if (/^%.*/.test(val)) {
+			modifier = 'Starts With';
+		}
+		const transformedVal = val.replace(/%/g, '').trim();
+		return { modifier: modifier, value: transformedVal };
+	},
+	outputFn: (mod, val) => {
+		let transformedValue;
+		switch(mod) {
+			case 'Starts With':
+				transformedValue = `%${val}`;
+				break;
+			case 'Contains':
+				transformedValue = `%${val}%`;
+				break;
+			case 'Matches':
+			default:
+				transformedValue = val;
+				break;
+		}
+		return transformedValue;
+	}
+};
+const dropDownModifiedInput = new fmd.DropdownModifiedInputField({
+	name: 'dropDownModifiedInput',
+	value: 'lovely',
+	modifiers,
+	transform: transformerFunctions
+});
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+export const formMetaDataObj = {
+	basicTextField,
+	styledTextField,
+	textareaField,
+	passwordField,
+	checkButton,
+	dropDownModifiedInput
+};

+ 3 - 75
src/app/app.component.ts

@@ -1,81 +1,9 @@
 import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';
-import { FormBuilder, FormGroup, FormArray, FormControl, Validators } from '@angular/forms';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { extractFormGroupData } from './dynaform/libs';
 
-import { ValueTransformer } from './dynaform/interfaces';
+import { formMetaDataObj } from './_mock/testfields';
 
-// ---------------------------------------------------------------------------------------------------------------------
-// For Dynaform
-
-import * as fmd from './dynaform/models/index'; // fmd = Form Meta Data
-
-// Utility function for extracting a FormGroup from a form metadata object - currently non-nested only
-import { reduce } from 'lodash/fp';
-const reducerIteree = (res, field) => Object.assign(res, { [field.name]: field.value || '' });
-const _extractFormGroupData = reduce(reducerIteree, {});
-const extractFormGroupData = nonNestedFormMetaLevel => _extractFormGroupData(nonNestedFormMetaLevel);
-
-// ---------------------------------------------------------------------------------------------------------------------
-
-const basicTextField = new fmd.BasicField({
-	name: 'basicTextField',
-	label: 'Field One',
-	placeholder: 'Type a value here',
-	class: ['red', 'bgPaleBlue'],
-	id: 'yoyo'
-});
-
-const checkButton = new fmd.BasicField({
-	name: 'checkButton',
-	type: 'Checkbutton',
-	value: '456',
-});
-
-const modifiers = ['Matches', 'Starts With', 'Contains'];
-const transformerFunctions: ValueTransformer = {
-	inputFn: val => {
-		let modifier = 'Starts With';
-		if (/^%.*?%$/.test(val)) {
-			modifier = 'Contains'
-		} else if (/^[^%].*?[^%]$/.test(val)) {
-			modifier = 'Matches'
-		} else if (/^%.*/.test(val)) {
-			modifier = 'Starts With';
-		}
-		const transformedVal = val.replace(/%/g, '').trim();
-		return { modifier: modifier, value: transformedVal };
-	},
-	outputFn: (mod, val) => {
-		let transformedValue;
-		switch(mod) {
-			case 'Starts With':
-				transformedValue = `%${val}`;
-				break;
-			case 'Contains':
-				transformedValue = `%${val}%`;
-				break;
-			case 'Matches':
-			default:
-				transformedValue = val;
-				break;
-		}
-		return transformedValue;
-	}
-};
-const dropDownModifiedInput = new fmd.DropdownModifiedInputField({
-	name: 'dropDownModifiedInput',
-	value: 'lovely',
-	modifiers,
-	transform: transformerFunctions
-});
-
-const formMetaDataObj = {
-	basicTextField,
-	checkButton,
-	dropDownModifiedInput
-};
-
-
-// --------------------------------------------------------------------------
 @Component({
 	selector: 'app-root',
 	templateUrl: './app.component.html',

+ 19 - 0
src/app/dynaform/components/_abstract/native-input/native-input.component.ts

@@ -0,0 +1,19 @@
+import { Input, OnInit } from '@angular/core';
+
+// @Component()
+export abstract class NativeInputComponent implements OnInit {
+
+	@Input()
+	control;
+
+	@Input()
+	meta;
+
+	exposeMetaInTemplate: string[] = [];
+
+	ngOnInit() {
+		// Move meta variables up a level, for direct access in templates
+		this.exposeMetaInTemplate.map(p => this[p] = this.meta[p] !== undefined ? this.meta[p] : this[p]);
+	}
+
+}

+ 0 - 21
src/app/dynaform/components/basicinput/basicinput.component.ts

@@ -1,21 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
-	selector: 'app-basicinput',
-	templateUrl: './basicinput.component.html',
-	styleUrls: ['./basicinput.component.scss']
-})
-export class BasicinputComponent implements OnInit {
-
-	@Input()
-	control;
-
-	@Input()
-	meta;
-
-	ngOnInit() {
-		// Move meta variables up a level, for direct access in templates
-		['placeholder'].map(p => this[p] = this.meta[p] || this[p]);
-	}
-
-}

src/app/dynaform/components/checkbutton-group/checkbutton-group.component.html → src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.html


src/app/dynaform/components/checkbutton-group/checkbutton-group.component.scss → src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.scss


src/app/dynaform/components/checkbutton-group/checkbutton-group.component.spec.ts → src/app/dynaform/components/custom/checkbutton-group/checkbutton-group.component.spec.ts


+ 1 - 4
src/app/dynaform/components/checkbutton-group/checkbutton-group.component.ts

@@ -1,4 +1,4 @@
-import { Component, Attribute, OnInit, Input, ElementRef } from '@angular/core';
+import { Component, Attribute, OnInit, Input } from '@angular/core';
 import { ControlContainer, FormGroup, FormControl } from '@angular/forms';
 
 @Component({
@@ -21,12 +21,9 @@ export class CheckbuttonGroupComponent implements OnInit {
 	firstControl: FormControl;
 
 	constructor(
-		private el: ElementRef,
 		private cc: ControlContainer,
 		@Attribute('firstEnablesRest') private firstEnablesRest
 	) {
-		// Read the classes applied to the host element, to pass through to the template
-		this.extraClass = el.nativeElement.className;
 		this.firstEnablesRest = firstEnablesRest === ''; // True if 'firstEnablesRest' exists as component attribute
 	}
 

src/app/dynaform/components/checkbutton/checkbutton.component.html → src/app/dynaform/components/custom/checkbutton/checkbutton.component.html


src/app/dynaform/components/checkbutton/checkbutton.component.scss → src/app/dynaform/components/custom/checkbutton/checkbutton.component.scss


src/app/dynaform/components/checkbutton/checkbutton.component.spec.ts → src/app/dynaform/components/custom/checkbutton/checkbutton.component.spec.ts


src/app/dynaform/components/checkbutton/checkbutton.component.ts → src/app/dynaform/components/custom/checkbutton/checkbutton.component.ts


src/app/dynaform/components/dropdown-modified-input/dropdown-modified-input.component.html → src/app/dynaform/components/custom/dropdown-modified-input/dropdown-modified-input.component.html


src/app/dynaform/components/dropdown-modified-input/dropdown-modified-input.component.spec.ts → src/app/dynaform/components/custom/dropdown-modified-input/dropdown-modified-input.component.spec.ts


+ 1 - 1
src/app/dynaform/components/dropdown-modified-input/dropdown-modified-input.component.ts

@@ -1,6 +1,6 @@
 import { Component, OnInit, Input, forwardRef } from '@angular/core';
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
-import { ValueTransformer }	from './../../interfaces';
+import { ValueTransformer }	from './../../../interfaces';
 
 @Component({
 	selector: 'app-dropdown-modified-input',

+ 6 - 4
src/app/dynaform/components/index.ts

@@ -1,8 +1,10 @@
 // Barrel grouping all form field components
 // See https://basarat.gitbooks.io/typescript/docs/tips/barrel.html
 
-export { BasicinputComponent } from './basicinput/basicinput.component';
-export { DropdownModifiedInputComponent } from './dropdown-modified-input/dropdown-modified-input.component';
-export { CheckbuttonComponent } from './checkbutton/checkbutton.component';
-// export { CheckbuttonGroupComponent } from './checkbutton-group/checkbutton-group.component';
+export { TextComponent } from './native/text/text.component';
+export { TextareaComponent } from './native/textarea/textarea.component';
+export { PasswordComponent } from './native/password/password.component';
+export { DropdownModifiedInputComponent } from './custom/dropdown-modified-input/dropdown-modified-input.component';
+export { CheckbuttonComponent } from './custom/checkbutton/checkbutton.component';
+// export { CheckbuttonGroupComponent } from './custom/checkbutton-group/checkbutton-group.component';
 

+ 5 - 0
src/app/dynaform/components/native/password/password.component.html

@@ -0,0 +1,5 @@
+<input type="password"
+	[formControl]="control"
+	[placeholder]="placeholder"
+	class="form-control form-control-sm"
+>

src/app/dynaform/components/basicinput/basicinput.component.scss → src/app/dynaform/components/native/password/password.component.scss


+ 6 - 6
src/app/dynaform/components/basicinput/basicinput.component.spec.ts

@@ -1,20 +1,20 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { BasicinputComponent } from './basicinput.component';
+import { PasswordComponent } from './password.component';
 
-describe('BasicinputComponent', () => {
-  let component: BasicinputComponent;
-  let fixture: ComponentFixture<BasicinputComponent>;
+describe('PasswordComponent', () => {
+  let component: PasswordComponent;
+  let fixture: ComponentFixture<PasswordComponent>;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [ BasicinputComponent ]
+      declarations: [ PasswordComponent ]
     })
     .compileComponents();
   }));
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(BasicinputComponent);
+    fixture = TestBed.createComponent(PasswordComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });

+ 13 - 0
src/app/dynaform/components/native/password/password.component.ts

@@ -0,0 +1,13 @@
+import { Component } from '@angular/core';
+import { NativeInputComponent } from './../../_abstract/native-input/native-input.component';
+
+@Component({
+	selector: 'app-password',
+	templateUrl: './password.component.html',
+	styleUrls: ['./password.component.scss']
+})
+export class PasswordComponent extends NativeInputComponent {
+
+	exposeMetaInTemplate: string[] = ['placeholder'];
+
+}

+ 3 - 0
src/app/dynaform/components/native/radio/radio.component.html

@@ -0,0 +1,3 @@
+<p>
+  radio works!
+</p>

+ 0 - 0
src/app/dynaform/components/native/radio/radio.component.scss


+ 25 - 0
src/app/dynaform/components/native/radio/radio.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RadioComponent } from './radio.component';
+
+describe('RadioComponent', () => {
+  let component: RadioComponent;
+  let fixture: ComponentFixture<RadioComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ RadioComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(RadioComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 15 - 0
src/app/dynaform/components/native/radio/radio.component.ts

@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-radio',
+  templateUrl: './radio.component.html',
+  styleUrls: ['./radio.component.scss']
+})
+export class RadioComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 3 - 0
src/app/dynaform/components/native/select/select.component.html

@@ -0,0 +1,3 @@
+<p>
+  select works!
+</p>

+ 0 - 0
src/app/dynaform/components/native/select/select.component.scss


+ 25 - 0
src/app/dynaform/components/native/select/select.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SelectComponent } from './select.component';
+
+describe('SelectComponent', () => {
+  let component: SelectComponent;
+  let fixture: ComponentFixture<SelectComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ SelectComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SelectComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 15 - 0
src/app/dynaform/components/native/select/select.component.ts

@@ -0,0 +1,15 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-select',
+  templateUrl: './select.component.html',
+  styleUrls: ['./select.component.scss']
+})
+export class SelectComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 1 - 1
src/app/dynaform/components/basicinput/basicinput.component.html

@@ -2,4 +2,4 @@
 	[formControl]="control"
 	[placeholder]="placeholder"
 	class="form-control form-control-sm"
-	>
+>

+ 0 - 0
src/app/dynaform/components/native/text/text.component.scss


+ 25 - 0
src/app/dynaform/components/native/text/text.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TextComponent } from './text.component';
+
+describe('TextComponent', () => {
+  let component: TextComponent;
+  let fixture: ComponentFixture<TextComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TextComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TextComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 13 - 0
src/app/dynaform/components/native/text/text.component.ts

@@ -0,0 +1,13 @@
+import { Component } from '@angular/core';
+import { NativeInputComponent } from './../../_abstract/native-input/native-input.component';
+
+@Component({
+	selector: 'app-text',
+	templateUrl: './text.component.html',
+	styleUrls: ['./text.component.scss']
+})
+export class TextComponent extends NativeInputComponent {
+
+	exposeMetaInTemplate: string[] = ['placeholder'];
+
+}

+ 6 - 0
src/app/dynaform/components/native/textarea/textarea.component.html

@@ -0,0 +1,6 @@
+<textarea
+	[formControl]="control"
+	[placeholder]="placeholder"
+	class="form-control form-control-sm"
+	rows="5"
+></textarea>

+ 0 - 0
src/app/dynaform/components/native/textarea/textarea.component.scss


+ 25 - 0
src/app/dynaform/components/native/textarea/textarea.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TextareaComponent } from './textarea.component';
+
+describe('TextareaComponent', () => {
+  let component: TextareaComponent;
+  let fixture: ComponentFixture<TextareaComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TextareaComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TextareaComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 13 - 0
src/app/dynaform/components/native/textarea/textarea.component.ts

@@ -0,0 +1,13 @@
+import { Component } from '@angular/core';
+import { NativeInputComponent } from './../../_abstract/native-input/native-input.component';
+
+@Component({
+	selector: 'app-textarea',
+	templateUrl: './textarea.component.html',
+	styleUrls: ['./textarea.component.scss']
+})
+export class TextareaComponent extends NativeInputComponent {
+
+	exposeMetaInTemplate: string[] = ['placeholder'];
+
+}

+ 2 - 0
src/app/dynaform/interfaces/index.ts

@@ -1,3 +1,5 @@
+
+
 export interface ValueTransformer {
 	inputFn: (value: string) => { modifier: string, value: string };
 	outputFn: (modifier: string, value: string) => string;

+ 11 - 0
src/app/dynaform/libs/index.ts

@@ -0,0 +1,11 @@
+import { reduce } from 'lodash/fp';
+
+// Utility function for extracting a FormGroup from a form metadata object - currently non-nested only
+const reducerIteree = (res, field) => Object.assign(res, { [field.name]: field.value || '' });
+const _extractFormGroupData = reduce(reducerIteree, {});
+const extractFormGroupData = nonNestedFormMetaLevel => _extractFormGroupData(nonNestedFormMetaLevel);
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Exports
+
+export { extractFormGroupData };

+ 66 - 30
src/app/dynaform/models/index.ts

@@ -7,20 +7,12 @@
 import { ValidatorFn, AsyncValidatorFn } from '@angular/forms';
 import { ValueTransformer } from './../interfaces';
 
-// ---------------------------------------------------------------------------------------------------------------------
-// Types & Interfaces
-
-interface Option {
-	label: string;
-	value: string;
-}
-
-interface BasicFieldMetaData {
+interface SimpleFieldMetaData {
 	name: string; 								// The FormControl name
 	origin?: string;							// Location in API-returned model - defaults to name
 	type?: string; 								// The component type e.g. BasicInput, Checkbutton, Timepicker, etc
 	label?: string;								// The field label - defaults to unCamelCased name if not supplied
-	value?: string;								// The field value - defaults to empty string if not supplied
+	value?: any;								// The field value - defaults to empty string if not supplied
 	placeholder?: string;						// Optional placeholder text
 	class?: string | Array<string>;				// CSS classes to apply
 	id?: string;								// CSS id to apply
@@ -30,37 +22,42 @@ interface BasicFieldMetaData {
 	valFailureMsgs?: StringMap;					// Validation failure messages - display appropriate message if validation fails
 }
 
-interface OptionsFieldMetaData extends BasicFieldMetaData {
-	options: Option[];							// Array of Options - for select, radio-button-group and other 'multiple-choice' types
+interface Option {
+	label: string;
+	value: string;
 }
 
-interface DropdownModifiedInputFieldMetaData extends BasicFieldMetaData {
+interface OptionsFieldMetaData extends SimpleFieldMetaData {
+	options: Option[];							// Array of Options - for select, radio-button-group and other 'multiple-choice' types
+}
+interface DropdownModifiedInputFieldMetaData extends SimpleFieldMetaData {
 	modifiers: string[];
 	transform: ValueTransformer;
 }
 
-interface TimePickerFieldMetaData extends BasicFieldMetaData {
+interface TimePickerFieldMetaData extends SimpleFieldMetaData {
 	// To add...
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
 // Form Field MetaData Models
+// ---------------------------------------------------------------------------------------------------------------------
+// Base Implementations
 
-class BasicField
-{
-	type: string = 'Basicinput';
+class SimpleField {
+	type = 'text';
 	name: string;
 	origin?: string;
 	label?: string;
-	value: string = '';
-	placeholder: string = '';
+	value;
+	placeholder = '';
 	class?: string | Array<string>;
 	isDisabled?: boolean;
 	validators: Array<ValidatorFn> = [];
 	asyncValidators: Array<AsyncValidatorFn> = [];
 	valFailureMsgs: StringMap = {};
 
-	constructor(meta: BasicFieldMetaData) {
+	constructor(meta: SimpleFieldMetaData) {
 		Object.assign(this, meta);
 		if (!this.origin) {
 			// If origin is not supplied it's the same as the name
@@ -74,18 +71,50 @@ class BasicField
 	}
 }
 
-class OptionsField extends BasicField
-{
-	type: string = 'OptionsField';
+class OptionsField extends SimpleField {
 	options: Option[];
 	constructor(meta: OptionsFieldMetaData) {
 		super(meta);
 	}
 }
 
-class DropdownModifiedInputField extends BasicField
-{
-	type: string = 'DropdownModifiedInput';
+// ---------------------------------------------------------------------------------------------------------------------
+// Concrete Implmemntations (suuplied for convenience)
+
+class TextField extends SimpleField {
+	type = 'Text';
+}
+
+class TextareaField extends SimpleField {
+	type = 'Textarea';
+}
+
+class PasswordField extends SimpleField {
+	type = 'Password';
+}
+
+class SelectField extends OptionsField {
+	type = 'Select';
+}
+
+class RadioField extends OptionsField {
+	type = 'Radio';
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Custom Form Component Models
+
+class CheckbuttonField extends SimpleField {
+	type = 'Checkbutton';
+}
+
+class CheckbuttonGroup extends SimpleField {
+	value: (string | boolean)[];
+	type = 'CheckbuttonGroup';
+}
+
+class DropdownModifiedInputField extends SimpleField {
+	type = 'DropdownModifiedInput';
 	modifiers: string[];
 	transform: ValueTransformer;
 	constructor(meta: DropdownModifiedInputFieldMetaData) {
@@ -93,18 +122,25 @@ class DropdownModifiedInputField extends BasicField
 	}
 }
 
-class TimePickerField extends BasicField
-{
+// ---------------------------------------------------------------------------------------------------------------------
+// Kendo Form Component Models
+
+class TimePickerField extends SimpleField {
 	// To add...
 	constructor(meta: TimePickerFieldMetaData) {
 		super(meta);
 	}
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+// ---------------------------------------------------------------------------------------------------------------------
 // ---------------------------------------------------------------------------------------------------------------------
 // Exports
 
-export { BasicField, OptionsField, DropdownModifiedInputField, TimePickerField };
+export {
+	TextField, TextareaField, PasswordField, SelectField, RadioField,
+	CheckbuttonField, CheckbuttonGroup, DropdownModifiedInputField, TimePickerField
+};
 
 
 /* *********************************************************************************************************************
@@ -127,7 +163,7 @@ for (field in model) {
 	if (field in overrides) {
 		Object.assign(modeledMeta, FieldFactory(model, overrides[field])); // FieldFactory returns a new field model of the specified type
 	} else {
-		Object.assign(modeledMeta, new BasicField(field)); // Defaults to basic text field
+		Object.assign(modeledMeta, new SimpleField(field)); // Defaults to basic text field
 	}
  }
 

+ 0 - 15
src/app/pipes/keys.pipe.ts

@@ -1,15 +0,0 @@
-// Take an object and return an array containing the names of its keys
-// e.g. { a: 1, b: 2, c: 3 } => ['a', 'b', 'c']
-
-import { Pipe, PipeTransform } from '@angular/core';
-
-@Pipe({
-	name: 'keys'
-})
-export class KeysPipe implements PipeTransform {
-
-	transform(value: object): Array<string> {
-		return Object.keys(value);
-	}
-
-}

+ 3 - 0
src/styles.scss

@@ -1,5 +1,8 @@
 /* You can add global styles to this file, and also import other style files */
 
+@import url("https://use.fontawesome.com/releases/v5.0.6/css/all.css");
+@import "~@progress/kendo-theme-bootstrap/scss/all";
+
 h1 {
 	color: magenta;
 	border-bottom: 4px blueviolet solid;