-
-
-
+
+
@@ -34,8 +35,41 @@
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
index 4dced85eb8..f8a06f72f1 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
@@ -1,12 +1,14 @@
import {
- ChangeDetectionStrategy,
+ ChangeDetectionStrategy, ChangeDetectorRef,
Component,
ComponentFactoryResolver,
ContentChildren,
EventEmitter,
Input,
+ NgZone,
OnChanges,
OnDestroy,
+ OnInit,
Output,
QueryList,
SimpleChanges,
@@ -48,6 +50,10 @@ import {
DynamicNGBootstrapTimePickerComponent
} from '@ng-dynamic-forms/ui-ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
+import {
+ Reorderable,
+ ReorderableRelationship
+} from './existing-metadata-list-element/existing-metadata-list-element.component';
import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model';
import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model';
@@ -56,7 +62,7 @@ import { DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER } from './models/date-picker/dat
import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP } from './models/lookup/dynamic-lookup.model';
import { DynamicListCheckboxGroupModel } from './models/list/dynamic-list-checkbox-group.model';
import { DynamicListRadioGroupModel } from './models/list/dynamic-list-radio-group.model';
-import { isNotEmpty, isNotUndefined } from '../../../empty.util';
+import { hasValue, isNotEmpty, isNotUndefined } from '../../../empty.util';
import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_NAME } from './models/lookup/dynamic-lookup-name.model';
import { DsDynamicTagComponent } from './models/tag/dynamic-tag.component';
import { DsDatePickerComponent } from './models/date-picker/date-picker.component';
@@ -69,12 +75,30 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a
import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components';
import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model';
import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component';
-import { map } from 'rxjs/operators';
-import { Observable } from 'rxjs';
+import { map, startWith, switchMap, take } from 'rxjs/operators';
+import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
import { SearchResult } from '../../../search/search-result.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 { SelectableListService } from '../../../object-list/selectable-list/selectable-list.service';
import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component';
import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model';
+import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';
+import { getAllSucceededRemoteData, getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Store } from '@ngrx/store';
+import { AppState } from '../../../../app.reducer';
+import { SubmissionObjectDataService } from '../../../../core/submission/submission-object-data.service';
+import { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
+import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { MetadataValue } from '../../../../core/shared/metadata.models';
+import { DynamicRowArrayModel } from './models/ds-dynamic-row-array-model';
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type
| null {
switch (model.type) {
@@ -145,7 +169,7 @@ export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<
templateUrl: './ds-dynamic-form-control-container.component.html',
changeDetection: ChangeDetectionStrategy.Default
})
-export class DsDynamicFormControlContainerComponent extends DynamicFormControlContainerComponent implements OnChanges {
+export class DsDynamicFormControlContainerComponent extends DynamicFormControlContainerComponent implements OnInit, OnChanges, OnDestroy {
@ContentChildren(DynamicTemplateDirective) contentTemplateList: QueryList;
// tslint:disable-next-line:no-input-rename
@Input('templates') inputTemplateList: QueryList;
@@ -158,6 +182,18 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() hasErrorMessaging = false;
@Input() layout = null as DynamicFormLayout;
@Input() model: any;
+ reorderable$: Observable;
+ reorderable: ReorderableRelationship;
+ hasRelationLookup: boolean;
+ modalRef: NgbModalRef;
+ item: Item;
+ listId: string;
+ searchConfig: string;
+
+ /**
+ * List of subscriptions to unsubscribe from
+ */
+ private subs: Subscription[] = [];
/* tslint:disable:no-output-rename */
@Output('dfBlur') blur: EventEmitter = new EventEmitter();
@@ -178,11 +214,97 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
protected layoutService: DynamicFormLayoutService,
protected validationService: DynamicFormValidationService,
protected translateService: TranslateService,
+ private modalService: NgbModal,
+ private relationService: RelationshipService,
+ private selectableListService: SelectableListService,
+ private itemService: ItemDataService,
+ private relationshipService: RelationshipService,
+ private zone: NgZone,
+ private store: Store,
+ private submissionObjectService: SubmissionObjectDataService,
+ private ref: ChangeDetectorRef
) {
super(componentFactoryResolver, layoutService, validationService);
}
+ ngOnInit(): void {
+ if (this.model.type === 'ARRAY') {
+ console.log((this.model as DynamicRowArrayModel).get(1));
+ }
+ this.hasRelationLookup = hasValue(this.model.relationship);
+ if (this.hasRelationLookup) {
+ this.listId = 'list-' + this.model.relationship.relationshipType;
+ const item$ = this.submissionObjectService
+ .findById(this.model.submissionId).pipe(
+ getAllSucceededRemoteData(),
+ getRemoteDataPayload(),
+ switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable>)
+ .pipe(
+ getAllSucceededRemoteData(),
+ getRemoteDataPayload()
+ )
+ )
+ );
+
+ this.subs.push(item$.subscribe((item) => this.item = item));
+ const value = Object.assign(new MetadataValue(), this.model.value);
+ if (hasValue(value) && value.isVirtual) {
+ this.reorderable$ = this.relationshipService.findById(value.virtualValue)
+ .pipe(
+ getAllSucceededRemoteData(),
+ getRemoteDataPayload(),
+ switchMap((relationship: Relationship) =>
+ relationship.leftItem.pipe(
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ map((leftItem: Item) => {
+ return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid)
+ }),
+ )
+ )
+ );
+
+ this.subs.push(this.reorderable$.subscribe((rs) => {
+ this.reorderable = rs;
+ this.ref.detectChanges();
+ }));
+
+ }
+ // this.reorderable$ =
+ // item$.pipe(
+ // switchMap((item) => this.relationService.getItemRelationshipsByLabel(item, this.model.relationship.relationshipType)
+ // .pipe(
+ // getAllSucceededRemoteData(),
+ // getRemoteDataPayload(),
+ // map((relationshipList: PaginatedList) => relationshipList.page),
+ // startWith([]),
+ // switchMap((relationships: Relationship[]) =>
+ // observableCombineLatest(
+ // relationships.map((relationship: Relationship) =>
+ // relationship.leftItem.pipe(
+ // getSucceededRemoteData(),
+ // getRemoteDataPayload(),
+ // map((leftItem: Item) => {
+ // return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid)
+ // }),
+ // )
+ // ))),
+ // map((relationships: ReorderableRelationship[]) =>
+ // relationships
+ // .sort((a: Reorderable, b: Reorderable) => {
+ // return Math.sign(a.getPlace() - b.getPlace());
+ // })
+ // )
+ // )
+ // )
+ // );
+
+ this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe(
+ map((items: RemoteData>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))),
+ ).subscribe((relatedItems: Array>) => this.selectableListService.select(this.listId, relatedItems));
+ }
+ }
ngOnChanges(changes: SimpleChanges) {
if (changes) {
@@ -224,4 +346,53 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
public hasResultsSelected(): Observable {
return this.model.value.pipe(map((list: Array>) => isNotEmpty(list)));
}
+
+ openLookup() {
+ this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, {
+ size: 'lg'
+ });
+ const modalComp = this.modalRef.componentInstance;
+ modalComp.repeatable = this.model.repeatable;
+ modalComp.listId = this.listId;
+ modalComp.relationshipOptions = this.model.relationship;
+ modalComp.label = this.model.label;
+ modalComp.metadataFields = this.model.metadataFields;
+ modalComp.item = this.item;
+ }
+
+ // moveSelection(event: CdkDragDrop) {
+ // this.zone.runOutsideAngular(() => {
+ // moveItemInArray(this.reorderables, event.previousIndex, event.currentIndex);
+ // const reorderables = this.reorderables.map((reo: Reorderable, index: number) => {
+ // reo.oldIndex = reo.getPlace();
+ // reo.newIndex = index;
+ // return reo;
+ // }
+ // );
+ // return observableCombineLatest(reorderables.map((rel: ReorderableRelationship) => {
+ // if (rel.oldIndex !== rel.newIndex) {
+ // return this.relationshipService.updatePlace(rel);
+ // } else {
+ // return observableOf(undefined);
+ // }
+ // })
+ // ).pipe(getSucceededRemoteData()).subscribe();
+ // })
+ // }
+
+ /**
+ * Unsubscribe from all subscriptions
+ */
+ ngOnDestroy(): void {
+ this.subs
+ .filter((sub) => hasValue(sub))
+ .forEach((sub) => sub.unsubscribe());
+ }
+
+ /**
+ * Prevent unnecessary rerendering so fields don't lose focus
+ */
+ trackReorderable(index, reorderable: Reorderable) {
+ return hasValue(reorderable) ? reorderable.getId() : undefined;
+ }
}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.html
index 650fd45c26..4d8123a4b9 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.html
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.html
@@ -1,4 +1,4 @@
-
+ (dfChange)="onEvent($event, 'change')"
+ (dfFocus)="onEvent($event, 'focus')">
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
index 2d0af016e4..a834930bc1 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
@@ -14,7 +14,6 @@ import { SelectableListService } from '../../../../object-list/selectable-list/s
import { Store } from '@ngrx/store';
import { AppState } from '../../../../../app.reducer';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
-import { Metadata } from '../../../../../core/shared/metadata.utils';
export abstract class Reorderable {
constructor(public oldIndex?: number, public newIndex?: number) {
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts
index af05d5bf35..0864d2d1ff 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts
@@ -44,19 +44,18 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
this.required = config.required;
this.metadataFields = config.metadataFields;
this.submissionId = config.submissionId;
-
this.valueUpdates = new Subject();
this.valueUpdates.subscribe((value: string) => this.value = value);
}
get value() {
- const firstValue = (this.get(0) as DsDynamicInputModel).value;
- const secondValue = (this.get(1) as DsDynamicInputModel).value;
+ const firstValue = (this.get(0) as DsDynamicInputModel).value as any;
+ const secondValue = (this.get(1) as DsDynamicInputModel).value as any;
if (isNotEmpty(firstValue) && isNotEmpty(secondValue)) {
- return new FormFieldMetadataValueObject(firstValue + this.separator + secondValue);
+ return Object.assign(new FormFieldMetadataValueObject(), firstValue, firstValue.value + this.separator + secondValue.value);
} else if (isNotEmpty(firstValue)) {
- return new FormFieldMetadataValueObject(firstValue);
+ return Object.assign(new FormFieldMetadataValueObject(), firstValue, firstValue.value);
} else {
return null;
}
@@ -71,12 +70,7 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
} else {
tempValue = value.value;
}
-
- if (tempValue.includes(this.separator)) {
- values = tempValue.split(this.separator);
- } else {
- values = [tempValue, null];
- }
+ values = [...tempValue.split(this.separator), null].map((v) => Object.assign(new FormFieldMetadataValueObject(), value, { value: v }));
if (values[0]) {
(this.get(0) as DsDynamicInputModel).valueUpdates.next(values[0]);
diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts
index b6238ca0c7..5c03d266d3 100644
--- a/src/app/shared/form/builder/parsers/field-parser.ts
+++ b/src/app/shared/form/builder/parsers/field-parser.ts
@@ -14,6 +14,7 @@ import { setLayout } from './parser.utils';
import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model';
import { ParserOptions } from './parser-options';
import { RelationshipOptions } from '../models/relationship-options.model';
+import { relationship } from '../../../../core/cache/builders/build-decorators';
export const SUBMISSION_ID: InjectionToken = new InjectionToken('submissionId');
export const CONFIG_DATA: InjectionToken = new InjectionToken('configData');
@@ -39,7 +40,6 @@ export abstract class FieldParser {
&& (this.configData.input.type !== 'list')
&& (this.configData.input.type !== 'tag')
&& (this.configData.input.type !== 'group')
- && isEmpty(this.configData.selectableRelationship)
) {
let arrayCounter = 0;
let fieldArrayCounter = 0;
@@ -48,10 +48,11 @@ export abstract class FieldParser {
id: uniqueId() + '_array',
label: this.configData.label,
initialCount: this.getInitArrayIndex(),
- notRepeatable: !this.configData.repeatable,
+ notRepeatable: !this.configData.repeatable || hasValue(this.configData.selectableRelationship),
required: isNotEmpty(this.configData.mandatory),
groupFactory: () => {
let model;
+ console.log(arrayCounter);
if ((arrayCounter === 0)) {
model = this.modelFactory();
arrayCounter++;
@@ -85,7 +86,7 @@ export abstract class FieldParser {
} else {
const model = this.modelFactory(this.getInitFieldValue());
- if (model.hasLanguages) {
+ if (model.hasLanguages || isNotEmpty(model.relationship)) {
setLayout(model, 'grid', 'control', 'col');
}
return model;
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 0029273b8f..061efed4d3 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -48,10 +48,7 @@ import { LogOutComponent } from './log-out/log-out.component';
import { FormComponent } from './form/form.component';
import { DsDynamicTypeaheadComponent } from './form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component';
import { DsDynamicScrollableDropdownComponent } from './form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component';
-import {
- DsDynamicFormControlContainerComponent,
- dsDynamicFormControlMapFn
-} from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
+import { DsDynamicFormControlContainerComponent, dsDynamicFormControlMapFn } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
import { DsDynamicFormComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form.component';
import { DYNAMIC_FORM_CONTROL_MAP_FN, DynamicFormsCoreModule } from '@ng-dynamic-forms/core';
import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap';
@@ -176,7 +173,6 @@ import { MetadataRepresentationListComponent } from '../+item-page/simple/metada
import { SelectableListItemControlComponent } from './object-collection/shared/selectable-list-item-control/selectable-list-item-control.component';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
-import { DsDynamicFormControlContainerWrapperComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container-wrapper/ds-dynamic-form-control-container-wrapper.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -438,8 +434,7 @@ const DIRECTIVES = [
...ENTRY_COMPONENTS,
...SHARED_ITEM_PAGE_COMPONENTS,
PublicationSearchResultListElementComponent,
- ExistingMetadataListElementComponent,
- DsDynamicFormControlContainerWrapperComponent
+ ExistingMetadataListElementComponent
],
providers: [
...PROVIDERS