diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html index 1b0386dee2..591d80bdd5 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html @@ -20,25 +20,33 @@ - - {{eperson.id}} + + {{epersonDTO.eperson.id}} - - {{ dsoNameService.getName(eperson) }} + + {{ dsoNameService.getName(epersonDTO.eperson) }} - {{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}
- {{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }} + {{messagePrefix + '.table.email' | translate}}: {{ epersonDTO.eperson.email ? epersonDTO.eperson.email : '-' }}
+ {{messagePrefix + '.table.netid' | translate}}: {{ epersonDTO.eperson.netid ? epersonDTO.eperson.netid : '-' }}
- +
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts index 9cc7727557..9a6b2b4f05 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts @@ -222,13 +222,13 @@ describe('MembersListComponent', () => { describe('if first delete button is pressed', () => { beforeEach(() => { + spyOn(component, 'search').and.callThrough(); const deleteButton: DebugElement = fixture.debugElement.query(By.css('#ePeopleMembersOfGroup tbody .fa-trash-alt')); deleteButton.nativeElement.click(); fixture.detectChanges(); }); - it('then no ePerson remains as a member of the active group.', () => { - const epersonsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tbody tr')); - expect(epersonsFound.length).toEqual(0); + it('should trigger the search to add the user back to the search table', () => { + expect(component.search).toHaveBeenCalled(); }); }); }); @@ -264,13 +264,13 @@ describe('MembersListComponent', () => { describe('if first add button is pressed', () => { beforeEach(() => { + spyOn(component, 'search').and.callThrough(); const addButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus')); addButton.nativeElement.click(); fixture.detectChanges(); }); - it('then all (two) ePersons are member of the active group. No non-members left', () => { - epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); - expect(epersonsFound.length).toEqual(0); + it('should trigger the search to remove the user from the search table', () => { + expect(component.search).toHaveBeenCalled(); }); }); }); diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index 4a707ef159..9b123ae447 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -24,21 +24,29 @@ import { } from '@ngx-translate/core'; import { BehaviorSubject, + combineLatest as observableCombineLatest, Observable, + ObservedValueOf, + of as observableOf, Subscription, } from 'rxjs'; import { + defaultIfEmpty, map, switchMap, take, } from 'rxjs/operators'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; -import { PaginatedList } from '../../../../core/data/paginated-list.model'; +import { + buildPaginatedList, + PaginatedList, +} from '../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { Group } from '../../../../core/eperson/models/group.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { @@ -137,7 +145,7 @@ export class MembersListComponent implements OnInit, OnDestroy { /** * List of EPeople members of currently active group being edited */ - ePeopleMembersOfGroup: BehaviorSubject> = new BehaviorSubject>(undefined); + ePeopleMembersOfGroup: BehaviorSubject> = new BehaviorSubject(undefined); /** * Pagination config used to display the list of EPeople that are result of EPeople search @@ -226,10 +234,35 @@ export class MembersListComponent implements OnInit, OnDestroy { return rd; } }), - getRemoteDataPayload()) - .subscribe((paginatedListOfEPersons: PaginatedList) => { - this.ePeopleMembersOfGroup.next(paginatedListOfEPersons); - })); + switchMap((epersonListRD: RemoteData>) => { + const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => { + const dto$: Observable = observableCombineLatest( + this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { + const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); + epersonDtoModel.eperson = member; + epersonDtoModel.ableToDelete = isMember; + return epersonDtoModel; + }); + return dto$; + })]); + return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => { + return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); + })); + }), + ).subscribe((paginatedListOfDTOs: PaginatedList) => { + this.ePeopleMembersOfGroup.next(paginatedListOfDTOs); + }), + ); + } + + /** + * We always return true since this is only used by the top section (which represents all the users part of the group + * in {@link MembersListComponent}) + * + * @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited + */ + isMemberOfGroup(possibleMember: EPerson): Observable { + return observableOf(true); } /** diff --git a/src/app/core/data/relationship-data.service.spec.ts b/src/app/core/data/relationship-data.service.spec.ts index 753d07b21e..1fa30ae7e4 100644 --- a/src/app/core/data/relationship-data.service.spec.ts +++ b/src/app/core/data/relationship-data.service.spec.ts @@ -128,6 +128,7 @@ describe('RelationshipDataService', () => { const itemService = jasmine.createSpyObj('itemService', { findById: (uuid) => createSuccessfulRemoteDataObject(relatedItems.find((relatedItem) => relatedItem.id === uuid)), findByHref: createSuccessfulRemoteDataObject$(relatedItems[0]), + getIDHrefObs: (uuid: string) => observableOf(`https://demo.dspace.org/server/api/core/items/${uuid}`), }); const getRequestEntry$ = (successful: boolean) => { @@ -244,6 +245,16 @@ describe('RelationshipDataService', () => { }); }); + describe('searchByItemsAndType', () => { + it('should call addDependency for each item to invalidate the request when one of the items is update', () => { + spyOn(service as any, 'addDependency'); + + service.searchByItemsAndType(relationshipType.id, item.id, relationshipType.leftwardType, ['item-id-1', 'item-id-2']); + + expect((service as any).addDependency).toHaveBeenCalledTimes(2); + }); + }); + describe('resolveMetadataRepresentation', () => { const parentItem: Item = Object.assign(new Item(), { id: 'parent-item', diff --git a/src/app/core/data/relationship-data.service.ts b/src/app/core/data/relationship-data.service.ts index 8514ab3e2a..0ab9a0a3a2 100644 --- a/src/app/core/data/relationship-data.service.ts +++ b/src/app/core/data/relationship-data.service.ts @@ -574,13 +574,18 @@ export class RelationshipDataService extends IdentifiableDataService>> = this.searchBy( 'byItemsAndType', { searchParams: searchParams, }, ) as Observable>>; + arrayOfItemIds.forEach((itemId: string) => { + this.addDependency(searchRD$, this.itemService.getIDHrefObs(encodeURIComponent(itemId))); + }); + + return searchRD$; } /** diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts index e0672e3207..0de6de7407 100644 --- a/src/app/core/eperson/eperson-data.service.ts +++ b/src/app/core/eperson/eperson-data.service.ts @@ -107,13 +107,17 @@ export class EPersonDataService extends IdentifiableDataService impleme * @param scope Scope of the EPeople search, default byMetadata * @param query Query of search * @param options Options of search request + * @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + * @param reRequestOnStale Whether or not the request should automatically be re- + * requested after the response becomes stale */ - public searchByScope(scope: string, query: string, options: FindListOptions = {}, useCachedVersionIfAvailable?: boolean): Observable>> { + public searchByScope(scope: string, query: string, options: FindListOptions = {}, useCachedVersionIfAvailable?: boolean, reRequestOnStale = true): Observable>> { switch (scope) { case 'metadata': - return this.getEpeopleByMetadata(query.trim(), options, useCachedVersionIfAvailable); + return this.getEpeopleByMetadata(query.trim(), options, useCachedVersionIfAvailable, reRequestOnStale); case 'email': - return this.getEPersonByEmail(query.trim()).pipe( + return this.getEPersonByEmail(query.trim(), useCachedVersionIfAvailable, reRequestOnStale).pipe( map((rd: RemoteData) => { if (rd.hasSucceeded) { // Turn the single EPerson or NoContent in to a PaginatedList @@ -145,7 +149,7 @@ export class EPersonDataService extends IdentifiableDataService impleme }), ); default: - return this.getEpeopleByMetadata(query.trim(), options, useCachedVersionIfAvailable); + return this.getEpeopleByMetadata(query.trim(), options, useCachedVersionIfAvailable, reRequestOnStale); } } diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.spec.ts index f969416783..4809a4f730 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.spec.ts @@ -185,6 +185,7 @@ describe('EditItemRelationshipsService', () => { expect(itemService.invalidateByHref).toHaveBeenCalledWith(currentItem.self); expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem1.self); + expect(itemService.invalidateByHref).toHaveBeenCalledWith(relationshipItem2.self); expect(notificationsService.success).toHaveBeenCalledTimes(1); }); @@ -265,6 +266,116 @@ describe('EditItemRelationshipsService', () => { }); }); + describe('isProvidedItemTypeLeftType', () => { + it('should return true if the provided item corresponds to the left type of the relationship', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'leftType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'rightType' }), + }); + const itemType = Object.assign(new ItemType(), { id: 'leftType' } ); + const item = Object.assign(new Item(), { uuid: 'item-uuid' }); + + const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item); + result.subscribe((resultValue) => { + expect(resultValue).toBeTrue(); + done(); + }); + }); + + it('should return false if the provided item corresponds to the right type of the relationship', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'leftType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'rightType' }), + }); + const itemType = Object.assign(new ItemType(), { id: 'rightType' } ); + const item = Object.assign(new Item(), { uuid: 'item-uuid' }); + + const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item); + result.subscribe((resultValue) => { + expect(resultValue).toBeFalse(); + done(); + }); + }); + + it('should return undefined if the provided item corresponds does not match any of the relationship types', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'leftType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'rightType' }), + }); + const itemType = Object.assign(new ItemType(), { id: 'something-else' } ); + const item = Object.assign(new Item(), { uuid: 'item-uuid' }); + + const result = service.isProvidedItemTypeLeftType(relationshipType, itemType, item); + result.subscribe((resultValue) => { + expect(resultValue).toBeUndefined(); + done(); + }); + }); + }); + + describe('relationshipMatchesBothSameTypes', () => { + it('should return true if both left and right type of the relationship type are the same and match the provided itemtype', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'sameType' }), + rightType: createSuccessfulRemoteDataObject$({ id:'sameType' }), + leftwardType: 'isDepartmentOfDivision', + rightwardType: 'isDivisionOfDepartment', + }); + const itemType = Object.assign(new ItemType(), { id: 'sameType' } ); + + const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType); + result.subscribe((resultValue) => { + expect(resultValue).toBeTrue(); + done(); + }); + }); + it('should return false if both left and right type of the relationship type are the same and match the provided itemtype but the leftwardType & rightwardType is identical', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'sameType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'sameType' }), + leftwardType: 'isOrgUnitOfOrgUnit', + rightwardType: 'isOrgUnitOfOrgUnit', + }); + const itemType = Object.assign(new ItemType(), { id: 'sameType' }); + + const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType); + result.subscribe((resultValue) => { + expect(resultValue).toBeFalse(); + done(); + }); + }); + it('should return false if both left and right type of the relationship type are the same and do not match the provided itemtype', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'sameType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'sameType' }), + leftwardType: 'isDepartmentOfDivision', + rightwardType: 'isDivisionOfDepartment', + }); + const itemType = Object.assign(new ItemType(), { id: 'something-else' } ); + + const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType); + result.subscribe((resultValue) => { + expect(resultValue).toBeFalse(); + done(); + }); + }); + it('should return false if both left and right type of the relationship type are different', (done) => { + const relationshipType = Object.assign(new RelationshipType(), { + leftType: createSuccessfulRemoteDataObject$({ id: 'leftType' }), + rightType: createSuccessfulRemoteDataObject$({ id: 'rightType' }), + leftwardType: 'isAuthorOfPublication', + rightwardType: 'isPublicationOfAuthor', + }); + const itemType = Object.assign(new ItemType(), { id: 'leftType' } ); + + const result = service.shouldDisplayBothRelationshipSides(relationshipType, itemType); + result.subscribe((resultValue) => { + expect(resultValue).toBeFalse(); + done(); + }); + }); + }); + describe('displayNotifications', () => { it('should show one success notification when multiple requests succeeded', () => { service.displayNotifications([ diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts b/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts index 14fa226bf4..e66562fe19 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-item-relationships.service.ts @@ -3,6 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, + combineLatest as observableCombineLatest, EMPTY, Observable, Subscription, @@ -28,8 +29,14 @@ import { ObjectUpdatesService } from '../../../core/data/object-updates/object-u import { RelationshipDataService } from '../../../core/data/relationship-data.service'; import { RemoteData } from '../../../core/data/remote-data'; import { Item } from '../../../core/shared/item.model'; +import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; +import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model'; import { NoContent } from '../../../core/shared/NoContent.model'; +import { + getFirstSucceededRemoteData, + getRemoteDataPayload, +} from '../../../core/shared/operators'; import { hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -70,7 +77,17 @@ export class EditItemRelationshipsService { // process each update one by one, while waiting for the previous to finish concatMap((update: FieldUpdate) => { if (update.changeType === FieldChangeType.REMOVE) { - return this.deleteRelationship(update.field as DeleteRelationship).pipe(take(1)); + return this.deleteRelationship(update.field as DeleteRelationship).pipe( + take(1), + switchMap((deleteRD: RemoteData) => { + if (deleteRD.hasSucceeded) { + return this.itemService.invalidateByHref((update.field as DeleteRelationship).relatedItem._links.self.href).pipe( + map(() => deleteRD), + ); + } + return [deleteRD]; + }), + ); } else if (update.changeType === FieldChangeType.ADD) { return this.addRelationship(update.field as RelationshipIdentifiable).pipe( take(1), @@ -181,6 +198,55 @@ export class EditItemRelationshipsService { } } + isProvidedItemTypeLeftType(relationshipType: RelationshipType, itemType: ItemType, item: Item): Observable { + return this.getRelationshipLeftAndRightType(relationshipType).pipe( + map(([leftType, rightType]: [ItemType, ItemType]) => { + if (leftType.id === itemType.id) { + return true; + } + + if (rightType.id === itemType.id) { + return false; + } + + // should never happen... + console.warn(`The item ${item.uuid} is not on the right or the left side of relationship type ${relationshipType.uuid}`); + return undefined; + }), + ); + } + + /** + * Whether both side of the relationship need to be displayed on the edit relationship page or not. + * + * @param relationshipType The relationship type + * @param itemType The item type + */ + shouldDisplayBothRelationshipSides(relationshipType: RelationshipType, itemType: ItemType): Observable { + return this.getRelationshipLeftAndRightType(relationshipType).pipe( + map(([leftType, rightType]: [ItemType, ItemType]) => { + return leftType.id === itemType.id && rightType.id === itemType.id && relationshipType.leftwardType !== relationshipType.rightwardType; + }), + ); + } + + protected getRelationshipLeftAndRightType(relationshipType: RelationshipType): Observable<[ItemType, ItemType]> { + const leftType$: Observable = relationshipType.leftType.pipe( + getFirstSucceededRemoteData(), + getRemoteDataPayload(), + ); + + const rightType$: Observable = relationshipType.rightType.pipe( + getFirstSucceededRemoteData(), + getRemoteDataPayload(), + ); + + return observableCombineLatest([ + leftType$, + rightType$, + ]); + } + /** @@ -197,6 +263,5 @@ export class EditItemRelationshipsService { */ getNotificationContent(key: string): string { return this.translateService.instant(this.notificationsPrefix + key + '.content'); - } } diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.html b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.html new file mode 100644 index 0000000000..ed7fb190f2 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.html @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.scss b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.spec.ts new file mode 100644 index 0000000000..077f609633 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.spec.ts @@ -0,0 +1,122 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { + ComponentFixture, + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { cold } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ItemType } from '../../../../core/shared/item-relationships/item-type.model'; +import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { EditItemRelationshipsService } from '../edit-item-relationships.service'; +import { EditRelationshipListComponent } from '../edit-relationship-list/edit-relationship-list.component'; +import { EditRelationshipListWrapperComponent } from './edit-relationship-list-wrapper.component'; + +describe('EditRelationshipListWrapperComponent', () => { + let editItemRelationshipsService: EditItemRelationshipsService; + let comp: EditRelationshipListWrapperComponent; + let fixture: ComponentFixture; + + const leftType = Object.assign(new ItemType(), { id: 'leftType', label: 'leftTypeString' }); + const rightType = Object.assign(new ItemType(), { id: 'rightType', label: 'rightTypeString' }); + + const relationshipType = Object.assign(new RelationshipType(), { + id: '1', + leftMaxCardinality: null, + leftMinCardinality: 0, + leftType: createSuccessfulRemoteDataObject$(leftType), + leftwardType: 'isOrgUnitOfOrgUnit', + rightMaxCardinality: null, + rightMinCardinality: 0, + rightType: createSuccessfulRemoteDataObject$(rightType), + rightwardType: 'isOrgUnitOfOrgUnit', + uuid: 'relationshiptype-1', + }); + + const item = Object.assign(new Item(), { uuid: 'item-uuid' }); + + beforeEach(waitForAsync(() => { + + editItemRelationshipsService = jasmine.createSpyObj('editItemRelationshipsService', { + isProvidedItemTypeLeftType: observableOf(true), + shouldDisplayBothRelationshipSides: observableOf(false), + }); + + + TestBed.configureTestingModule({ + imports: [ + EditRelationshipListWrapperComponent, + ], + providers: [ + { provide: EditItemRelationshipsService, useValue: editItemRelationshipsService }, + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + ], + }).overrideComponent(EditRelationshipListWrapperComponent, { + remove: { + imports: [ + EditRelationshipListComponent, + ], + }, + }).compileComponents(); + + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditRelationshipListWrapperComponent); + comp = fixture.componentInstance; + comp.relationshipType = relationshipType; + comp.itemType = leftType; + comp.item = item; + + fixture.detectChanges(); + }); + + describe('onInit', () => { + it('should render the component', () => { + expect(comp).toBeTruthy(); + }); + it('should set currentItemIsLeftItem$ and bothItemsMatchType$ based on the provided relationshipType, itemType and item', () => { + expect(editItemRelationshipsService.isProvidedItemTypeLeftType).toHaveBeenCalledWith(relationshipType, leftType, item); + expect(editItemRelationshipsService.shouldDisplayBothRelationshipSides).toHaveBeenCalledWith(relationshipType, leftType); + + expect(comp.currentItemIsLeftItem$.getValue()).toEqual(true); + expect(comp.shouldDisplayBothRelationshipSides$).toBeObservable(cold('(a|)', { a: false })); + }); + }); + + describe('when the current item is left', () => { + it('should render one relationship list section', () => { + const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list')); + expect(relationshipLists.length).toEqual(1); + }); + }); + + describe('when the current item is right', () => { + it('should render one relationship list section', () => { + (editItemRelationshipsService.isProvidedItemTypeLeftType as jasmine.Spy).and.returnValue(observableOf(false)); + comp.ngOnInit(); + fixture.detectChanges(); + + const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list')); + expect(relationshipLists.length).toEqual(1); + }); + }); + + describe('when the current item is both left and right', () => { + it('should render two relationship list sections', () => { + (editItemRelationshipsService.shouldDisplayBothRelationshipSides as jasmine.Spy).and.returnValue(observableOf(true)); + comp.ngOnInit(); + fixture.detectChanges(); + + const relationshipLists = fixture.debugElement.queryAll(By.css('ds-edit-relationship-list')); + expect(relationshipLists.length).toEqual(2); + }); + }); + +}); diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.ts new file mode 100644 index 0000000000..943a147546 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list-wrapper/edit-relationship-list-wrapper.component.ts @@ -0,0 +1,111 @@ +import { + AsyncPipe, + NgIf, +} from '@angular/common'; +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, +} from '@angular/core'; +import { + BehaviorSubject, + Observable, + Subscription, +} from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ItemType } from '../../../../core/shared/item-relationships/item-type.model'; +import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; +import { hasValue } from '../../../../shared/empty.util'; +import { EditItemRelationshipsService } from '../edit-item-relationships.service'; +import { EditRelationshipListComponent } from '../edit-relationship-list/edit-relationship-list.component'; + +@Component({ + selector: 'ds-edit-relationship-list-wrapper', + styleUrls: ['./edit-relationship-list-wrapper.component.scss'], + templateUrl: './edit-relationship-list-wrapper.component.html', + standalone: true, + imports: [ + AsyncPipe, + EditRelationshipListComponent, + NgIf, + ], +}) +/** + * A component creating a list of editable relationships of a certain type + * The relationships are rendered as a list of related items + */ +export class EditRelationshipListWrapperComponent implements OnInit, OnDestroy { + + /** + * The item to display related items for + */ + @Input() item: Item; + + @Input() itemType: ItemType; + + /** + * The URL to the current page + * Used to fetch updates for the current item from the store + */ + @Input() url: string; + + /** + * The label of the relationship-type we're rendering a list for + */ + @Input() relationshipType: RelationshipType; + + /** + * If updated information has changed + */ + @Input() hasChanges!: Observable; + + /** + * The event emmiter to submit the new information + */ + @Output() submitModal: EventEmitter = new EventEmitter(); + + /** + * Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType}, + * false if it is on the right-hand side and undefined in the rare case that it is on neither side. + */ + currentItemIsLeftItem$: BehaviorSubject = new BehaviorSubject(undefined); + + + isLeftItem$ = new BehaviorSubject(true); + + isRightItem$ = new BehaviorSubject(false); + + shouldDisplayBothRelationshipSides$: Observable; + + /** + * Array to track all subscriptions and unsubscribe them onDestroy + * @type {Array} + */ + private subs: Subscription[] = []; + + constructor( + protected editItemRelationshipsService: EditItemRelationshipsService, + ) { + } + + + ngOnInit(): void { + this.subs.push(this.editItemRelationshipsService.isProvidedItemTypeLeftType(this.relationshipType, this.itemType, this.item) + .subscribe((nextValue: boolean) => { + this.currentItemIsLeftItem$.next(nextValue); + })); + + this.shouldDisplayBothRelationshipSides$ = this.editItemRelationshipsService.shouldDisplayBothRelationshipSides(this.relationshipType, this.itemType); + } + + + ngOnDestroy(): void { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } +} diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 42296a1d71..48415000be 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -15,7 +15,10 @@ import { import { provideMockStore } from '@ngrx/store/testing'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { of as observableOf } from 'rxjs'; +import { + BehaviorSubject, + of as observableOf, +} from 'rxjs'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; import { environment } from '../../../../../environments/environment.test'; @@ -82,6 +85,7 @@ describe('EditRelationshipListComponent', () => { let relationships: Relationship[]; let relationshipType: RelationshipType; let paginationOptions: PaginationComponentOptions; + let currentItemIsLeftItem$ = new BehaviorSubject(true); const resetComponent = () => { fixture = TestBed.createComponent(EditRelationshipListComponent); @@ -92,6 +96,7 @@ describe('EditRelationshipListComponent', () => { comp.url = url; comp.relationshipType = relationshipType; comp.hasChanges = observableOf(false); + comp.currentItemIsLeftItem$ = currentItemIsLeftItem$; fixture.detectChanges(); }; @@ -193,6 +198,9 @@ describe('EditRelationshipListComponent', () => { [relationships[0].uuid]: fieldUpdate1, [relationships[1].uuid]: fieldUpdate2, }), + // eslint-disable-next-line @typescript-eslint/no-empty-function + initialize: () => { + }, }, ); @@ -325,6 +333,7 @@ describe('EditRelationshipListComponent', () => { leftwardType: 'isAuthorOfPublication', rightwardType: 'isPublicationOfAuthor', }); + currentItemIsLeftItem$ = new BehaviorSubject(true); relationshipService.getItemRelationshipsByLabel.calls.reset(); resetComponent(); }); @@ -349,6 +358,7 @@ describe('EditRelationshipListComponent', () => { leftwardType: 'isPublicationOfAuthor', rightwardType: 'isAuthorOfPublication', }); + currentItemIsLeftItem$ = new BehaviorSubject(false); relationshipService.getItemRelationshipsByLabel.calls.reset(); resetComponent(); }); diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index e396c5ee92..656d608935 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -43,6 +43,7 @@ import { AppConfig, } from '../../../../../config/app-config.interface'; import { LinkService } from '../../../../core/cache/builders/link.service'; +import { RequestParam } from '../../../../core/cache/models/request-param.model'; import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; import { FieldUpdates } from '../../../../core/data/object-updates/field-updates.model'; @@ -59,6 +60,7 @@ import { Relationship } from '../../../../core/shared/item-relationships/relatio import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { getAllSucceededRemoteData, + getFirstCompletedRemoteData, getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload, @@ -67,6 +69,7 @@ import { hasNoValue, hasValue, hasValueOperator, + isNotEmpty, } from '../../../../shared/empty.util'; import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component'; import { RelationshipOptions } from '../../../../shared/form/builder/models/relationship-options.model'; @@ -143,7 +146,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { * Observable that emits true if {@link itemType} is on the left-hand side of {@link relationshipType}, * false if it is on the right-hand side and undefined in the rare case that it is on neither side. */ - private currentItemIsLeftItem$: BehaviorSubject = new BehaviorSubject(undefined); + @Input() currentItemIsLeftItem$: BehaviorSubject = new BehaviorSubject(undefined); relatedEntityType$: Observable; @@ -243,17 +246,14 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { * Get the relevant label for this relationship type */ private getLabel(): Observable { - return observableCombineLatest([ - this.relationshipType.leftType, - this.relationshipType.rightType, - ].map((itemTypeRD) => itemTypeRD.pipe( - getFirstSucceededRemoteData(), - getRemoteDataPayload(), - ))).pipe( - map((itemTypes: ItemType[]) => [ - this.relationshipType.leftwardType, - this.relationshipType.rightwardType, - ][itemTypes.findIndex((itemType) => itemType.id === this.itemType.id)]), + return this.currentItemIsLeftItem$.pipe( + map((currentItemIsLeftItem) => { + if (currentItemIsLeftItem) { + return this.relationshipType.leftwardType; + } else { + return this.relationshipType.rightwardType; + } + }), ); } @@ -281,6 +281,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { modalComp.toAdd = []; modalComp.toRemove = []; modalComp.isPending = false; + modalComp.hiddenQuery = '-search.resourceid:' + this.item.uuid; this.item.owningCollection.pipe( getFirstSucceededRemoteDataPayload(), @@ -309,7 +310,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { } } - this.loading$.next(true); + this.loading$.next(isNotEmpty(modalComp.toAdd) || isNotEmpty(modalComp.toRemove)); // emit the last page again to trigger a fieldupdates refresh this.relationshipsRd$.next(this.relationshipsRd$.getValue()); }); @@ -327,6 +328,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { } else { modalComp.toRemove.push(searchResult); } + this.loading$.next(isNotEmpty(modalComp.toAdd) || isNotEmpty(modalComp.toRemove)); }); }; @@ -366,6 +368,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { type: this.relationshipType, originalIsLeft: isLeft, originalItem: this.item, + relatedItem, relationship, } as RelationshipIdentifiable; return this.objectUpdatesService.saveRemoveFieldUpdate(this.url,update); @@ -399,6 +402,11 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { modalComp.toAdd = []; modalComp.toRemove = []; + this.loading$.next(false); + }; + + modalComp.closeEv = () => { + this.loading$.next(false); }; this.relatedEntityType$ @@ -454,24 +462,6 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { this.relationshipMessageKey$ = this.getRelationshipMessageKey(); - this.subs.push(this.relationshipLeftAndRightType$.pipe( - map(([leftType, rightType]: [ItemType, ItemType]) => { - if (leftType.id === this.itemType.id) { - return true; - } - - if (rightType.id === this.itemType.id) { - return false; - } - - // should never happen... - console.warn(`The item ${this.item.uuid} is not on the right or the left side of relationship type ${this.relationshipType.uuid}`); - return undefined; - }), - ).subscribe((nextValue: boolean) => { - this.currentItemIsLeftItem$.next(nextValue); - })); - // initialize the pagination options this.paginationConfig = new PaginationComponentOptions(); @@ -494,23 +484,32 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { observableCombineLatest([ currentPagination$, this.currentItemIsLeftItem$, + this.relatedEntityType$, ]).pipe( - switchMap(([currentPagination, currentItemIsLeftItem]: [PaginationComponentOptions, boolean]) => { - // get the relationships for the current item, relationshiptype and page + switchMap(([currentPagination, currentItemIsLeftItem, relatedEntityType]: [PaginationComponentOptions, boolean, ItemType]) => { + // get the relationships for the current page, item, relationship type and related entity type return this.relationshipService.getItemRelationshipsByLabel( this.item, currentItemIsLeftItem ? this.relationshipType.leftwardType : this.relationshipType.rightwardType, { elementsPerPage: currentPagination.pageSize, currentPage: currentPagination.currentPage, + searchParams: [ + new RequestParam('relatedEntityType', relatedEntityType.label), + ], }, true, true, ...linksToFollow, ); }), - ).subscribe((rd: RemoteData>) => { - this.relationshipsRd$.next(rd); + tap((rd: RemoteData>) => { + this.relationshipsRd$.next(rd); + }), + getAllSucceededRemoteData(), + getRemoteDataPayload(), + ).subscribe((relationshipPaginatedList: PaginatedList) => { + this.objectUpdatesService.initialize(this.url, relationshipPaginatedList.page, new Date()); }), ); @@ -533,10 +532,24 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { this.relationshipService.isLeftItem(relationship, this.item).pipe( // emit an array containing both the relationship and whether it's the left item, // as we'll need both - map((isLeftItem: boolean) => [relationship, isLeftItem]), + switchMap((isLeftItem: boolean) => { + if (isLeftItem) { + return relationship.rightItem.pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + map((relatedItem: Item) => [relationship, isLeftItem, relatedItem]), + ); + } else { + return relationship.leftItem.pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + map((relatedItem: Item) => [relationship, isLeftItem, relatedItem]), + ); + } + }), ), ), - map(([relationship, isLeftItem]: [Relationship, boolean]) => { + map(([relationship, isLeftItem, relatedItem]: [Relationship, boolean, Item]) => { // turn it into a RelationshipIdentifiable, an const nameVariant = isLeftItem ? relationship.rightwardValue : relationship.leftwardValue; @@ -546,6 +559,7 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { relationship, originalIsLeft: isLeftItem, originalItem: this.item, + relatedItem: relatedItem, nameVariant, } as RelationshipIdentifiable; }), diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts index 97cfb7a16b..f79d0ee0d1 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts @@ -131,9 +131,7 @@ export class EditRelationshipComponent implements OnChanges { this.leftItem$, this.rightItem$, ]).pipe( - map((items: Item[]) => - items.find((item) => item.uuid !== this.editItem.uuid), - ), + map(([leftItem, rightItem]: [Item, Item]) => leftItem.uuid === this.editItem.uuid ? rightItem : leftItem), take(1), ).subscribe((relatedItem) => { this.relatedItem$.next(relatedItem); diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html index 53eb412a8f..4c8ea49f99 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html @@ -5,13 +5,13 @@
- + >
diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts index 051c4db136..7b0cc46647 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts @@ -50,6 +50,7 @@ import { compareArraysUsingIds } from '../../simple/item-types/shared/item-relat import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component'; import { EditItemRelationshipsService } from './edit-item-relationships.service'; import { EditRelationshipListComponent } from './edit-relationship-list/edit-relationship-list.component'; +import { EditRelationshipListWrapperComponent } from './edit-relationship-list-wrapper/edit-relationship-list-wrapper.component'; @Component({ selector: 'ds-item-relationships', @@ -65,6 +66,7 @@ import { EditRelationshipListComponent } from './edit-relationship-list/edit-rel ThemedLoadingComponent, TranslateModule, VarDirective, + EditRelationshipListWrapperComponent, ], standalone: true, }) diff --git a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.spec.ts b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.spec.ts index 45759aa90b..a722d07c9d 100644 --- a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.spec.ts +++ b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.spec.ts @@ -37,6 +37,7 @@ describe('ModifyItemOverviewComponent', () => { fixture = TestBed.createComponent(ModifyItemOverviewComponent); comp = fixture.componentInstance; comp.item = mockItem; + comp.ngOnChanges(); fixture.detectChanges(); }); diff --git a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.ts b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.ts index 2d4b622ea9..3bd5024837 100644 --- a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.ts +++ b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.ts @@ -5,7 +5,7 @@ import { import { Component, Input, - OnInit, + OnChanges, } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; @@ -21,12 +21,12 @@ import { MetadataMap } from '../../../core/shared/metadata.models'; /** * Component responsible for rendering a table containing the metadatavalues from the to be edited item */ -export class ModifyItemOverviewComponent implements OnInit { +export class ModifyItemOverviewComponent implements OnChanges { @Input() item: Item; metadata: MetadataMap; - ngOnInit(): void { - this.metadata = this.item.metadata; + ngOnChanges(): void { + this.metadata = this.item?.metadata; } } diff --git a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts index 39faae5ca4..58521569f1 100644 --- a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts @@ -9,7 +9,7 @@ import { map } from 'rxjs/operators'; import { BrowseDefinitionDataService } from '../../../../core/browse/browse-definition-data.service'; import { BrowseDefinition } from '../../../../core/shared/browse-definition.model'; import { Item } from '../../../../core/shared/item.model'; -import { getRemoteDataPayload } from '../../../../core/shared/operators'; +import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component'; import { ImageField } from './image-field'; @@ -75,8 +75,8 @@ export class ItemPageFieldComponent { */ get browseDefinition(): Observable { return this.browseDefinitionDataService.findByFields(this.fields).pipe( - getRemoteDataPayload(), - map((def) => def), + getFirstCompletedRemoteData(), + map((def) => def.payload), ); } } diff --git a/src/app/item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/item-page/simple/item-types/shared/item-relationships-utils.ts index 112884303b..6b9f100df4 100644 --- a/src/app/item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/item-page/simple/item-types/shared/item-relationships-utils.ts @@ -2,6 +2,7 @@ import { InjectionToken } from '@angular/core'; import { combineLatest as observableCombineLatest, Observable, + of as observableOf, zip as observableZip, } from 'rxjs'; import { @@ -53,17 +54,19 @@ export const compareArraysUsingIds = () => /** * Operator for turning a list of relationships into a list of the relevant items * @param {string} thisId The item's id of which the relations belong to - * @returns {(source: Observable) => Observable} */ -export const relationsToItems = (thisId: string) => +export const relationsToItems = (thisId: string): (source: Observable) => Observable => (source: Observable): Observable => source.pipe( - mergeMap((rels: Relationship[]) => - observableZip( - ...rels.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem)), - ), - ), - map((arr) => + mergeMap((relationships: Relationship[]) => { + if (relationships.length === 0) { + return observableOf([]); + } + return observableZip( + ...relationships.map((rel: Relationship) => observableCombineLatest([rel.leftItem, rel.rightItem])), + ); + }), + map((arr: [RemoteData, RemoteData][]) => arr .filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded) .map(([leftItem, rightItem]) => { diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts index 39d393a6e0..d8e5cb0e53 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts @@ -22,7 +22,7 @@ import { Item } from '../../../core/shared/item.model'; import { MetadataValue } from '../../../core/shared/metadata.models'; import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; -import { getRemoteDataPayload } from '../../../core/shared/operators'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { MetadataFieldWrapperComponent } from '../../../shared/metadata-field-wrapper/metadata-field-wrapper.component'; import { MetadataRepresentationLoaderComponent } from '../../../shared/metadata-representation/metadata-representation-loader.component'; @@ -112,8 +112,8 @@ export class MetadataRepresentationListComponent extends AbstractIncrementalList searchKeyArray = searchKeyArray.concat(BrowseService.toSearchKeyArray(field)); }); return this.browseDefinitionDataService.findByFields(this.metadataFields).pipe( - getRemoteDataPayload(), - map((def) => Object.assign(new MetadatumRepresentation(this.itemType, def), metadatum)), + getFirstCompletedRemoteData(), + map((def) => Object.assign(new MetadatumRepresentation(this.itemType, def.payload), metadatum)), ); } }), diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html index 112175d806..cef8516b84 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html @@ -19,6 +19,7 @@ [repeatable]="repeatable" [context]="context" [query]="query" + [hiddenQuery]="hiddenQuery" [relationshipType]="relationshipType" [isLeft]="isLeft" [item]="item" @@ -85,7 +86,7 @@