mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
name variants combobox
This commit is contained in:
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||
import { DeleteRequest, FindAllOptions, PostRequest, RestRequest } from './request.models';
|
||||
@@ -78,7 +78,7 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
);
|
||||
}
|
||||
|
||||
addRelationship(typeId: string, item1: Item, item2: Item): Observable<RestResponse> {
|
||||
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RestResponse> {
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
@@ -87,6 +87,8 @@ export class RelationshipService extends DataService<Relationship> {
|
||||
isNotEmptyOperator(),
|
||||
take(1),
|
||||
map((endpointUrl: string) => `${endpointUrl}?relationshipType=${typeId}`),
|
||||
map((endpointUrl: string) => isNotEmpty(leftwardValue) ? `${endpointUrl}&leftwardValue=${leftwardValue}` : endpointUrl),
|
||||
map((endpointUrl: string) => isNotEmpty(rightwardValue) ? `${endpointUrl}&rightwardValue=${rightwardValue}` : endpointUrl),
|
||||
map((endpointURL: string) => new PostRequest(this.requestService.generateRequestId(), endpointURL, `${item1.self} \n ${item2.self}`, options)),
|
||||
configureRequest(this.requestService),
|
||||
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
|
||||
|
@@ -21,6 +21,7 @@ import { ProjectSearchResultGridElementComponent } from './item-grid-elements/se
|
||||
import { PersonItemMetadataListElementComponent } from './metadata-representations/person/person-item-metadata-list-element.component';
|
||||
import { OrgUnitItemMetadataListElementComponent } from './metadata-representations/org-unit/org-unit-item-metadata-list-element.component';
|
||||
import { PersonSearchResultListSubmissionElementComponent } from './submission/item-list-elements/person/person-search-result-list-submission-element.component';
|
||||
import { PersonInputSuggestionsComponent } from './submission/item-list-elements/person/person-suggestions/person-input-suggestions.component';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
OrgUnitComponent,
|
||||
@@ -40,7 +41,8 @@ const ENTRY_COMPONENTS = [
|
||||
PersonSearchResultGridElementComponent,
|
||||
OrgUnitSearchResultGridElementComponent,
|
||||
ProjectSearchResultGridElementComponent,
|
||||
PersonSearchResultListSubmissionElementComponent
|
||||
PersonSearchResultListSubmissionElementComponent,
|
||||
PersonInputSuggestionsComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<select>
|
||||
<option [value]="firstMetadataValue('person.name')" [innerHTML]="firstMetadataValue('person.name')"></option>
|
||||
<option *ngFor="let value of allMetadataValues('title.alternative')" [value]="value" [innerHTML]="value"></option>
|
||||
</select>
|
||||
<ds-person-input-suggestions [suggestions]="suggestions" (typeSuggestion)="filter($event)"></ds-person-input-suggestions>
|
||||
<!-- <select>-->
|
||||
<!-- <option [value]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')" [innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"></option>-->
|
||||
<!-- <option *ngFor="let value of allMetadataValues('dc.title.alternative')" [value]="value" [innerHTML]="value"></option>-->
|
||||
<!-- </select>-->
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
<span *ngIf="dso.allMetadata(['person.jobTitle']).length > 0"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
@@ -15,5 +15,19 @@ import { Context } from '../../../../../core/shared/context.model';
|
||||
/**
|
||||
* The component for displaying a list element for an item search result of the type Person
|
||||
*/
|
||||
export class PersonSearchResultListSubmissionElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
|
||||
export class PersonSearchResultListSubmissionElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> implements OnInit {
|
||||
suggestions: string[];
|
||||
allSuggestions: string[];
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
const defaultValue = this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName')
|
||||
const alternatives = this.allMetadataValues('dc.title.alternative');
|
||||
this.allSuggestions = [defaultValue, ...alternatives];
|
||||
this.suggestions = this.allSuggestions;
|
||||
}
|
||||
|
||||
filter(query) {
|
||||
this.suggestions = this.allSuggestions.filter((suggestion) => suggestion.includes(query));
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
<form #form="ngForm" (ngSubmit)="onSubmit(value)"
|
||||
[action]="action" (keydown)="onKeydown($event)"
|
||||
(keydown.arrowdown)="shiftFocusDown($event)"
|
||||
(keydown.arrowup)="shiftFocusUp($event)" (keydown.esc)="close()"
|
||||
(dsClickOutside)="close();">
|
||||
<input #inputField type="text" [(ngModel)]="value" [name]="name"
|
||||
class="form-control suggestion_input"
|
||||
(focus)="open()"
|
||||
(click)="open()"
|
||||
[ngClass]="{'is-invalid': !valid}"
|
||||
[dsDebounce]="debounceTime" (onDebounce)="find($event)"
|
||||
[placeholder]="placeholder"
|
||||
[ngModelOptions]="{standalone: true}" autocomplete="off"/>
|
||||
<input type="submit" class="d-none"/>
|
||||
<div class="autocomplete dropdown-menu" [ngClass]="{'show': (show | async) && isNotEmpty(suggestions)}">
|
||||
<div class="dropdown-list">
|
||||
<div *ngFor="let suggestionOption of suggestions">
|
||||
<a href="#" class="d-block dropdown-item" (click)="onClickSuggestion(suggestionOption)" #suggestion>
|
||||
<span [innerHTML]="suggestionOption"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@@ -0,0 +1,50 @@
|
||||
import { Component, forwardRef, Input, OnInit } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { InputSuggestion } from '../../../../../../shared/input-suggestions/input-suggestions.model';
|
||||
import { InputSuggestionsComponent } from '../../../../../../shared/input-suggestions/input-suggestions.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-person-input-suggestions',
|
||||
styleUrls: ['./../../../../../../shared/input-suggestions/input-suggestions.component.scss'],
|
||||
templateUrl: './person-input-suggestions.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
// Usage of forwardRef necessary https://github.com/angular/angular.io/issues/1151
|
||||
// tslint:disable-next-line:no-forward-ref
|
||||
useExisting: forwardRef(() => PersonInputSuggestionsComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* Component representing a form with a autocomplete functionality
|
||||
*/
|
||||
export class PersonInputSuggestionsComponent extends InputSuggestionsComponent implements OnInit {
|
||||
/**
|
||||
* The suggestions that should be shown
|
||||
*/
|
||||
@Input() suggestions: string[] = [];
|
||||
|
||||
ngOnInit() {
|
||||
if (this.suggestions.length > 0) {
|
||||
this.value = this.suggestions[0]
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(data) {
|
||||
this.value = data;
|
||||
this.submitSuggestion.emit(data);
|
||||
}
|
||||
|
||||
onClickSuggestion(data) {
|
||||
this.value = data;
|
||||
this.clickSuggestion.emit(data);
|
||||
this.close();
|
||||
this.blockReopen = true;
|
||||
this.queryInput.nativeElement.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -14,6 +14,7 @@
|
||||
[listId]="listId"
|
||||
[relationship]="relationship"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
@@ -27,6 +28,7 @@
|
||||
[listId]="listId"
|
||||
[label]="label"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
@@ -34,7 +36,6 @@
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<small>{{ ('submission.sections.describe.relationship-lookup.selected' | translate:{size: (selection$ | async)?.length || 0}) }}</small>
|
||||
|
@@ -18,6 +18,7 @@ import { RelationshipService } from '../../../../../core/data/relationship.servi
|
||||
import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '../../../../../app.reducer';
|
||||
import { Context } from '../../../../../core/shared/context.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dynamic-lookup-relation-modal',
|
||||
@@ -38,6 +39,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
|
||||
itemRD$;
|
||||
repeatable: boolean;
|
||||
selection$: Observable<ListableObject[]>;
|
||||
context: Context;
|
||||
|
||||
constructor(
|
||||
public modal: NgbActiveModal,
|
||||
@@ -51,8 +53,10 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.selection$ = this.selectableListService.getSelectableList(this.listId).pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []));
|
||||
if (this.relationship.nameVariants) {
|
||||
this.context = Context.Submission;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
close() {
|
||||
this.modal.close();
|
||||
@@ -67,7 +71,8 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
|
||||
return selectableObjects.forEach((object) =>
|
||||
this.store.dispatch(new AddRelationshipAction(itemRD.payload, object.indexableObject, this.relationship.relationshipType))
|
||||
);
|
||||
})
|
||||
}
|
||||
)
|
||||
).subscribe());
|
||||
}
|
||||
|
||||
@@ -80,9 +85,9 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
|
||||
return selectableObjects.forEach((object) =>
|
||||
this.store.dispatch(new RemoveRelationshipAction(itemRD.payload, object.indexableObject, this.relationship.relationshipType))
|
||||
);
|
||||
})
|
||||
}
|
||||
)
|
||||
).subscribe()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -17,12 +17,12 @@ export const RelationshipActionTypes = {
|
||||
*/
|
||||
export class AddRelationshipAction implements Action {
|
||||
type = RelationshipActionTypes.ADD_RELATIONSHIP;
|
||||
exists = true;
|
||||
|
||||
payload: {
|
||||
item1: Item;
|
||||
item2: Item;
|
||||
relationshipType: string;
|
||||
nameVariant: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -31,13 +31,15 @@ export class AddRelationshipAction implements Action {
|
||||
* @param item1 The first item in the relationship
|
||||
* @param item2 The second item in the relationship
|
||||
* @param relationshipType The label of the relationshipType
|
||||
* @param nameVariant The nameVariant of the relationshipType
|
||||
*/
|
||||
constructor(
|
||||
item1: Item,
|
||||
item2: Item,
|
||||
relationshipType: string
|
||||
relationshipType: string,
|
||||
nameVariant?: string
|
||||
) {
|
||||
this.payload = { item1, item2, relationshipType };
|
||||
this.payload = { item1, item2, relationshipType, nameVariant };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +48,6 @@ export class AddRelationshipAction implements Action {
|
||||
*/
|
||||
export class RemoveRelationshipAction implements Action {
|
||||
type = RelationshipActionTypes.REMOVE_RELATIONSHIP;
|
||||
exists = false;
|
||||
|
||||
payload: {
|
||||
item1: Item;
|
||||
|
@@ -3,7 +3,7 @@ import { Actions, Effect, ofType } from '@ngrx/effects';
|
||||
import { debounceTime, map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { RelationshipService } from '../../../../../core/data/relationship.service';
|
||||
import { RelationshipAction, RelationshipActionTypes } from './relationship.actions';
|
||||
import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes } from './relationship.actions';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { hasNoValue, hasValueOperator } from '../../../../empty.util';
|
||||
import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
|
||||
@@ -21,11 +21,11 @@ export class RelationshipEffects {
|
||||
* Map that keeps track of the latest RelationshipEffects for each relationship's composed identifier
|
||||
*/
|
||||
private debounceMap: {
|
||||
[identifier: string]: BehaviorSubject<boolean>
|
||||
[identifier: string]: BehaviorSubject<string>
|
||||
} = {};
|
||||
|
||||
private initialStateMap: {
|
||||
[identifier: string]: boolean
|
||||
private initialActionMap: {
|
||||
[identifier: string]: string
|
||||
} = {};
|
||||
|
||||
|
||||
@@ -39,22 +39,22 @@ export class RelationshipEffects {
|
||||
const { item1, item2, relationshipType } = action.payload;
|
||||
const identifier: string = this.createIdentifier(item1, item2, relationshipType);
|
||||
if (hasNoValue(this.debounceMap[identifier])) {
|
||||
this.initialStateMap[identifier] = !action.exists;
|
||||
this.debounceMap[identifier] = new BehaviorSubject<boolean>(action.exists);
|
||||
this.initialActionMap[identifier] = action.type;
|
||||
this.debounceMap[identifier] = new BehaviorSubject<string>(action.type);
|
||||
this.debounceMap[identifier].pipe(
|
||||
debounceTime(DEBOUNCE_TIME),
|
||||
take(1)
|
||||
).subscribe(
|
||||
(exists) => {
|
||||
if (this.initialStateMap[identifier] !== exists) {
|
||||
exists ? this.addRelationship(item1, item2, relationshipType) : this.removeRelationship(item1, item2, relationshipType);
|
||||
(type) => {
|
||||
if (this.initialActionMap[identifier] === type) {
|
||||
type === RelationshipActionTypes.ADD_RELATIONSHIP ? this.addRelationship(item1, item2, relationshipType, (action as AddRelationshipAction).payload.nameVariant) : this.removeRelationship(item1, item2, relationshipType);
|
||||
}
|
||||
delete this.debounceMap[identifier];
|
||||
delete this.initialStateMap[identifier];
|
||||
delete this.initialActionMap[identifier];
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.debounceMap[identifier].next(action.exists);
|
||||
this.debounceMap[identifier].next(action.type);
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -72,7 +72,7 @@ export class RelationshipEffects {
|
||||
}
|
||||
|
||||
|
||||
private addRelationship(item1: Item, item2: Item, relationshipType: string) {
|
||||
private addRelationship(item1: Item, item2: Item, relationshipType: string, nameVariant?: string) {
|
||||
const type1: string = item1.firstMetadataValue('relationship.type');
|
||||
// const type1: string = 'JournalVolume';
|
||||
const type2: string = item2.firstMetadataValue('relationship.type');
|
||||
@@ -81,9 +81,9 @@ export class RelationshipEffects {
|
||||
mergeMap((type: RelationshipType) => {
|
||||
const isSwitched = type.rightwardType === relationshipType;
|
||||
if (isSwitched) {
|
||||
return this.relationshipService.addRelationship(type.id, item2, item1);
|
||||
return this.relationshipService.addRelationship(type.id, item2, item1, undefined, nameVariant);
|
||||
} else {
|
||||
return this.relationshipService.addRelationship(type.id, item1, item2);
|
||||
return this.relationshipService.addRelationship(type.id, item1, item2, nameVariant, undefined);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@@ -39,6 +39,8 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
|
||||
@Input() listId: string;
|
||||
@Input() repeatable: boolean;
|
||||
@Input() selection$: Observable<ListableObject[]>;
|
||||
@Input() context: Context;
|
||||
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
resultsRD$: Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
|
||||
@@ -52,7 +54,6 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
|
||||
pageSize: 5
|
||||
});
|
||||
linkTypes = CollectionElementLinkType;
|
||||
context: Context;
|
||||
|
||||
constructor(
|
||||
private searchService: SearchService,
|
||||
@@ -67,13 +68,6 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
|
||||
this.resetRoute();
|
||||
this.routeService.setParameter('fixedFilterQuery', this.relationship.filter);
|
||||
this.routeService.setParameter('configuration', this.relationship.searchConfiguration);
|
||||
/**
|
||||
* TODO REMOVE NEXT LINE
|
||||
*/
|
||||
this.relationship.nameVariants = true;
|
||||
if (this.relationship.nameVariants) {
|
||||
this.context = Context.Submission;
|
||||
}
|
||||
|
||||
this.someSelected$ = this.selection$.pipe(map((selection) => isNotEmpty(selection)));
|
||||
this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
|
||||
|
@@ -14,6 +14,7 @@
|
||||
[selectionConfig]="{ repeatable: repeatable, listId: listId }"
|
||||
[config]="initialPagination"
|
||||
[hideGear]="true"
|
||||
[context]="context"
|
||||
(deselectObject)="deselectObject.emit($event)"
|
||||
(selectObject)="selectObject.emit($event)"
|
||||
></ds-viewable-collection>
|
||||
|
@@ -11,6 +11,7 @@ import { PaginatedList } from '../../../../../../core/data/paginated-list';
|
||||
import { Router } from '@angular/router';
|
||||
import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model';
|
||||
import { PageInfo } from '../../../../../../core/shared/page-info.model';
|
||||
import { Context } from '../../../../../../core/shared/context.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dynamic-lookup-relation-selection-tab',
|
||||
@@ -30,6 +31,7 @@ export class DsDynamicLookupRelationSelectionTabComponent {
|
||||
@Input() repeatable: boolean;
|
||||
@Input() selection$: Observable<ListableObject[]>;
|
||||
@Input() selectionRD$: Observable<RemoteData<PaginatedList<ListableObject>>>;
|
||||
@Input() context: Context;
|
||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||
|
||||
@@ -61,16 +63,13 @@ export class DsDynamicLookupRelationSelectionTabComponent {
|
||||
currentPage: pagination.currentPage,
|
||||
totalPages: Math.ceil(selected.length / pagination.pageSize)
|
||||
});
|
||||
return createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, selection))
|
||||
return createSuccessfulRemoteDataObject(new PaginatedList(pageInfo, selection));
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
resetRoute() {
|
||||
this.router.navigate([], {
|
||||
queryParams: Object.assign({}, { page: 1, pageSize: this.initialPagination.pageSize }),
|
||||
|
Reference in New Issue
Block a user