Переглянути джерело

Testing callbacks from deeply nested groups

Richard Knight 6 роки тому
батько
коміт
af95f7a4e9

+ 3 - 0
ng-dynaform.code-workspace

@@ -5,6 +5,9 @@
 		},
 		{
 			"path": "src\\app\\dynaform"
+		},
+		{
+			"path": "src/app"
 		}
 	],
 	"settings": {}

+ 1 - 1
src/app/_mock/testfields.v5.ts

@@ -1,4 +1,4 @@
-// TESTS: ADADADA
+// TESTS: Deep nesting
 
 const model = {
 	dynaformtest: {

+ 1 - 1
src/app/_mock/testfields.v6.ts

@@ -1,4 +1,4 @@
-// TESTS: Validators
+// TESTS: Deep Nesting and Validators
 
 import { Validators } from '@angular/forms';
 

+ 60 - 0
src/app/_mock/testfields.v9.ts

@@ -0,0 +1,60 @@
+// TESTS: Button callbacks in deeply nested forms
+
+import { Validators } from '@angular/forms';
+
+const model = {
+	// field: '',
+	// field2: '',
+	// dynaformtest: {
+	// 	a: '',
+	// 	valTest: '',
+	// 	b: 'Value 2',
+	// 	c: 'Maybe',
+	// 	d: {
+	// 		e: '',
+	// 		f: 555,
+	// 		g: {
+	// 			h: true,
+	// 			i: false
+	// 		}
+	// 	},
+	// 	z: 'THE END'
+	// }
+};
+
+const meta = {
+	buttonGroup: { type: 'buttonGroup', label: 'Button Group',
+		meta: [
+			{ label: 'Say Hello', fnId: 'SAYHELLO' },
+			{ label: 'Say Cheese', fnId: 'SAYCHEESE' }
+		]
+	},
+	dynaformtest: {
+		meta: {
+			valTest: {
+				validators: [ Validators.required, Validators.minLength(4) ],
+				// perhaps have some standard messages for standard failures in the Object.prototype ??
+				valFailureMessages: {
+					'required': 'This field is required',
+					'minlength': 'Please type something longer'
+				}
+			},
+			b: { type: 'checkbutton' },
+			c: { label: 'Property Three', type: 'radio', options: ['Yes', 'No', 'Maybe'], horizontal: 1 },
+			d: {
+				meta: {
+					e: { type: 'radio', 'label': 'Does it work yet?', validators: Validators.required },
+					f: { type: 'datepicker' },
+					deeplyNestedButtonGroup: { type: 'buttonGroup', label: 'Deeply Nested Buttons',
+						meta: [
+							{ label: 'Say Hello', fnId: 'SAYHELLO' },
+							{ label: 'Say Cheese', fnId: 'SAYCHEESE' }
+						]
+					}
+				}
+			}
+		}
+	}
+};
+
+export { model, meta };

+ 1 - 1
src/app/app.component.html

@@ -4,7 +4,7 @@
 			<h1>NgDynaform</h1>
 			<p>
 			Dynamic Form Layout Module<br>
-			Load different tests by appending a query param to the URL <b>?test=N</b> (where N is a number between 1 and 8).<br>
+			Load different tests by appending a query param to the URL <b>?test=N</b> (where N is a number between 1 and 9).<br>
 			NOTE: Model set to update on change, but this can be set to blur or submit for less re-rendering.
 			</p>
 		</div>

+ 19 - 2
src/app/app.component.ts

@@ -10,8 +10,9 @@ import * as test5 from './_mock/testfields.v5';
 import * as test6 from './_mock/testfields.v6';
 import * as test7 from './_mock/testfields.v7';
 import * as test8 from './_mock/testfields.v8';
+import * as test9 from './_mock/testfields.v9';
 
-const testdata = [ test1, test2, test3, test4, test5, test6, test7, test8 ];
+const testdata = [ test1, test2, test3, test4, test5, test6, test7, test8, test9 ];
 
 const defatltTest = 8;
 
@@ -46,6 +47,7 @@ export class AppComponent implements OnInit, OnChanges {
 		console.log('Model', model);
 		console.log('Meta', meta);
 
+		/*
 		// Test autoMeta
 		const auto = this.dynaform.autoMeta(model);
 		console.log(auto);
@@ -61,19 +63,34 @@ export class AppComponent implements OnInit, OnChanges {
 		// Test building the FormGroup
 		const fg = this.dynaform.buildFormGroup(fsm);
 		console.log(fg);
+		*/
 
 		// Build the FormGroup and Modeled Metadata from the imported test data
-		const dynaformdata = this.dynaform.build(model, meta);
+		const dynaformdata = this.dynaform.build(model, meta, true);
 		({ form: this.form, meta: this.meta } = dynaformdata);
 
 		console.log('%c *** Modeled MetaData *** ', this.hCssGreen);
 		console.dir(this.meta);
 		console.log('%c *** FormGroup *** ', this.hCssGreen);
 		console.dir(this.form);
+
+		// Registering callbacks
+		this.dynaform.register({
+			'SAYHELLO': this.sayHello,
+			'SAYCHEESE': this.sayCheese
+		}, this);
 	}
 
 	ngOnChanges() {
 		console.log(this.form.errors);
 	}
+
+	sayHello() {
+		alert('HELLO');
+	}
+
+	sayCheese() {
+		alert('CHEESE');
+	}
 }
 

+ 1 - 0
src/app/dynaform/components/nocontrol/button-group/button-group.component.ts

@@ -20,6 +20,7 @@ export class ButtonGroupComponent implements OnInit {
 	}
 	
 	handle(fnId: string, e: Event) {
+		console.log('BG Component', fnId);
 		e.preventDefault();
 		(e.target as HTMLElement).blur();
 		this.call.emit(fnId);

+ 4 - 1
src/app/dynaform/directives/dynafield.directive.ts

@@ -96,7 +96,10 @@ export class DynafieldDirective extends NgControl implements OnInit, OnDestroy {
 
 			// Listen for 'call' Output events and send them onwards
 			if (instance.call) {
-				instance.call.subscribe(val => this.call.emit(val));
+				instance.call.subscribe(val => { 
+					console.log('Directive', val);
+					this.call.emit(val);
+				});
 			}
 
 			// Add id and classes (as specified)

+ 16 - 9
src/app/dynaform/dynaform.component.ts

@@ -77,7 +77,7 @@ export class DynaformComponent implements OnInit {
 		// drill down to find the FormGroup's metadata
 		const path = [...this.path]; // Clone to avoid mutating this.path
 		const metaDataKeysExpected = this.controlNames.join(',');
-		while (path.length && metaDataKeysExpected !== Object.keys(this.formMetaData).join(',')) {
+		while (path.length && metaDataKeysExpected !== this.getContolKeysCSVFromMetadata(this.formMetaData)) {
 			const branch = path.pop();
 			this.formMetaData = this.formMetaData[branch].meta;
 		}
@@ -86,15 +86,12 @@ export class DynaformComponent implements OnInit {
 		// Check we've got a "FormGroup <---> MetaData" match
 		//
 		// const metaDataKeys = Object.keys(this.formMetaData).join(','); // OLD VERSION - before we introduced entities like ButtonGroup that don't create FormControls
-		const metaDataKeys = Object.entries(this.formMetaData)
-							.filter(([key, val]) => !(<StringMap>val).noFormControls)
-							.reduce((acc, [key]) => [...acc, key], [])
-							.join(',');
-		if (metaDataKeys !== metaDataKeysExpected) {
+		const metaDataKeysFound = this.getContolKeysCSVFromMetadata(this.formMetaData);
+		if (metaDataKeysFound !== metaDataKeysExpected) {
 			throw new Error(`
 				Dynaform can't match FormGroup's controls with metadata
 				Expected ${metaDataKeysExpected}
-				Got ${metaDataKeys}`
+				Got ${metaDataKeysFound}`
 			);
 		}
 	}
@@ -119,7 +116,6 @@ export class DynaformComponent implements OnInit {
 	}
 	
 	getTemplateContext(controlName: string): DynarowContext {
-		// console.log(controlName);
 		return {
 			control: this.formGroup.get(controlName),
 			meta: this.formMetaData[controlName]
@@ -132,7 +128,6 @@ export class DynaformComponent implements OnInit {
 
 	getValidationErrors() {
 		if (!this.formGroup.valid) {
-			// const errors = SuperForm.getAllErrors(this.formGroup);
 			const errorsFlat = SuperForm.getAllErrorsFlat(this.formGroup);
 			return errorsFlat;		
 		}
@@ -140,7 +135,19 @@ export class DynaformComponent implements OnInit {
 	}
 
 	handle(val) {
+		console.log('Dynaform Component', val);
 		this.call.emit(val);
 	}
 
+	private getContolKeysCSVFromMetadata(metadata): string {
+		// Return CSV of control keys references in nesting-levels metadata,
+		// excluding metadata points that don't create FormControls, FromGroups or FormArrays
+		// (identified by their 'noFormControsl' flag)
+		// e.g. ButtonGroups, HTMLChunks, etc.
+		return Object.entries(metadata)
+				.filter(([key, val]) => !(<StringMap>val).noFormControls)
+				.reduce((acc, [key]) => [...acc, key], [])
+				.join(',');
+	}
+
 }

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

@@ -1,11 +0,0 @@
-<<<<<<< HEAD
-export { DynaformComponent } from './dynaform.component';
-export { DynaformService } from './services/dynaform.service';
-export { ModelMapperService } from './services/model-mapper.service';
-export { standardModifiers, standardTransformer, arrayToMeta } from './utils';
-=======
-export { DynaformService } from './services/dynaform.service';
-export { ModelMapperService } from './services/model-mapper.service';
-export { standardModifiers, standardTransformer } from './utils';
->>>>>>> a3dfe85e06beb3d977f49241b2360bb3e2f4a09d
-

+ 14 - 4
src/app/dynaform/models/field.model.ts

@@ -223,11 +223,20 @@ class Container {
 // ---------------------------------------------------------------------------------------------------------------------
 // Button Group
 
-interface Button {
+interface ButtonInterface {
 	label: string;
 	fnId: string;
-	class: string;
-	icon: string;
+	class?: string;
+	icon?: string;
+}
+
+class Button implements ButtonInterface {
+	label;
+	fnId;
+	class: string = 'btn-primary';
+	constructor(buttonProps) {
+		Object.assign(this, buttonProps);
+	}
 }
 
 class ButtonGroup {
@@ -236,8 +245,9 @@ class ButtonGroup {
 	label = '';
 	meta: Button[];
 	readonly noFormControls = true; // Indicates this has no FormControls associated with it
-	constructor(meta: StringMap) {
+	constructor(meta) {
 		Object.assign(this, meta);
+		this.meta = this.meta.map(b => b instanceof Button ? b : new Button(b));
 	}
 }
 

+ 1 - 0
src/app/dynaform/services/dynaform.service.ts

@@ -110,6 +110,7 @@ export class DynaformService {
 	}
 	
 	call(fnId: string) {
+		console.log('Dynaform Service', fnId);
 		// Handle callback events
 		try {
 			this.callbacks[fnId]();

+ 0 - 46
src/app/dynaform/utils.ts.orig

@@ -1,46 +0,0 @@
-// Some standard utility functions for Dyynaform consumers
-
-import { ValueTransformer } from './interfaces';
-
-// Dropdown Modified Input - Starts With / Contains / Matches
-const standardModifiers = ['Starts with', 'Contains', 'Matches'];
-const standardTransformer: 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;
-	}
-};
-
-<<<<<<< HEAD
-
-// Utility function for casting an array to metadata (useful for components that render FormGroups)
-const arrayToMeta = array => array.map(val => ({ name: val, 'value' : val }));
-
-export { standardModifiers, standardTransformer, arrayToMeta };
-=======
-export { standardModifiers, standardTransformer };
->>>>>>> a3dfe85e06beb3d977f49241b2360bb3e2f4a09d

+ 7 - 0
src/styles.scss

@@ -87,3 +87,10 @@ div.col-sm-8 {
 	font-size: 1.1em;
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+// Button Groups
+
+.buttongroup a {
+	margin-right: 4px;
+}
+