[CST-3782] Make changes in order to remove template row in form's repeatable fields

This commit is contained in:
Giuseppe Digilio
2021-02-17 16:10:22 +01:00
parent 942dd2e72e
commit 151b02aeec
12 changed files with 154 additions and 179 deletions

View File

@@ -13,6 +13,11 @@
<ng-container #componentViewContainer></ng-container> <ng-container #componentViewContainer></ng-container>
<small *ngIf="hasHint && (model.repeatable === false || context?.index === 0) && (!showErrorMessages || errorMessages.length === 0)" <small *ngIf="hasHint && (model.repeatable === false || context?.index === 0) && (!showErrorMessages || errorMessages.length === 0)"
class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small> class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<!-- -->
<small *ngIf="(model.type !== 'GROUP') && !asBootstrapFormGroup
&& (context?.index > 0 && context?.index < context?.context?.groups?.length - 1)
&& (!showErrorMessages || errorMessages.length === 0)"
class="text-muted" [ngClass]="getClass('element', 'hint')">&nbsp;</small>
<div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]"> <div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate: model.validators }}</small> <small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate: model.validators }}</small>
@@ -32,7 +37,7 @@
<option *ngFor="let lang of model.languageCodes" [value]="lang.code">{{lang.display}}</option> <option *ngFor="let lang of model.languageCodes" [value]="lang.code">{{lang.display}}</option>
</select> </select>
</div> </div>
<div *ngIf="isRelationship" class="col-auto text-center" [class.invisible]="context?.index > 0"> <div *ngIf="isRelationship" class="col-auto text-center" [class.d-none]="context?.index > 0">
<button class="btn btn-secondary" <button class="btn btn-secondary"
type="button" type="button"
ngbTooltip="{{'form.lookup-help' | translate}}" ngbTooltip="{{'form.lookup-help' | translate}}"

View File

@@ -437,6 +437,10 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
.forEach((sub) => sub.unsubscribe()); .forEach((sub) => sub.unsubscribe());
} }
get hasHint(): boolean {
return isNotEmpty(this.model.hint) && this.model.hint !== '&nbsp;';
}
/** /**
* Initialize this.item$ based on this.model.submissionId * Initialize this.item$ based on this.model.submissionId
*/ */

View File

@@ -2,44 +2,31 @@
<div [id]="id" <div [id]="id"
[formArrayName]="model.id" [formArrayName]="model.id"
[ngClass]="getClass('element', 'control')"> [ngClass]="getClass('element', 'control')">
<div role="group"
formGroupName="0" [ngClass]="[getClass('element', 'group'), getClass('grid', 'group')]"> <div *ngFor="let groupModel of model.groups; let idx = index" role="group"
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: model.groups[0]"></ng-container> [formGroupName]="idx" [ngClass]="[getClass('element', 'group'), getClass('grid', 'group')]">
<ng-container *ngTemplateOutlet="controlContainer; context: {$implicit: 0}"></ng-container>
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: model.groups[0]"></ng-container>
</div>
<div cdkDropList cdkDropListLockAxis="y" (cdkDropListDropped)="moveSelection($event)">
<div *ngFor="let groupModel of model.groups; let idx = index"
[ngClass]="{'pt-2 pb-2': idx > 0}" cdkDrag cdkDragHandle>
<div [formGroupName]="idx"
[class]="getClass('element', 'group') + ' ' + getClass('grid', 'group')"
[ngClass]="{'d-flex align-items-center': idx > 0}"
>
<ng-container *ngIf="idx > 0">
<i class="drag-icon fas fa-grip-vertical fa-fw"></i>
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container> <ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
<ng-container *ngTemplateOutlet="controlContainer; context: {$implicit: idx}"></ng-container>
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: groupModel"></ng-container> <ds-dynamic-form-control-container *ngFor="let _model of groupModel.group"
</ng-container> [bindId]="false"
</div> [formGroup]="group"
</div> [context]="groupModel"
[group]="control.get([idx])"
[hidden]="_model.hidden"
[layout]="formLayout"
[model]="_model"
[templates]="templates"
[ngClass]="[getClass('element', 'host', _model), getClass('grid', 'host', _model)]"
(dfBlur)="onBlur($event)"
(dfChange)="onChange($event)"
(dfFocus)="onFocus($event)"
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container>
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: groupModel"></ng-container>
</div> </div>
</div> </div>
</ng-container> </ng-container>
<ng-template #controlContainer let-idx>
<ds-dynamic-form-control-container *ngFor="let _model of model.groups[idx].group"
[bindId]="false"
[context]="model.groups[idx]"
[group]="control.get([idx])"
[hidden]="_model.hidden"
[layout]="formLayout"
[model]="_model"
[templates]="templates"
[ngClass]="[getClass('element', 'host', _model), getClass('grid', 'host', _model)]"
(dfBlur)="update($event, idx)"
(dfChange)="update($event, idx)"
(dfFocus)="onFocus($event)"
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container>
</ng-template>

