Pārlūkot izejas kodu

Repeating fields working

Richard Knight 4 gadi atpakaļ
vecāks
revīzija
2a89c73d0e

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

@@ -1,5 +1,5 @@
 <clr-input-container *ngIf="!link; else fieldWithLink">
-	<label [ngClass]="{ 'label-error': control.touched && control.invalid }">{{ label }}</label>
+	<label *ngIf="label" [ngClass]="{ 'label-error': control.touched && control.invalid }">{{ label }}</label>
 	<input clrInput type="text"
 		[formControl]="control"
 		[placeholder]="placeholder"
@@ -12,7 +12,7 @@
 
 <ng-template #fieldWithLink>
 	<clr-input-container>
-		<label [ngClass]="{ 'label-error': control.touched && control.invalid }">{{ label }}</label>
+		<label *ngIf="label" [ngClass]="{ 'label-error': control.touched && control.invalid }">{{ label }}</label>
 		<input clrInput type="text"
 			#field
 			[formControl]="control"

+ 24 - 9
src/app/dynaform/dynaform.component.html

@@ -10,20 +10,34 @@
 
 		<ng-container *ngIf="!meta.noLabel; else fullWidth">
 			<div *ngIf="isField(meta); else recursiveDynaform" [ngClass]="getRowClass(control, meta)">
-				<ng-container dynafield
-					[control]="control"
-					[meta]="meta"
-					(call)="handleCallback($event)"
-				></ng-container>
+				<ng-container dynafield [control]="control" [meta]="meta" (call)="handleCallback($event)"></ng-container>
 			</div>
 		</ng-container>
 
 		<ng-template #recursiveDynaform>
-			<ng-container *ngIf="isRepeatingField(meta)">
+
+			<div *ngIf="isRepeatingField(meta)" class="dyna-rf-container">
 				<ng-container *ngFor="let field of meta.meta; let i = index">
+					<button *ngIf="meta.showDeleteControl"
+						class="btn btn-sm btn-icon btn-outline-danger dyna-rc-btn-delete"
+						[disabled]="!deleteRCMemberAllowed(meta.name)"
+						(click)="deleteRCMember(meta.name, i)">
+						<clr-icon shape="trash"></clr-icon>
+					</button>
 					<ng-container *ngTemplateOutlet="dynafield; context: getRepeatingFieldTemplateContext(meta.name, i)"></ng-container>
 				</ng-container>
-			</ng-container>
+				<div *ngIf="meta.showAddControl" class="clr-row">
+					<div class="clr-col-sm-12">
+						<button
+							class="btn btn-sm btn-success dyna-rc-btn-add"
+							[disabled]="!addRCMemberAllowed(meta.name)"
+							(click)="addRFMember(meta.name)">
+							<clr-icon shape="plus"></clr-icon> Add Another
+						</button>
+					</div>
+				</div>
+			</div>
+
 			<ng-container *ngIf="isRepeatingContainer(meta); else container">
 				<div *ngIf="meta.display === 'SINGLE'" class="clr-row dyna-rc-selector">
 					<div class="clr-col-sm-2 text-right dyna-rc-focus-block">
@@ -58,13 +72,14 @@
 					<div class="clr-col-sm-12">
 						<button
 							class="btn btn-sm btn-success dyna-rc-btn-add"
-							[ngClass]="{ 'btn-disabled': !addRCMemberAllowed(meta.name) }"
+							[disabled]="!addRCMemberAllowed(meta.name)"
 							(click)="addRCMember(meta.name)">
-							<clr-icon shape="plus"></clr-icon> Add New
+							<clr-icon shape="plus"></clr-icon> Add Another
 						</button>
 					</div>
 				</div>
 			</ng-container>
+
 		</ng-template>
 
 		<ng-template #container>

+ 23 - 2
src/app/dynaform/dynaform.component.ts

