mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-18 15:33:04 +00:00
[CST-3782] Changes in order to display add more and lookup button at the end of the repeatable fields array
This commit is contained in:
@@ -11,13 +11,12 @@
|
|||||||
'd-none': value?.isVirtual && (model.hasSelectableMetadata || context?.index > 0)}">
|
'd-none': value?.isVirtual && (model.hasSelectableMetadata || context?.index > 0)}">
|
||||||
<div [ngClass]="getClass('grid', 'control')">
|
<div [ngClass]="getClass('grid', 'control')">
|
||||||
<ng-container #componentViewContainer></ng-container>
|
<ng-container #componentViewContainer></ng-container>
|
||||||
|
<!-- In case of repeatable fields show hint only for first element -->
|
||||||
<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>
|
||||||
<!-- -->
|
<!-- In case of repeatable fields show empty space for all elements except the first -->
|
||||||
<small *ngIf="(model.type !== 'GROUP') && !asBootstrapFormGroup
|
<div *ngIf="context?.index > 0
|
||||||
&& (context?.index > 0 && context?.index < context?.context?.groups?.length - 1)
|
&& (!showErrorMessages || errorMessages.length === 0)" class="clearfix w-100 mb-2"></div>
|
||||||
&& (!showErrorMessages || errorMessages.length === 0)"
|
|
||||||
class="text-muted" [ngClass]="getClass('element', 'hint')"> </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>
|
||||||
@@ -37,14 +36,6 @@
|
|||||||
<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.d-none]="context?.index > 0">
|
|
||||||
<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">
|
||||||
@@ -70,6 +61,7 @@
|
|||||||
[relationshipOptions]="model.relationship"
|
[relationshipOptions]="model.relationship"
|
||||||
>
|
>
|
||||||
</ds-existing-relation-list-element>
|
</ds-existing-relation-list-element>
|
||||||
|
<div class="clearfix w-100 mb-2"></div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -37,7 +37,6 @@ import {
|
|||||||
DynamicFormControl,
|
DynamicFormControl,
|
||||||
DynamicFormControlContainerComponent,
|
DynamicFormControlContainerComponent,
|
||||||
DynamicFormControlEvent,
|
DynamicFormControlEvent,
|
||||||
DynamicFormControlEventType,
|
|
||||||
DynamicFormControlModel,
|
DynamicFormControlModel,
|
||||||
DynamicFormLayout,
|
DynamicFormLayout,
|
||||||
DynamicFormLayoutService,
|
DynamicFormLayoutService,
|
||||||
@@ -83,18 +82,16 @@ 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,
|
||||||
getFirstSucceededRemoteDataPayload,
|
getFirstSucceededRemoteDataPayload,
|
||||||
getPaginatedListPayload,
|
getPaginatedListPayload,
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload
|
||||||
getFirstSucceededRemoteData
|
|
||||||
} from '../../../../core/shared/operators';
|
} from '../../../../core/shared/operators';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
@@ -110,7 +107,6 @@ 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';
|
||||||
@@ -206,7 +202,6 @@ 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;
|
||||||
@@ -239,7 +234,6 @@ 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,
|
||||||
@@ -249,7 +243,6 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -264,7 +257,6 @@ 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) {
|
||||||
@@ -378,45 +370,6 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
|
|||||||
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'
|
|
||||||
});
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasValue(this.model.value)) {
|
|
||||||
this.model.value = '';
|
|
||||||
this.onChange({
|
|
||||||
$event: { previousIndex: 0 },
|
|
||||||
context: { index: 0 },
|
|
||||||
control: this.control,
|
|
||||||
model: this.model,
|
|
||||||
type: DynamicFormControlEventType.Change
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.submissionService.dispatchSave(this.model.submissionId);
|
|
||||||
|
|
||||||
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
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
<button type="button" class="btn btn-secondary"
|
<button type="button" class="btn btn-secondary"
|
||||||
|
title="{{'form.remove' | translate}}"
|
||||||
(click)="removeSelection()">
|
(click)="removeSelection()">
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -14,6 +14,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils
|
|||||||
import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
|
import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
|
||||||
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
|
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { TranslateLoaderMock } from '../../../../testing/translate-loader.mock';
|
||||||
|
|
||||||
describe('ExistingMetadataListElementComponent', () => {
|
describe('ExistingMetadataListElementComponent', () => {
|
||||||
let component: ExistingMetadataListElementComponent;
|
let component: ExistingMetadataListElementComponent;
|
||||||
@@ -65,6 +67,14 @@ describe('ExistingMetadataListElementComponent', () => {
|
|||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
init();
|
init();
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
declarations: [ExistingMetadataListElementComponent],
|
declarations: [ExistingMetadataListElementComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SelectableListService, useValue: selectionService },
|
{ provide: SelectableListService, useValue: selectionService },
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
[ngClass]="[getClass('element', 'group'), getClass('grid', 'group')]"
|
[ngClass]="[getClass('element', 'group'), getClass('grid', 'group')]"
|
||||||
cdkDrag cdkDragHandle>
|
cdkDrag cdkDragHandle>
|
||||||
<!-- Item content -->
|
<!-- Item content -->
|
||||||
<i class="drag-icon fas fa-grip-vertical fa-fw"></i>
|
<i class="drag-icon fas fa-grip-vertical fa-fw" [class.invisible]="model.groups.length === 1"></i>
|
||||||
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
|
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: groupModel"></ng-container>
|
||||||
<ds-dynamic-form-control-container *ngFor="let _model of groupModel.group"
|
<ds-dynamic-form-control-container *ngFor="let _model of groupModel.group"
|
||||||
[bindId]="false"
|
[bindId]="false"
|
||||||
|
@@ -14,7 +14,8 @@
|
|||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
margin: $btn-padding-y 0;
|
margin: $btn-padding-y 0;
|
||||||
line-height: $btn-line-height;
|
line-height: $btn-line-height;
|
||||||
text-indent: 0.5 * $spacer
|
text-indent: 0.5 * $spacer;
|
||||||
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
|
@@ -6,7 +6,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ngb-tabset>
|
<ds-loading *ngIf="!item || !collection"></ds-loading>
|
||||||
|
<ngb-tabset *ngIf="item && collection">
|
||||||
<ngb-tab [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + relationshipOptions.relationshipType | translate : {count: (totalInternal$ | async)}">
|
<ngb-tab [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + relationshipOptions.relationshipType | translate : {count: (totalInternal$ | async)}">
|
||||||
<ng-template ngbTabContent>
|
<ng-template ngbTabContent>
|
||||||
<ds-dynamic-lookup-relation-search-tab
|
<ds-dynamic-lookup-relation-search-tab
|
||||||
|
@@ -20,6 +20,10 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils
|
|||||||
import { createPaginatedList } from '../../../../testing/utils.test';
|
import { createPaginatedList } from '../../../../testing/utils.test';
|
||||||
import { ExternalSourceService } from '../../../../../core/data/external-source.service';
|
import { ExternalSourceService } from '../../../../../core/data/external-source.service';
|
||||||
import { LookupRelationService } from '../../../../../core/data/lookup-relation.service';
|
import { LookupRelationService } from '../../../../../core/data/lookup-relation.service';
|
||||||
|
import { SubmissionService } from '../../../../../submission/submission.service';
|
||||||
|
import { SubmissionObjectDataService } from '../../../../../core/submission/submission-object-data.service';
|
||||||
|
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
|
||||||
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
|
|
||||||
describe('DsDynamicLookupRelationModalComponent', () => {
|
describe('DsDynamicLookupRelationModalComponent', () => {
|
||||||
let component: DsDynamicLookupRelationModalComponent;
|
let component: DsDynamicLookupRelationModalComponent;
|
||||||
@@ -27,6 +31,7 @@ describe('DsDynamicLookupRelationModalComponent', () => {
|
|||||||
let item;
|
let item;
|
||||||
let item1;
|
let item1;
|
||||||
let item2;
|
let item2;
|
||||||
|
let testWSI;
|
||||||
let searchResult1;
|
let searchResult1;
|
||||||
let searchResult2;
|
let searchResult2;
|
||||||
let listID;
|
let listID;
|
||||||
@@ -39,6 +44,8 @@ describe('DsDynamicLookupRelationModalComponent', () => {
|
|||||||
let externalSourceService;
|
let externalSourceService;
|
||||||
let lookupRelationService;
|
let lookupRelationService;
|
||||||
let submissionId;
|
let submissionId;
|
||||||
|
let submissionService;
|
||||||
|
let submissionObjectDataService;
|
||||||
|
|
||||||
const externalSources = [
|
const externalSources = [
|
||||||
Object.assign(new ExternalSource(), {
|
Object.assign(new ExternalSource(), {
|
||||||
@@ -54,11 +61,16 @@ describe('DsDynamicLookupRelationModalComponent', () => {
|
|||||||
];
|
];
|
||||||
const totalLocal = 10;
|
const totalLocal = 10;
|
||||||
const totalExternal = 8;
|
const totalExternal = 8;
|
||||||
|
const collection: Collection = new Collection();
|
||||||
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
item = Object.assign(new Item(), { uuid: '7680ca97-e2bd-4398-bfa7-139a8673dc42', metadata: {} });
|
item = Object.assign(new Item(), { uuid: '7680ca97-e2bd-4398-bfa7-139a8673dc42', metadata: {} });
|
||||||
item1 = Object.assign(new Item(), { uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e' });
|
item1 = Object.assign(new Item(), { uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e' });
|
||||||
item2 = Object.assign(new Item(), { uuid: 'c8279647-1acc-41ae-b036-951d5f65649b' });
|
item2 = Object.assign(new Item(), { uuid: 'c8279647-1acc-41ae-b036-951d5f65649b' });
|
||||||
|
testWSI = new WorkspaceItem();
|
||||||
|
testWSI.item = createSuccessfulRemoteDataObject$(item);
|
||||||
|
testWSI.collection = createSuccessfulRemoteDataObject$(collection);
|
||||||
searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 });
|
searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 });
|
||||||
searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 });
|
searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 });
|
||||||
listID = '6b0c8221-fcb4-47a8-b483-ca32363fffb3';
|
listID = '6b0c8221-fcb4-47a8-b483-ca32363fffb3';
|
||||||
@@ -80,6 +92,12 @@ describe('DsDynamicLookupRelationModalComponent', () => {
|
|||||||
getTotalLocalResults: observableOf(totalLocal),
|
getTotalLocalResults: observableOf(totalLocal),
|
||||||
getTotalExternalResults: observableOf(totalExternal)
|
getTotalExternalResults: observableOf(totalExternal)
|
||||||
});
|
});
|
||||||
|
submissionService = jasmine.createSpyObj('SubmissionService', {
|
||||||
|
dispatchSave: jasmine.createSpy('dispatchSave')
|
||||||
|
});
|
||||||
|
submissionObjectDataService = jasmine.createSpyObj('SubmissionObjectDataService', {
|
||||||
|
findById: createSuccessfulRemoteDataObject$(testWSI)
|
||||||
|
});
|
||||||
submissionId = '1234';
|
submissionId = '1234';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +121,8 @@ describe('DsDynamicLookupRelationModalComponent', () => {
|
|||||||
provide: RelationshipService, useValue: { getNameVariant: () => observableOf(nameVariant) }
|
provide: RelationshipService, useValue: { getNameVariant: () => observableOf(nameVariant) }
|
||||||
},
|
},
|
||||||
{ provide: RelationshipTypeService, useValue: {} },
|
{ provide: RelationshipTypeService, useValue: {} },
|
||||||
|
{ provide: SubmissionService, useValue: submissionService },
|
||||||
|
{ provide: SubmissionObjectDataService, useValue: submissionObjectDataService },
|
||||||
{
|
{
|
||||||
provide: Store, useValue: {
|
provide: Store, useValue: {
|
||||||
// tslint:disable-next-line:no-empty
|
// tslint:disable-next-line:no-empty
|
||||||
|
@@ -24,6 +24,11 @@ import { PaginatedList } from '../../../../../core/data/paginated-list.model';
|
|||||||
import { ExternalSource } from '../../../../../core/shared/external-source.model';
|
import { ExternalSource } from '../../../../../core/shared/external-source.model';
|
||||||
import { ExternalSourceService } from '../../../../../core/data/external-source.service';
|
import { ExternalSourceService } from '../../../../../core/data/external-source.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { followLink } from '../../../../utils/follow-link-config.model';
|
||||||
|
import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model';
|
||||||
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
|
import { SubmissionService } from '../../../../../submission/submission.service';
|
||||||
|
import { SubmissionObjectDataService } from '../../../../../core/submission/submission-object-data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dynamic-lookup-relation-modal',
|
selector: 'ds-dynamic-lookup-relation-modal',
|
||||||
@@ -113,6 +118,11 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
|
|||||||
*/
|
*/
|
||||||
totalExternal$: Observable<number[]>;
|
totalExternal$: Observable<number[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of subscriptions to unsubscribe from
|
||||||
|
*/
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public modal: NgbActiveModal,
|
public modal: NgbActiveModal,
|
||||||
private selectableListService: SelectableListService,
|
private selectableListService: SelectableListService,
|
||||||
@@ -121,14 +131,18 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
|
|||||||
private externalSourceService: ExternalSourceService,
|
private externalSourceService: ExternalSourceService,
|
||||||
private lookupRelationService: LookupRelationService,
|
private lookupRelationService: LookupRelationService,
|
||||||
private searchConfigService: SearchConfigurationService,
|
private searchConfigService: SearchConfigurationService,
|
||||||
|
private submissionService: SubmissionService,
|
||||||
|
private submissionObjectService: SubmissionObjectDataService,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private store: Store<AppState>,
|
private store: Store<AppState>,
|
||||||
private router: Router,
|
private router: Router
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
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 : []));
|
||||||
@@ -182,6 +196,24 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize this.item$ based on this.model.submissionId
|
||||||
|
*/
|
||||||
|
private setItem() {
|
||||||
|
const submissionObject$ = this.submissionObjectService
|
||||||
|
.findById(this.submissionId, true, true, followLink('item'), followLink('collection')).pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload()
|
||||||
|
);
|
||||||
|
|
||||||
|
const item$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
|
||||||
|
const collection$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.collection as Observable<RemoteData<Collection>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
|
||||||
|
|
||||||
|
this.subs.push(item$.subscribe((item) => this.item = item));
|
||||||
|
this.subs.push(collection$.subscribe((collection) => this.collection = collection));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subscription updating relationships with name variants
|
* Add a subscription updating relationships with name variants
|
||||||
* @param sri The search result to track name variants for
|
* @param sri The search result to track name variants for
|
||||||
@@ -240,5 +272,8 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
|
|||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.router.navigate([], {});
|
this.router.navigate([], {});
|
||||||
Object.values(this.subMap).forEach((subscription) => subscription.unsubscribe());
|
Object.values(this.subMap).forEach((subscription) => subscription.unsubscribe());
|
||||||
|
this.subs
|
||||||
|
.filter((sub) => hasValue(sub))
|
||||||
|
.forEach((sub) => sub.unsubscribe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,27 +12,41 @@
|
|||||||
(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.notRepeatable)"
|
<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="Add and remove button">
|
<div class="btn-group" role="group" aria-label="remove button">
|
||||||
<button *ngIf="index === 0" type="button" class="btn btn-secondary"
|
<button type="button" class="btn btn-secondary"
|
||||||
|
title="{{'form.remove' | translate}}"
|
||||||
|
(click)="removeItem($event, context, index)"
|
||||||
|
[disabled]="isItemReadOnly(context, index)">
|
||||||
|
<span attr.aria-label="{{'form.remove' | translate}}"><i class="fas fa-trash" aria-hidden="true"></i></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
<button type="button" class="ds-form-add-more btn btn-link"
|
||||||
|
title="{{'form.add' | translate}}"
|
||||||
[disabled]="isItemReadOnly(context, index)"
|
[disabled]="isItemReadOnly(context, index)"
|
||||||
(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}}">{{'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="index > 0" type="button" class="btn btn-secondary"
|
<button *ngIf="hasRelationship(context, index)"
|
||||||
(click)="removeItem($event, context, index)"
|
type="button" class="ds-form-add-more btn btn-link"
|
||||||
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
|
title="{{'form.add' | translate}}"
|
||||||
<span attr.aria-label="{{'form.remove' | translate}}">{{'form.remove' | translate}}</span>
|
[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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Array with non repeatable items - Only delete button-->
|
<!--Array with non repeatable items - Only discard button-->
|
||||||
<div *ngIf="context.notRepeatable && context.showButtons && group.context.groups.length > 1"
|
<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">
|
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">
|
<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.discard' | translate}}"
|
||||||
(click)="removeItem($event, context, index)"
|
(click)="removeItem($event, context, index)"
|
||||||
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
|
[disabled]="group.context.groups.length === 1 || isItemReadOnly(context, index)">
|
||||||
<span attr.aria-label="{{'form.discard' | translate}}">{{'form.discard' | translate}}</span>
|
<span attr.aria-label="{{'form.discard' | translate}}">{{'form.discard' | translate}}</span>
|
||||||
@@ -40,7 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</ds-dynamic-form>
|
</ds-dynamic-form>
|
||||||
|
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
|
@@ -15,6 +15,11 @@
|
|||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.ds-form-add-more:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ds-form-input-value {
|
.ds-form-input-value {
|
||||||
border-top-left-radius: 0 !important;
|
border-top-left-radius: 0 !important;
|
||||||
border-bottom-left-radius: 0 !important;
|
border-bottom-left-radius: 0 !important;
|
||||||
|
@@ -2,6 +2,7 @@ import { distinctUntilChanged, filter, map } from 'rxjs/operators';
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
DynamicFormArrayModel,
|
DynamicFormArrayModel,
|
||||||
DynamicFormControlEvent,
|
DynamicFormControlEvent,
|
||||||
@@ -9,15 +10,16 @@ import {
|
|||||||
DynamicFormGroupModel,
|
DynamicFormGroupModel,
|
||||||
DynamicFormLayout,
|
DynamicFormLayout,
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
|
import { NgbModal, 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 { Observable, Subscription } from 'rxjs';
|
|
||||||
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 { QUALDROP_GROUP_SUFFIX } from './builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model';
|
import { DsDynamicLookupRelationModalComponent } from './builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component';
|
||||||
|
import { RelationshipOptions } from './builder/models/relationship-options.model';
|
||||||
const QUALDROP_GROUP_REGEX = new RegExp(`${QUALDROP_GROUP_SUFFIX}_\\d+$`);
|
import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default form component.
|
* The default form component.
|
||||||
@@ -87,9 +89,9 @@ export class FormComponent implements OnDestroy, OnInit {
|
|||||||
@Output() submitForm: EventEmitter<Observable<any>> = new EventEmitter<Observable<any>>();
|
@Output() submitForm: EventEmitter<Observable<any>> = new EventEmitter<Observable<any>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object of FormGroup type
|
* Reference to NgbModal
|
||||||
*/
|
*/
|
||||||
// public formGroup: FormGroup;
|
modalRef: NgbModalRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array to track all subscriptions and unsubscribe them onDestroy
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
@@ -99,7 +101,8 @@ 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -311,6 +314,48 @@ export class FormComponent implements OnDestroy, OnInit {
|
|||||||
this.formService.changeForm(this.formId, this.formModel);
|
this.formService.changeForm(this.formId, this.formModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isVirtual(arrayContext: DynamicFormArrayModel, index: number) {
|
||||||
|
const context = arrayContext.groups[index];
|
||||||
|
const value: FormFieldMetadataValueObject = (context.group[0] as any).metadataValue;
|
||||||
|
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;
|
||||||
|
@@ -1257,7 +1257,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"form.add": "Add",
|
"form.add": "Add more",
|
||||||
|
|
||||||
"form.add-help": "Click here to add the current entry and to add another one",
|
"form.add-help": "Click here to add the current entry and to add another one",
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user