View File

@@ -3,16 +3,17 @@ import { Component, EventEmitter, Input, Output, QueryList } from '@angular/core
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { import {
DynamicFormArrayComponent, DynamicFormArrayComponent,
DynamicFormArrayModel,
DynamicFormControlCustomEvent, DynamicFormControlCustomEvent,
DynamicFormControlEvent, DynamicFormControlEvent,
DynamicFormControlEventType, DynamicFormControlEventType,
DynamicFormControlLayout, DynamicFormLayout, DynamicFormControlLayout,
DynamicFormLayout,
DynamicFormLayoutService, DynamicFormLayoutService,
DynamicFormValidationService, DynamicFormValidationService,
DynamicTemplateDirective DynamicTemplateDirective
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { Relationship } from '../../../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../../../core/shared/item-relationships/relationship.model';
import { DynamicRowArrayModel } from '../ds-dynamic-row-array-model';
import { hasValue } from '../../../../../empty.util'; import { hasValue } from '../../../../../empty.util';
@Component({ @Component({
@@ -25,7 +26,7 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent {
@Input() formLayout: DynamicFormLayout; @Input() formLayout: DynamicFormLayout;
@Input() group: FormGroup; @Input() group: FormGroup;
@Input() layout: DynamicFormControlLayout; @Input() layout: DynamicFormControlLayout;
@Input() model: DynamicRowArrayModel; @Input() model: DynamicFormArrayModel;
@Input() templates: QueryList<DynamicTemplateDirective> | undefined; @Input() templates: QueryList<DynamicTemplateDirective> | undefined;
/* tslint:disable:no-output-rename */ /* tslint:disable:no-output-rename */

View File

@@ -28,7 +28,7 @@ import { DynamicRelationGroupModel } from './ds-dynamic-form-ui/models/relation-
import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model';
import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model';
import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model'; import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model';
import { isNgbDateStruct } from '../../date.util'; import { dateToString, isNgbDateStruct } from '../../date.util';
import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/ds-dynamic-form-constants'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/ds-dynamic-form-constants';
import { CONCAT_GROUP_SUFFIX, DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { CONCAT_GROUP_SUFFIX, DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model';
@@ -123,6 +123,8 @@ export class FormBuilderService extends DynamicFormService {
const controlLanguage = (controlModel as DsDynamicInputModel).hasLanguages ? (controlModel as DsDynamicInputModel).language : null; const controlLanguage = (controlModel as DsDynamicInputModel).hasLanguages ? (controlModel as DsDynamicInputModel).language : null;
if (isString(controlValue)) { if (isString(controlValue)) {
return new FormFieldMetadataValueObject(controlValue, controlLanguage, null, null, controlModelIndex); return new FormFieldMetadataValueObject(controlValue, controlLanguage, null, null, controlModelIndex);
} else if (isNgbDateStruct(controlValue)) {
return new FormFieldMetadataValueObject(dateToString(controlValue));
} else if (isObject(controlValue)) { } else if (isObject(controlValue)) {
const authority = (controlValue as any).authority || (controlValue as any).id || null; const authority = (controlValue as any).authority || (controlValue as any).id || null;
const place = controlModelIndex || (controlValue as any).place; const place = controlModelIndex || (controlValue as any).place;
@@ -240,7 +242,7 @@ export class FormBuilderService extends DynamicFormService {
} }
hasArrayGroupValue(model: DynamicFormControlModel): boolean { hasArrayGroupValue(model: DynamicFormControlModel): boolean {
return model && (this.isListGroup(model) || model.type === DYNAMIC_FORM_CONTROL_TYPE_TAG || model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY); return model && (this.isListGroup(model) || model.type === DYNAMIC_FORM_CONTROL_TYPE_TAG);
} }
hasMappedGroupValue(model: DynamicFormControlModel): boolean { hasMappedGroupValue(model: DynamicFormControlModel): boolean {
@@ -310,7 +312,7 @@ export class FormBuilderService extends DynamicFormService {
let tempModel: DynamicFormControlModel; let tempModel: DynamicFormControlModel;
if (this.isArrayGroup(model as DynamicFormControlModel)) { if (this.isArrayGroup(model as DynamicFormControlModel)) {
return hasValue((model as any).metadataKey) ? (model as any).metadataKey : model.index.toString(); return model.index.toString();
} else if (this.isModelInCustomGroup(model as DynamicFormControlModel)) { } else if (this.isModelInCustomGroup(model as DynamicFormControlModel)) {
tempModel = (model as any).parent; tempModel = (model as any).parent;
} else { } else {

View File

@@ -58,8 +58,20 @@ export class ConcatFieldParser extends FieldParser {
concatGroup.group = []; concatGroup.group = [];
concatGroup.separator = this.separator; concatGroup.separator = this.separator;
const input1ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_FIRST_INPUT_SUFFIX, false, false); const input1ModelConfig: DynamicInputModelConfig = this.initModel(
const input2ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_SECOND_INPUT_SUFFIX, false, false); id + CONCAT_FIRST_INPUT_SUFFIX,
false,
true,
true,
false
);
const input2ModelConfig: DynamicInputModelConfig = this.initModel(
id + CONCAT_SECOND_INPUT_SUFFIX,
false,
true,
true,
false
);
if (hasNoValue(concatGroup.hint) && hasValue(input1ModelConfig.hint) && hasNoValue(input2ModelConfig.hint)) { if (hasNoValue(concatGroup.hint) && hasValue(input1ModelConfig.hint) && hasNoValue(input2ModelConfig.hint)) {
concatGroup.hint = input1ModelConfig.hint; concatGroup.hint = input1ModelConfig.hint;

View File

@@ -15,6 +15,8 @@ import { setLayout } from './parser.utils';
import { ParserOptions } from './parser-options'; import { ParserOptions } from './parser-options';
import { RelationshipOptions } from '../models/relationship-options.model'; import { RelationshipOptions } from '../models/relationship-options.model';
import { VocabularyOptions } from '../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyOptions } from '../../../../core/submission/vocabularies/models/vocabulary-options.model';
import { ParserType } from './parser-type';
import { isNgbDateStruct } from '../../../date.util';
export const SUBMISSION_ID: InjectionToken<string> = new InjectionToken<string>('submissionId'); export const SUBMISSION_ID: InjectionToken<string> = new InjectionToken<string>('submissionId');
export const CONFIG_DATA: InjectionToken<FormFieldModel> = new InjectionToken<FormFieldModel>('configData'); export const CONFIG_DATA: InjectionToken<FormFieldModel> = new InjectionToken<FormFieldModel>('configData');
@@ -37,9 +39,8 @@ export abstract class FieldParser {
public parse() { public parse() {
if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable)) if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable))
&& (this.configData.input.type !== 'list') && (this.configData.input.type !== ParserType.List)
&& (this.configData.input.type !== 'tag') && (this.configData.input.type !== ParserType.Tag)
&& (this.configData.input.type !== 'group')
) { ) {
let arrayCounter = 0; let arrayCounter = 0;
let fieldArrayCounter = 0; let fieldArrayCounter = 0;
@@ -66,22 +67,16 @@ export abstract class FieldParser {
model = this.modelFactory(); model = this.modelFactory();
arrayCounter++; arrayCounter++;
} else { } else {
const fieldArrayOfValueLength = this.getInitValueCount(arrayCounter - 1); const fieldArrayOfValueLenght = this.getInitValueCount(arrayCounter - 1);
let fieldValue = null; let fieldValue = null;
if (fieldArrayOfValueLength > 0) { if (fieldArrayOfValueLenght > 0) {
if (fieldArrayCounter === 0) { fieldValue = this.getInitFieldValue(arrayCounter - 1, fieldArrayCounter++);
fieldValue = ''; if (fieldArrayCounter === fieldArrayOfValueLenght) {
} else {
fieldValue = this.getInitFieldValue(arrayCounter - 1, fieldArrayCounter - 1);
}
fieldArrayCounter++;
if (fieldArrayCounter === fieldArrayOfValueLength + 1) {
fieldArrayCounter = 0; fieldArrayCounter = 0;
arrayCounter++; arrayCounter++;
} }
} }
model = this.modelFactory(fieldValue, false); model = this.modelFactory(fieldValue, false);
model.id = `${model.id}_${fieldArrayCounter}`;
} }
setLayout(model, 'element', 'host', 'col'); setLayout(model, 'element', 'host', 'col');
if (model.hasLanguages || isNotEmpty(model.relationship)) { if (model.hasLanguages || isNotEmpty(model.relationship)) {
@@ -130,7 +125,9 @@ export abstract class FieldParser {
return; return;
} }
if (typeof fieldValue === 'object') { if (isNgbDateStruct(fieldValue)) {
modelConfig.value = fieldValue;
} else if (typeof fieldValue === 'object') {
modelConfig.metadataValue = fieldValue; modelConfig.metadataValue = fieldValue;
modelConfig.language = fieldValue.language; modelConfig.language = fieldValue.language;
modelConfig.place = fieldValue.place; modelConfig.place = fieldValue.place;
@@ -210,10 +207,9 @@ export abstract class FieldParser {
} }
protected getInitArrayIndex() { protected getInitArrayIndex() {
let fieldCount = 0;
const fieldIds: any = this.getAllFieldIds(); const fieldIds: any = this.getAllFieldIds();
if (isNotEmpty(this.initFormValues) && isNotNull(fieldIds) && fieldIds.length === 1 && this.initFormValues.hasOwnProperty(fieldIds)) { if (isNotEmpty(this.initFormValues) && isNotNull(fieldIds) && fieldIds.length === 1 && this.initFormValues.hasOwnProperty(fieldIds)) {
fieldCount = this.initFormValues[fieldIds].filter((value) => hasValue(value) && hasValue(value.value)).length; return this.initFormValues[fieldIds].length;
} else if (isNotEmpty(this.initFormValues) && isNotNull(fieldIds) && fieldIds.length > 1) { } else if (isNotEmpty(this.initFormValues) && isNotNull(fieldIds) && fieldIds.length > 1) {
let counter = 0; let counter = 0;
fieldIds.forEach((id) => { fieldIds.forEach((id) => {
@@ -221,9 +217,10 @@ export abstract class FieldParser {
counter = counter + this.initFormValues[id].length; counter = counter + this.initFormValues[id].length;
} }
}); });
fieldCount = counter; return (counter === 0) ? 1 : counter;
} else {
return 1;
} }
return (fieldCount === 0) ? 1 : fieldCount + 1;
} }
protected getFieldId(): string { protected getFieldId(): string {
@@ -245,7 +242,7 @@ export abstract class FieldParser {
} }
} }
protected initModel(id?: string, label = true, setErrors = true, hint = true) { protected initModel(id?: string, label = true, labelEmpty = false, setErrors = true, hint = true) {
const controlModel = Object.create(null); const controlModel = Object.create(null);
@@ -316,7 +313,7 @@ export abstract class FieldParser {
protected setLabel(controlModel, label = true, labelEmpty = false) { protected setLabel(controlModel, label = true, labelEmpty = false) {
if (label) { if (label) {
controlModel.label = this.configData.label; controlModel.label = (labelEmpty) ? '&nbsp;' : this.configData.label;
} }
} }

View File

@@ -59,7 +59,7 @@ export class OneboxFieldParser extends FieldParser {
this.setLabel(inputSelectGroup, label); this.setLabel(inputSelectGroup, label);
inputSelectGroup.required = isNotEmpty(this.configData.mandatory); inputSelectGroup.required = isNotEmpty(this.configData.mandatory);
const selectModelConfig: DynamicSelectModelConfig<any> = this.initModel(newId + QUALDROP_METADATA_SUFFIX, label, false); const selectModelConfig: DynamicSelectModelConfig<any> = this.initModel(newId + QUALDROP_METADATA_SUFFIX, label, false, false);
selectModelConfig.hint = null; selectModelConfig.hint = null;
this.setOptions(selectModelConfig); this.setOptions(selectModelConfig);
if (isNotEmpty(fieldValue)) { if (isNotEmpty(fieldValue)) {
@@ -67,7 +67,7 @@ export class OneboxFieldParser extends FieldParser {
} }
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect)); inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + QUALDROP_VALUE_SUFFIX, label, false); const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + QUALDROP_VALUE_SUFFIX, label, false, false);
inputModelConfig.hint = null; inputModelConfig.hint = null;
this.setValues(inputModelConfig, fieldValue); this.setValues(inputModelConfig, fieldValue);
inputSelectGroup.readOnly = selectModelConfig.disabled && inputModelConfig.readOnly; inputSelectGroup.readOnly = selectModelConfig.disabled && inputModelConfig.readOnly;

View File

@@ -1,57 +1,62 @@
<div class="container-fluid"> <div class="container-fluid">
<form class="form-horizontal" [formGroup]="formGroup"> <form class="form-horizontal" [formGroup]="formGroup">
<ds-dynamic-form <ds-dynamic-form
[formId]="formId" [formId]="formId"
[formGroup]="formGroup" [formGroup]="formGroup"
[formModel]="formModel" [formModel]="formModel"
[formLayout]="formLayout" [formLayout]="formLayout"
(change)="$event.stopPropagation();" (change)="$event.stopPropagation();"
(dfBlur)="onBlur($event)" (dfBlur)="onBlur($event)"
(dfChange)="onChange($event)" (dfChange)="onChange($event)"
(dfFocus)="onFocus($event)"> (dfFocus)="onFocus($event)">
<ng-template modelType="ARRAY" let-group let-index="index" let-context="context"> <ng-template modelType="ARRAY" let-group let-index="index" let-context="context">
<!--Array with repeatable items--> <!--Array with repeatable items-->
<div *ngIf="context.hasSelectableMetadata && !context.notRepeatable && index < 1" <div *ngIf="(!context.notRepeatable)"
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end"> class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end">
<div class="btn-group" role="group" aria-label="Add and remove button"> <div class="btn-group" role="group" aria-label="Add and remove button">
<button type="button" class="btn btn-secondary" <button *ngIf="index === 0" type="button" class="btn btn-secondary"
[disabled]="isItemReadOnly(context, index)" [disabled]="isItemReadOnly(context, index)"
(click)="insertItem($event, group.context, group.index)"> (click)="insertItem($event, group.context, group.context.groups.length)">
<span aria-label="Add">{{'form.add' | translate}}</span> <span attr.aria-label="{{'form.add' | translate}}">{{'form.add' | translate}}</span>
</button> </button>
</div> <button *ngIf="index > 0" type="button" class="btn btn-secondary"
</div> (click)="removeItem($event, context, index)"
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
<!--Array with non repeatable items - Only delete button--> <span attr.aria-label="{{'form.remove' | translate}}">{{'form.remove' | translate}}</span>
<div *ngIf="context.notRepeatable && group.context.groups.length > 1 || index > 0 && !(group.group[0]?.value?.isVirtual || group.group[0]?.metadataValue?.isVirtual)" </button>
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end"> </div>
<div class="btn-group" role="group" aria-label="Remove button">
<button type="button" class="btn btn-secondary"
(click)="removeItem($event, context, index)"
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
<i class="fas fa-trash" aria-hidden="true"></i>
</button>
</div>
</div>
</ng-template>
</ds-dynamic-form>
<ng-content></ng-content>
<div *ngIf="displaySubmit">
<hr>
<div class="form-group row">
<div class="col text-right">
<button type="reset" class="btn btn-default" (click)="reset()">{{cancelLabel | translate}}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()"
[disabled]="!(isValid() | async)">{{submitLabel | translate}}
</button>
</div>
</div>
</div> </div>
</form> <!--Array with non repeatable items - Only delete button-->
<div *ngIf="context.notRepeatable && context.showButtons && group.context.groups.length > 1"
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end">
<div class="btn-group" role="group" aria-label="Remove button">
<button type="button" class="btn btn-secondary"
(click)="removeItem($event, context, index)"
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
<span attr.aria-label="{{'form.discard' | translate}}">{{'form.discard' | translate}}</span>
</button>
</div>
</div>
</ng-template>
</ds-dynamic-form>
<ng-content></ng-content>
<div *ngIf="displaySubmit">
<hr>
<div class="form-group row">
<div class="col text-right">
<button type="reset" class="btn btn-default" (click)="reset()">{{cancelLabel | translate}}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()"
[disabled]="!(isValid() | async)">{{submitLabel | translate}}
</button>
</div>
</div>
</div>
</form>
</div> </div>

View File

@@ -187,7 +187,6 @@ export class FormComponent implements OnDestroy, OnInit {
if (field) { if (field) {
const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel); const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel);
this.formService.addErrorToField(field, model, error.message); this.formService.addErrorToField(field, model, error.message);
// this.formService.validateAllFormFields(formGroup);
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
} }
@@ -300,55 +299,15 @@ export class FormComponent implements OnDestroy, OnInit {
removeItem($event, arrayContext: DynamicFormArrayModel, index: number): void { removeItem($event, arrayContext: DynamicFormArrayModel, index: number): void {
const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray;
this.removeArrayItem.emit(this.getEvent($event, arrayContext, index - 1, 'remove')); this.removeArrayItem.emit(this.getEvent($event, arrayContext, index, 'remove'));
this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext); this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext);
this.formService.changeForm(this.formId, this.formModel); this.formService.changeForm(this.formId, this.formModel);
} }
insertItem($event, arrayContext: DynamicFormArrayModel, index: number): void { insertItem($event, arrayContext: DynamicFormArrayModel, index: number): void {
const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray;
this.formBuilderService.insertFormArrayGroup(index, formArrayControl, arrayContext);
// First emit the new value so it can be sent to the server this.addArrayItem.emit(this.getEvent($event, arrayContext, index, 'add'));
const value = formArrayControl.controls[0].value;
const event = this.getEvent($event, arrayContext, 0, 'add');
this.addArrayItem.emit(event);
this.change.emit(event);
// Next: update the UI so the user sees the changes
// without having to wait for the server's reply
// add an empty new field at the bottom
this.formBuilderService.addFormArrayGroup(formArrayControl, arrayContext);
// set that field to the new value
const model = arrayContext.groups[arrayContext.groups.length - 1].group[0] as any;
if (model.hasAuthority) {
model.value = Object.values(value)[0];
const ctrl = formArrayControl.controls[formArrayControl.length - 1];
const ctrlValue = ctrl.value;
const ctrlValueKey = Object.keys(ctrlValue)[0];
ctrl.setValue({
[ctrlValueKey]: model.value
});
} else if (this.formBuilderService.isQualdropGroup(model)) {
const ctrl = formArrayControl.controls[formArrayControl.length - 1];
const ctrlKey = Object.keys(ctrl.value).find((key: string) => isNotEmpty(key.match(QUALDROP_GROUP_REGEX)));
const valueKey = Object.keys(value).find((key: string) => isNotEmpty(key.match(QUALDROP_GROUP_REGEX)));
if (ctrlKey !== valueKey) {
Object.defineProperty(value, ctrlKey, Object.getOwnPropertyDescriptor(value, valueKey));
delete value[valueKey];
}
ctrl.setValue(value);
} else {
formArrayControl.controls[formArrayControl.length - 1].setValue(value);
}
// Clear the topmost field by removing the filled out version and inserting a new, empty version.
// Doing it this way ensures an empty value of the correct type is added without a bunch of ifs here
this.formBuilderService.removeFormArrayGroup(0, formArrayControl, arrayContext);
this.formBuilderService.insertFormArrayGroup(0, formArrayControl, arrayContext);
// Tell the formService that it should rerender.
this.formService.changeForm(this.formId, this.formModel); this.formService.changeForm(this.formId, this.formModel);
} }

View File

@@ -9,15 +9,7 @@ import {
DynamicFormControlModel DynamicFormControlModel
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { import { hasValue, isNotEmpty, isNotNull, isNotUndefined, isNull, isUndefined } from '../../../shared/empty.util';
hasNoValue,
hasValue,
isNotEmpty,
isNotNull,
isNotUndefined,
isNull,
isUndefined
} from '../../../shared/empty.util';
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object'; import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object';
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
@@ -30,6 +22,7 @@ import { DynamicQualdropModel } from '../../../shared/form/builder/ds-dynamic-fo
import { DynamicRelationGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { DynamicRelationGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { deepClone } from 'fast-json-patch'; import { deepClone } from 'fast-json-patch';
import { dateToString, isNgbDateStruct } from '../../../shared/date.util';
/** /**
* The service handling all form section operations * The service handling all form section operations
@@ -71,9 +64,6 @@ export class SectionFormOperationsService {
case 'change': case 'change':
this.dispatchOperationsFromChangeEvent(pathCombiner, event, previousValue, hasStoredValue); this.dispatchOperationsFromChangeEvent(pathCombiner, event, previousValue, hasStoredValue);
break; break;
case 'add':
this.dispatchOperationsFromAddEvent(pathCombiner, event);
break;
default: default:
break; break;
} }
@@ -245,6 +235,8 @@ export class SectionFormOperationsService {
// Language without Authority (input, textArea) // Language without Authority (input, textArea)
fieldValue = new FormFieldMetadataValueObject(value, language); fieldValue = new FormFieldMetadataValueObject(value, language);
} }
} else if (isNgbDateStruct(value)) {
fieldValue = new FormFieldMetadataValueObject(dateToString(value));
} else if (value instanceof FormFieldLanguageValueObject || value instanceof VocabularyEntry } else if (value instanceof FormFieldLanguageValueObject || value instanceof VocabularyEntry
|| value instanceof VocabularyEntryDetail || isObject(value)) { || value instanceof VocabularyEntryDetail || isObject(value)) {
fieldValue = value; fieldValue = value;
@@ -295,7 +287,7 @@ export class SectionFormOperationsService {
const value = this.getFieldValueFromChangeEvent(event); const value = this.getFieldValueFromChangeEvent(event);
if (this.formBuilder.isQualdropGroup(event.model as DynamicFormControlModel)) { if (this.formBuilder.isQualdropGroup(event.model as DynamicFormControlModel)) {
this.dispatchOperationsFromMap(this.getQualdropValueMap(event), pathCombiner, event, previousValue); this.dispatchOperationsFromMap(this.getQualdropValueMap(event), pathCombiner, event, previousValue);
} else if (isNotEmpty(value)) { } else if (isNotEmpty(value) && (value instanceof FormFieldMetadataValueObject && value.hasValue())) {
this.operationsBuilder.remove(pathCombiner.getPath(path)); this.operationsBuilder.remove(pathCombiner.getPath(path));
} }
} }
@@ -362,7 +354,7 @@ export class SectionFormOperationsService {
} else if (this.formBuilder.isRelationGroup(event.model)) { } else if (this.formBuilder.isRelationGroup(event.model)) {
// It's a relation model // It's a relation model
this.dispatchOperationsFromMap(this.getValueMap(value), pathCombiner, event, previousValue); this.dispatchOperationsFromMap(this.getValueMap(value), pathCombiner, event, previousValue);
} else if (this.formBuilder.hasArrayGroupValue(event.model) && hasNoValue((event.model as any).relationshipConfig)) { } else if (this.formBuilder.hasArrayGroupValue(event.model)) {
// Model has as value an array, so dispatch an add operation with entire block of values // Model has as value an array, so dispatch an add operation with entire block of values
this.operationsBuilder.add( this.operationsBuilder.add(
pathCombiner.getPath(segmentedPath), pathCombiner.getPath(segmentedPath),
@@ -398,13 +390,22 @@ export class SectionFormOperationsService {
value); value);
} }
previousValue.delete(); previousValue.delete();
} else if (value.hasValue() && (isUndefined(this.getArrayIndexFromEvent(event)) } else if (value.hasValue()) {
|| this.getArrayIndexFromEvent(event) === 0)) { // Here model has no previous value but a new one
if (isUndefined(this.getArrayIndexFromEvent(event))
|| this.getArrayIndexFromEvent(event) === 0) {
// Model is single field or is part of an array model but is the first item, // Model is single field or is part of an array model but is the first item,
// so dispatch an add operation that initialize the values of a specific metadata // so dispatch an add operation that initialize the values of a specific metadata
this.operationsBuilder.add( this.operationsBuilder.add(
pathCombiner.getPath(segmentedPath), pathCombiner.getPath(segmentedPath),
value, true); value, true);
} else {
// Model is part of an array model but is not the first item,
// so dispatch an add operation that add a value to an existent metadata
this.operationsBuilder.add(
pathCombiner.getPath(path),
value);
}
} }
} }

View File

@@ -1267,6 +1267,8 @@
"form.clear-help": "Click here to remove the selected value", "form.clear-help": "Click here to remove the selected value",
"form.discard": "Discard",
"form.edit": "Edit", "form.edit": "Edit",
"form.edit-help": "Click here to edit the selected value", "form.edit-help": "Click here to edit the selected value",