@@ -1,7 +1,7 @@
 import { Component, Input, Output, EventEmitter, TemplateRef, Optional, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
 import { FormBuilder, FormControl, FormGroup, FormArray, FormGroupName, AbstractControl, ControlContainer } from '@angular/forms';
 import { SuperForm } from 'angular-super-validator';
-import { buildFormGroupFunctionFactory } from './services/_formdata-utils';
+import { buildFormGroupFunctionFactory, buildFormControl } from './services/_formdata-utils';
 import { cloneDeep } from 'lodash/fp';
 
 export interface DynarowContext {
@@ -205,6 +205,28 @@ export class DynaformComponent implements OnInit, OnChanges {
 		return typeof rcMeta.maxRepeat === 'number' && rcMeta.maxRepeat > rcMeta.meta.length;
 	}
 
+	// Maybe move the AddRF and deleteRF funtions to _formdata-utils.ts ?
+	addRFMember(repeatingFieldName: string): void {
+		// (1) Check that we can still add controls
+		if (!this.addRCMemberAllowed(repeatingFieldName)) {
+			return;
+		}
+		// (2) Add metadata for new container member
+		const rfMeta = this.formMetaData[repeatingFieldName];
+		const fieldTemplate = cloneDeep(rfMeta.__fieldTemplate);
+		const i = this.formMetaData[repeatingFieldName].meta.length;
+		fieldTemplate.name = `group${i+1}`; // CHECK
+		rfMeta.meta.push(fieldTemplate);
+		// (3) Add FormControl to FormArray
+		const newFormControl = buildFormControl(fieldTemplate);
+		(this.formGroup.get(repeatingFieldName) as FormArray).push(newFormControl);
+	}
+
+	delateRFMember(repeatingFieldName: string, index: number): void {
+		this.deleteRCMember(repeatingFieldName, index);
+	}
+
+
 	// Maybe move the AddRC and deleteRC funtions to _formdata-utils.ts ?
 	addRCMember(repeatingContainerName: string): void {
 		// (1) Check that we can still add controls
@@ -250,7 +272,6 @@ export class DynaformComponent implements OnInit, OnChanges {
 		if (rcMeta.display === 'SINGLE') {
 			this.focusContainer(repeatingContainerName, newMetaArr.length - 1);
 		}
-
 	}
 
 	getValidationFailureMessage(control: FormControl, meta: StringMap<any>) {

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

@@ -256,7 +256,6 @@ class CheckboxGroup {
 	showAllOrNone?: boolean;
 	meta: CheckbuttonField[] | { [key: string]: CheckbuttonField };
 	constructor(groupmeta: any) {
-		console.log(groupmeta);
 		Object.assign(this, groupmeta);
 		if (typeof this.label === 'undefined') {
 			// If label is not supplied set it to the unCamelCased'n'Spaced name
@@ -264,7 +263,6 @@ class CheckboxGroup {
 			this.label = unCamelCase(this.name);
 		}
 		// Can render as a FormArray or FormGroup depending on input data
-		console.log('GMM', groupmeta.meta);
 		if (Array.isArray(groupmeta.meta)) {
 			const arrayMembers = groupmeta.meta;
 			this.meta = arrayMembers.map(cb => new CheckbuttonField(cb));
@@ -315,7 +313,7 @@ class RepeatingField<T> {
 	meta: T[]; // An array of fields of type T
 	minRepeat: number = 1;
 	maxRepeat: number = 10;
-	initialRepeat: number;
+	initialRepeat: number = 1;
 	showAddControl:  boolean = true;
 	showDeleteControl: boolean = true;
 	constructor(repeatingFieldMeta: StringMap<any>) {
@@ -360,7 +358,7 @@ class RepeatingContainer {
 	meta: Container[]; // An array of Containers
 	minRepeat: number = 1;
 	maxRepeat: number = 10;
-	initialRepeat: number;
+	initialRepeat: number = 1;
 	showAddControl:  boolean = true;
 	showDeleteControl: boolean = true;
 	primaryField: string = '';

+ 22 - 19
src/app/dynaform/services/_formdata-utils.ts

@@ -140,12 +140,14 @@ const combineExtraMeta = (metaG, extraMeta, createFromExtra = false, containerSe
 						// We've got a repeating field
 						const metaForFieldToRepeat = {
 							...containerSeed,
-							...omit(['seed', 'minRepeat', 'maxRepeat', 'initialRepeat', 'showAddControl', 'showDeleteControl'], val as StringMap<any>)
+							...omit(['seed', 'minRepeat', 'maxRepeat', 'initialRepeat', 'showAddControl', 'showDeleteControl'], val as StringMap<any>),
+							label: `${val.label || key}`
 						};
 						delete val.type;
 						extra = {
 							...val,
-							meta: Array.from(Array(val.initialRepeat || 1).keys()).map((f, i) => ({ ...metaForFieldToRepeat, label: `${val.label || key} ${i + 1}` }))
+							meta: Array.from(Array(val.initialRepeat || 1).keys()).map((f, i) => metaForFieldToRepeat),
+							__fieldTemplate: metaForFieldToRepeat
 						}
 					} else {
 						// We've got a standard field
@@ -238,16 +240,16 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
 	// Build Form Group Member
 	const buildModeledFieldGroupMember = (metaFoG) => {
 		const modeledGroupMember = buildModeledField(metaFoG);
-		
-		console.log('------------- DEBUGGING -------------');
+		/*
+		console.log('------------- DEBUGGING --->', metaFoG.name);
 		console.log(modeledGroupMember);
 		console.log('IS REP Field', isRepeatingField(modeledGroupMember));
 		console.log('IS REP Container', isRepeatingContainer(modeledGroupMember));
 		console.log('IS ORD Container', isContainer(modeledGroupMember));
-		console.log(modeledGroupMember);
-		
+		*/
 		if (isRepeatingField(modeledGroupMember)) {
 			modeledGroupMember.meta = modeledGroupMember.meta.map(metaF => buildModeledField(metaF));
+			modeledGroupMember.__fieldTemplate = buildModeledField(modeledGroupMember.__fieldTemplate);
 		} else if (isContainer(modeledGroupMember)) {
 			modeledGroupMember.meta = _buildFieldSpecificMeta(modeledGroupMember.meta);
 		} else if (isRepeatingContainer(modeledGroupMember)) {
@@ -269,9 +271,6 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
 		reduce(buildModeledFieldGroupReducerIteree, {}, metaG);
 	const buildFieldSpecificMeta = metaG => _buildFieldSpecificMeta(addMissingNames(metaG));
 
-	// DEBUGGING
-	console.log('buildFieldSpecificMetaInClosure', metaG, context);
-
 	return buildFieldSpecificMeta(metaG); // RUN CODE
 }
 
@@ -409,18 +408,21 @@ const extractFieldMappings = (metaG, parentPath = '') => Object.entries(metaG)
 // TODO: In progress: elegantly adding validators at FormGroup and FormArray levels
 // Working, but code needs a rework
 
+// Build Form Control
+const buildControlState = metaF => ({ value: metaF.value || metaF.default, disabled: metaF.disabled });
+const buildValidators = (metaFoG): AbstractControlOptions => ({
+	validators: metaFoG.validators,
+	asyncValidators: metaFoG.asyncValidators,
+	// blur not working for custom components, so use change for custom and blur for text
+	updateOn: getUpdateOn(metaFoG.type)
+});
+const buildFormControl = metaF => new FormControl(buildControlState(metaF), buildValidators(metaF));
+
+// Build Form Group - uses the 3 functions avove
 const buildFormGroupFunctionFactory = (fb: FormBuilder): (meta) => FormGroup => {
 	// Establishes a closure over the supplied FormBuilder and returns a function that builds FormGroups from metadata
 	// ( it's done this way so we can use the FormBuilder singleton without reinitialising )
 
-	// Build Form Control
-	const buildControlState = metaF => ({ value: metaF.value || metaF.default, disabled: metaF.disabled });
-	const buildValidators = (metaFoG): AbstractControlOptions => ({
-		validators: metaFoG.validators,
-		asyncValidators: metaFoG.asyncValidators,
-		// blur not working for custom components, so use change for custom and blur for text
-		updateOn: getUpdateOn(metaFoG.type)
-	});
 	const BVAL = metaFoG => {
 		if (!metaFoG || !(metaFoG.validators || metaFoG.asyncValidators)) {
 			return undefined;
@@ -430,7 +432,6 @@ const buildFormGroupFunctionFactory = (fb: FormBuilder): (meta) => FormGroup =>
 		console.log(res);
 		return res;
 	}
-	const buildFormControl = metaF => new FormControl(buildControlState(metaF), buildValidators(metaF));
 
 	// Build Form Array containing either Form Controls or Form Groups
 	const buildFormArray = (metaG, grMeta?): FormArray => {
@@ -751,7 +752,8 @@ const isRepeatingContainer = (metaFoG): boolean => metaFoG.type && metaFoG.type.
 		!metaFoG.type
 		&& isRepeating(metaFoG)
 		&& hasMeta(metaFoG)
-		&& Array.isArray(metaFoG.meta) && metaFoG.meta[0]
+		&& Array.isArray(metaFoG.meta)
+		&& metaFoG.meta[0]
 		&& !isScalar(Object.values(metaFoG.meta[0])[0])
 	);
 
@@ -791,6 +793,7 @@ const addMissingFieldSpecificMeta = metaG => Object.entries(metaG)
 export {
 	autoMeta, combineModelWithMeta, combineExtraMeta, execMetaReorderingInstructions,
 	buildFieldSpecificMetaInClosure, extractFieldMappings,
+	buildFormControl,
 	buildFormGroupFunctionFactory,
 	resetAsyncValidatorsRecursive,
 	touchAndUpdateValidityRecursive, resetValidityRecursive,