[CST-3620] Changes to show lookup button next to the field

This commit is contained in:
Giuseppe Digilio
2021-02-25 17:09:33 +01:00
parent ee07d42c9c
commit a6effcad34
5 changed files with 77 additions and 52 deletions

View File

@@ -35,6 +35,14 @@
<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 && !isVirtual()" class="col-auto text-center">
<button class="btn btn-secondary"
type="button"
ngbTooltip="{{'form.lookup-help' | translate}}"
placement="top"
(click)="openLookup(); $event.stopPropagation();">{{'form.lookup' | translate}}
</button>
</div>
</div> </div>
<ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: model"></ng-container> <ng-container *ngTemplateOutlet="endTemplate?.templateRef; context: model"></ng-container>
<ng-container *ngIf="value?.isVirtual"> <ng-container *ngIf="value?.isVirtual">

View File

@@ -37,6 +37,7 @@ import {
DynamicFormControl, DynamicFormControl,
DynamicFormControlContainerComponent, DynamicFormControlContainerComponent,
DynamicFormControlEvent, DynamicFormControlEvent,
DynamicFormControlEventType,
DynamicFormControlModel, DynamicFormControlModel,
DynamicFormLayout, DynamicFormLayout,
DynamicFormLayoutService, DynamicFormLayoutService,
@@ -82,10 +83,12 @@ import { find, map, startWith, switchMap, take } from 'rxjs/operators';
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import { SearchResult } from '../../../search/search-result.model'; import { SearchResult } from '../../../search/search-result.model';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { RelationshipService } from '../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../core/data/relationship.service';
import { SelectableListService } from '../../../object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../object-list/selectable-list/selectable-list.service';
import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component'; import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component';
import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model'; import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model';
import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';
import { import {
getAllSucceededRemoteData, getAllSucceededRemoteData,
getFirstSucceededRemoteData, getFirstSucceededRemoteData,
@@ -107,6 +110,7 @@ import { Collection } from '../../../../core/shared/collection.model';
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../../core/shared/metadata.models'; import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../../core/shared/metadata.models';
import { FormService } from '../../form.service'; import { FormService } from '../../form.service';
import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer'; import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer';
import { SubmissionService } from '../../../../submission/submission.service';
import { followLink } from '../../../utils/follow-link-config.model'; import { followLink } from '../../../utils/follow-link-config.model';
import { paginatedRelationsToItems } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils'; import { paginatedRelationsToItems } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { RelationshipOptions } from '../models/relationship-options.model'; import { RelationshipOptions } from '../models/relationship-options.model';
@@ -202,6 +206,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() model: any; @Input() model: any;
relationshipValue$: Observable<ReorderableRelationship>; relationshipValue$: Observable<ReorderableRelationship>;
isRelationship: boolean; isRelationship: boolean;
modalRef: NgbModalRef;
item: Item; item: Item;
item$: Observable<Item>; item$: Observable<Item>;
collection: Collection; collection: Collection;
@@ -234,6 +239,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
protected validationService: DynamicFormValidationService, protected validationService: DynamicFormValidationService,
protected translateService: TranslateService, protected translateService: TranslateService,
protected relationService: DynamicFormRelationService, protected relationService: DynamicFormRelationService,
private modalService: NgbModal,
private relationshipService: RelationshipService, private relationshipService: RelationshipService,
private selectableListService: SelectableListService, private selectableListService: SelectableListService,
private itemService: ItemDataService, private itemService: ItemDataService,
@@ -243,6 +249,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
private ref: ChangeDetectorRef, private ref: ChangeDetectorRef,
private formService: FormService, private formService: FormService,
private formBuilderService: FormBuilderService, private formBuilderService: FormBuilderService,
private submissionService: SubmissionService
) { ) {
super(ref, componentFactoryResolver, layoutService, validationService, dynamicFormComponentService, relationService); super(ref, componentFactoryResolver, layoutService, validationService, dynamicFormComponentService, relationService);
} }
@@ -257,6 +264,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
if (this.isRelationship || isWrapperAroundRelationshipList) { if (this.isRelationship || isWrapperAroundRelationshipList) {
const config = this.model.relationshipConfig || this.model.relationship; const config = this.model.relationshipConfig || this.model.relationship;
const relationshipOptions = Object.assign(new RelationshipOptions(), config); const relationshipOptions = Object.assign(new RelationshipOptions(), config);
this.listId = `list-${this.model.submissionId}-${relationshipOptions.relationshipType}`;
this.setItem(); this.setItem();
if (isWrapperAroundRelationshipList || !this.model.repeatable) { if (isWrapperAroundRelationshipList || !this.model.repeatable) {
@@ -366,10 +374,68 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
} }
} }
hasRelationship() {
return isNotEmpty(this.model) && this.model.hasOwnProperty('relationship') && isNotEmpty(this.model.relationship);
}
isVirtual() {
const value: FormFieldMetadataValueObject = this.model.metadataValue;
return isNotEmpty(value) && value.isVirtual;
}
public hasResultsSelected(): Observable<boolean> { public hasResultsSelected(): Observable<boolean> {
return this.model.value.pipe(map((list: SearchResult<DSpaceObject>[]) => isNotEmpty(list))); return this.model.value.pipe(map((list: SearchResult<DSpaceObject>[]) => isNotEmpty(list)));
} }
/**
* Open a modal where the user can select relationships to be added to item being submitted
*/
openLookup() {
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, {
size: 'lg'
});
if (hasValue(this.model.value)) {
this.focus.emit({
$event: new Event('focus'),
context: this.context,
control: this.control,
model: this.model,
type: DynamicFormControlEventType.Focus
} as DynamicFormControlEvent);
this.model.value = null;
this.change.emit({
$event: new Event('change'),
context: this.context,
control: this.control,
model: this.model,
type: DynamicFormControlEventType.Change
} as DynamicFormControlEvent);
this.submissionService.dispatchSave(this.model.submissionId);
}
const modalComp = this.modalRef.componentInstance;
if (hasValue(this.model.value) && !this.model.readOnly) {
if (typeof this.model.value === 'string') {
modalComp.query = this.model.value;
} else if (typeof this.model.value.value === 'string') {
modalComp.query = this.model.value.value;
}
}
modalComp.repeatable = this.model.repeatable;
modalComp.listId = this.listId;
modalComp.relationshipOptions = this.model.relationship;
modalComp.label = this.model.relationship.relationshipType;
modalComp.metadataFields = this.model.metadataFields;
modalComp.item = this.item;
modalComp.collection = this.collection;
modalComp.submissionId = this.model.submissionId;
}
/** /**
* Callback for the remove event, * Callback for the remove event,
* remove the current control from its array * remove the current control from its array

View File

@@ -147,7 +147,6 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
ngOnInit(): void { ngOnInit(): void {
this.setItem(); this.setItem();
this.submissionService.dispatchSave(this.submissionId);
this.selection$ = this.selectableListService this.selection$ = this.selectableListService
.getSelectableList(this.listId) .getSelectableList(this.listId)
.pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : [])); .pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []));

View File

@@ -14,7 +14,6 @@
<!--Array with repeatable items--> <!--Array with repeatable items-->
<div *ngIf="(!context.notRepeatable) && !isVirtual(context, index)" <div *ngIf="(!context.notRepeatable) && !isVirtual(context, index)"
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="remove button">
<button type="button" class="btn btn-secondary" <button type="button" class="btn btn-secondary"
title="{{'form.remove' | translate}}" title="{{'form.remove' | translate}}"
(click)="removeItem($event, context, index)" (click)="removeItem($event, context, index)"
@@ -22,7 +21,6 @@
<span attr.aria-label="{{'form.remove' | translate}}"><i class="fas fa-trash" aria-hidden="true"></i></span> <span attr.aria-label="{{'form.remove' | translate}}"><i class="fas fa-trash" aria-hidden="true"></i></span>
</button> </button>
</div> </div>
</div>
<div *ngIf="(!context.notRepeatable) && index === (group.context.groups.length - 1)" class="clearfix pl-4 w-100"> <div *ngIf="(!context.notRepeatable) && index === (group.context.groups.length - 1)" class="clearfix pl-4 w-100">
<div class="btn-group" role="group" aria-label="remove button"> <div class="btn-group" role="group" aria-label="remove button">
<button type="button" class="ds-form-add-more btn btn-link" <button type="button" class="ds-form-add-more btn btn-link"
@@ -31,13 +29,6 @@
(click)="insertItem($event, group.context, group.context.groups.length)"> (click)="insertItem($event, group.context, group.context.groups.length)">
<span attr.aria-label="{{'form.add' | translate}}"><i class="fas fa-plus"></i> {{'form.add' | translate}}</span> <span attr.aria-label="{{'form.add' | translate}}"><i class="fas fa-plus"></i> {{'form.add' | translate}}</span>
</button> </button>
<button *ngIf="hasRelationship(context, index)"
type="button" class="ds-form-add-more btn btn-link"
title="{{'form.add' | translate}}"
[disabled]="isItemReadOnly(context, index)"
(click)="openLookup(context, index)">
<span attr.aria-label="{{'form.add' | translate}}"><i class="fas fa-search-plus"></i> {{'form.lookup' | translate}}</span>
</button>
</div> </div>
</div> </div>
@@ -54,6 +45,7 @@
</div> </div>
</div> </div>
</ng-template> </ng-template>
</ds-dynamic-form> </ds-dynamic-form>
<ng-content></ng-content> <ng-content></ng-content>

View File

@@ -10,15 +10,13 @@ import {
DynamicFormGroupModel, DynamicFormGroupModel,
DynamicFormLayout, DynamicFormLayout,
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { findIndex } from 'lodash'; import { findIndex } from 'lodash';
import { FormBuilderService } from './builder/form-builder.service'; import { FormBuilderService } from './builder/form-builder.service';
import { hasValue, isNotEmpty, isNotNull, isNull } from '../empty.util'; import { hasValue, isNotEmpty, isNotNull, isNull } from '../empty.util';
import { FormService } from './form.service'; import { FormService } from './form.service';
import { FormEntry, FormError } from './form.reducer'; import { FormEntry, FormError } from './form.reducer';
import { DsDynamicLookupRelationModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
import { RelationshipOptions } from './builder/models/relationship-options.model';
import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model'; import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model';
/** /**
@@ -101,8 +99,7 @@ export class FormComponent implements OnDestroy, OnInit {
constructor(private formService: FormService, constructor(private formService: FormService,
protected changeDetectorRef: ChangeDetectorRef, protected changeDetectorRef: ChangeDetectorRef,
private formBuilderService: FormBuilderService, private formBuilderService: FormBuilderService) {
private modalService: NgbModal) {
} }
/** /**
@@ -169,7 +166,6 @@ export class FormComponent implements OnDestroy, OnInit {
filter((formState: FormEntry) => !!formState && (isNotEmpty(formState.errors) || isNotEmpty(this.formErrors))), filter((formState: FormEntry) => !!formState && (isNotEmpty(formState.errors) || isNotEmpty(this.formErrors))),
map((formState) => formState.errors), map((formState) => formState.errors),
distinctUntilChanged()) distinctUntilChanged())
// .delay(100) // this terrible delay is here to prevent the detection change error
.subscribe((errors: FormError[]) => { .subscribe((errors: FormError[]) => {
const { formGroup, formModel } = this; const { formGroup, formModel } = this;
errors errors
@@ -320,42 +316,6 @@ export class FormComponent implements OnDestroy, OnInit {
return isNotEmpty(value) && value.isVirtual; return isNotEmpty(value) && value.isVirtual;
} }
hasRelationship(arrayContext: DynamicFormArrayModel, index: number) {
const context = arrayContext.groups[index];
const model = context.group[0] as any;
return isNotEmpty(model) && model.hasOwnProperty('relationship') && isNotEmpty(model.relationship);
}
/**
* Open a modal where the user can select relationships to be added to item being submitted
*/
openLookup(arrayContext: DynamicFormArrayModel, index: number) {
const context = arrayContext.groups[index];
const model = context.group[0] as any;
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, {
size: 'lg'
});
const modalComp = this.modalRef.componentInstance;
if (hasValue(model.value) && !model.readOnly) {
if (typeof model.value === 'string') {
modalComp.query = model.value;
} else if (typeof model.value.value === 'string') {
modalComp.query = model.value.value;
}
}
const config = model.relationshipConfig || model.relationship;
const relationshipOptions = Object.assign(new RelationshipOptions(), config);
modalComp.repeatable = model.repeatable;
modalComp.listId = `list-${model.submissionId}-${relationshipOptions.relationshipType}`;
modalComp.relationshipOptions = model.relationship;
modalComp.label = model.relationship.relationshipType;
modalComp.metadataFields = model.metadataFields;
modalComp.submissionId = model.submissionId;
}
protected getEvent($event: any, arrayContext: DynamicFormArrayModel, index: number, type: string): DynamicFormControlEvent { protected getEvent($event: any, arrayContext: DynamicFormArrayModel, index: number, type: string): DynamicFormControlEvent {
const context = arrayContext.groups[index]; const context = arrayContext.groups[index];
const itemGroupModel = context.context; const itemGroupModel = context.context;