Kaynağa Gözat

Updates to resolve copy-and-paste keyups putting controls into PENDING state

Richard Knight 4 yıl önce
ebeveyn
işleme
a7442179be

+ 2 - 2
src/app/_mock/testfields.v14.ts

@@ -1,5 +1,5 @@
 // ---------------------------------------------------------------------------------------------------------------------
-// TESTS: Repeating Groups Of Fields in Single Mode ( 1 -> N arrays of containers for multiple fields )
+// TESTS: Repeating Groups Of Fields in SINGLE Mode ( 1 -> N arrays of containers for multiple fields )
 // ---------------------------------------------------------------------------------------------------------------------
 
 const model = {
@@ -26,7 +26,7 @@ const meta = {
 		showAddControl: true,
 		showDeleteControl: true,
 		display: 'SINGLE', // ALL or SINGLE,
-		primaryField: 'a',
+		primaryField: 'a', // Field used to label the group selector buttons (by current value)
 		meta: [
 			{
 				a: { label: 'Field A' },

+ 18 - 0
src/app/_mock/testfields.v15.ts

@@ -0,0 +1,18 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// TESTS: Repeating Single Fields (i.e. fields not part of a repeating container group)
+// ---------------------------------------------------------------------------------------------------------------------
+
+const model = {};
+
+const meta = {
+	repeatingField: {
+		type: 'text',
+		minRepeat: 1,
+		maxRepeat: 5,
+		initialRepeat: 3,
+		showAddControl: true,
+		showDeleteControl: true
+	}
+};
+
+export { model, meta };

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

@@ -16,8 +16,9 @@ import * as test11 from './_mock/testfields.v11';
 import * as test12 from './_mock/testfields.v12';
 import * as test13 from './_mock/testfields.v13';
 import * as test14 from './_mock/testfields.v14';
+import * as test15 from './_mock/testfields.v15';
 
-const testdata = [ test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11, test12, test13, test14 ];
+const testdata = [ test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11, test12, test13, test14, test15 ];
 
 const defatltTest = 1;
 

+ 9 - 26
src/app/dynaform/components/_abstract/native-input.component.ts

@@ -2,7 +2,7 @@ import { OnInit, OnDestroy, Input, Output, ChangeDetectorRef, EventEmitter } fro
 import { FormControl } from '@angular/forms';
 import { FriendlyValidationErrorsService } from './../../services/friendly-validation-errors.service';
 import { Subject, Subscription } from 'rxjs';
-import { debounceTime } from 'rxjs/operators';
+import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
 
 export abstract class NativeInputComponent implements OnInit, OnDestroy {
 
@@ -23,8 +23,6 @@ export abstract class NativeInputComponent implements OnInit, OnDestroy {
 	_meta: StringMap<any>;
 
 	pendingValidation: boolean;
-	hasFocus: boolean = false;
-	waitForFirstChange: boolean = false;
 	keyUp$: Subject<string> = new Subject();
 
 	valueSBX: Subscription;
@@ -58,14 +56,14 @@ export abstract class NativeInputComponent implements OnInit, OnDestroy {
 				}
 			});
 		}
-		this.keyUpSBX = this.keyUp$.pipe(debounceTime(this.keyUpValidationDelay)).subscribe(val => {
-			if (this.hasFocus) {
-				// THE ORDER OF THE NEXT THREE LINES IS IMPORTANT!
-				// We MUST make the control as dirty before setting the value to avoid corrupting the initialValue recorded in Dynaform's makeAsyncTest utility
-				this.control.markAsTouched();
-				this.control.markAsDirty();
-				this.control.setValue(val);
-			}
+		this.keyUpSBX = this.keyUp$.pipe(
+			debounceTime(this.keyUpValidationDelay),
+			distinctUntilChanged(),
+			tap(val => console.log('CHANGED', val))
+		).subscribe(val => {
+			this.control.markAsTouched();
+			this.control.markAsDirty();
+			this.control.setValue(val);
 		});
 	}
 
@@ -114,23 +112,8 @@ export abstract class NativeInputComponent implements OnInit, OnDestroy {
 		return this._meta.valFailureMsgs[key] || this.valErrsService.getFriendly(key, this.control.errors[key]);
 	}
 
-	gainFocus(): void {
-		this.hasFocus = true;
-		this.waitForFirstChange = true;
-	}
-
-	loseFocus(): void {
-		this.hasFocus = false;
-	}
-
 	handleKeyup(currentFieldValue: string): void {
 		this.keyUp$.next(currentFieldValue);
-		if (this.control.value && this.waitForFirstChange) {
-			// Hide any validation errors if the control has a previous value (set after Angular's first updateOn event) and its value has changed
-			// NOTE that this.control.value may lag behind currentFieldValue depending on the updateOn startegy chosen
-			this.control.markAsPending();
-			this.waitForFirstChange = false;
-		}
 	}
 
 }

+ 0 - 2
src/app/dynaform/components/clarity/datepicker/datepicker.component.html

@@ -2,8 +2,6 @@
 	<label [ngClass]="{ 'label-error': control.touched && control.invalid }">{{ label }}</label>
 	<input clrDate
 		[formControl]="control"
-		(focus)="gainFocus()"
-		(blur)="loseFocus()"
 		(keyup)="handleKeyup($event.target.value)"
 	>
 	<clr-control-error>{{ getFirstFailureMsg() }}</clr-control-error>

+ 0 - 2
src/app/dynaform/components/clarity/password/clr-password.component.html

@@ -3,8 +3,6 @@
 	<input clrPassword
 		[formControl]="control"
 		[placeholder]="placeholder"
-		(focus)="gainFocus()"
-		(blur)="loseFocus()"
 		(keyup)="handleKeyup($event.target.value)"
 	>
 	<clr-control-error>{{ getFirstFailureMsg() }}</clr-control-error>

+ 0 - 4
src/app/dynaform/components/clarity/text/clr-text.component.html

@@ -3,8 +3,6 @@
 	<input clrInput type="text"
 		[formControl]="control"
 		[placeholder]="placeholder"
-		(focus)="gainFocus()"
-		(blur)="loseFocus()"
 		(keyup)="handleKeyup($event.target.value)"
 		spellcheck="false"
 	>
@@ -19,8 +17,6 @@
 			#field
 			[formControl]="control"
 			[placeholder]="placeholder"
-			(focus)="gainFocus()"
-			(blur)="loseFocus()"
 			(keyup)="handleKeyup($event.target.value)"
 			spellcheck="false"
 		>

+ 0 - 2
src/app/dynaform/components/clarity/textarea/clr-textarea.component.html

@@ -3,8 +3,6 @@
 	<textarea clrTextarea
 		[formControl]="control"
 		[placeholder]="placeholder"
-		(focus)="gainFocus()"
-		(blur)="loseFocus()"
 		(keyup)="handleKeyup($event.target.value)"
 		rows="5"
 	></textarea>

+ 34 - 6
src/app/dynaform/services/_formdata-utils.ts

@@ -31,8 +31,8 @@ import * as fmdModels from '../models/field.model';
 // AutoMeta: Generate Automatic Metadata from a model
 // ---------------------------------------------------------------------------------------------------------------------
 
-const isScalar = val => typeof val === 'boolean' || typeof val === 'number' || typeof val === 'string';
-const isArray = val => Array.isArray(val);
+const isScalar = (val: any) => typeof val === 'boolean' || typeof val === 'number' || typeof val === 'string';
+const isArray = (val: any) => Array.isArray(val);
 
 const keyValPairToMeta = (val: any, key: string) => ({ name: key, [isScalar(val) ? 'value' : 'meta']: val });
 const keyValPairToMetaRecursive = ( [key, val] ) => {
@@ -72,11 +72,10 @@ const combineExtraMeta = (metaG, extraMeta, createFromExtra = false, containerSe
 	Object.entries(extraMeta).forEach(([key, val]: [string, StringMap<any>]) => {
 		if (typeof metaG[key] === 'object' || createFromExtra) { // If the key exists (in the model) OR we're creating from metadata
 			const isCon = isContainer(val);
-			const isRepeating = isRepeatingContainer(val);
 			const metaFoG = metaG[key] || {};
 			const seed = isCon ? {} : containerSeed; // Container's don't seed themselves, only their children
 
-			if (isRepeating)
+			if (isRepeatingContainer(val))
 			{
 				// We've got a Repeating Container
 				const baseObjWithAllKeys = getRCBaseObjectWithAllKeys(metaFoG, val, createFromExtra);
@@ -102,9 +101,10 @@ const combineExtraMeta = (metaG, extraMeta, createFromExtra = false, containerSe
 					val['seed'] || containerSeed
 				);
 			}
-			else
+			else // ----------------------------------------------
 			{
 				// We've got a Container or a Field
+				/*
 				const extra = isCon ?
 					{
 						...val,
@@ -116,7 +116,31 @@ const combineExtraMeta = (metaG, extraMeta, createFromExtra = false, containerSe
 						)
 					}
 					:
-					val;
+					isRepeatingField(val) ? [ val, val, val ] : val; // <----------! HERE WE BE WORKING!
+				*/
+				let extra: StringMap<any>;
+				if (isCon) {
+					extra = {
+						...val,
+						meta: combineExtraMeta( // RECURSION
+							metaFoG.meta || {},
+							val['meta'],
+							createFromExtra,
+							val['seed'] || containerSeed // Inherit seeded data if this group's seed isn't set
+						)
+					}
+				}
+				else // ----------------------------------------------
+				{
+					if (isRepeatingField(val)) {
+						extra = {
+							...val,
+							meta: [ val, val, val ]  // <----------! HERE WE BE WORKING!
+						}
+					} else {
+						extra = val;
+					}
+				}
 				combinedMeta[key] = combineMetaForField(metaFoG, seed, extra);
 			}
 		}
@@ -664,6 +688,10 @@ const undefinedNullOrScalar = val => {
 // Add Property to object, returning updated object
 const addProp = (obj, key, val) => { obj[key] = val; return obj; };
 
+// Is Repeating Field
+// Helper function to distinguish a repeating field (a field that can be repeated 1...N times)
+const isRepeatingField = (metaF): boolean => metaF.minRepeat || metaF.maxRepeat || metaF.initialRepeat;
+
 // Is Group
 // Helper function to distinguish group from field
 const isGroup = (metaFoG): boolean => !!metaFoG.meta;