62849: fixed issues with multiple selections, improved UX, updating to use new REST response

This commit is contained in:
lotte
2019-07-29 15:42:30 +02:00
parent 16feb61ebf
commit 1f7b8b8210
20 changed files with 130 additions and 157 deletions

View File

@@ -431,7 +431,6 @@
"volume-title": "Volume Title",
"publisher": "Publisher",
"description": "Description"
},
"listelement": {
"badge": "Publication"
@@ -1038,10 +1037,8 @@
"title": "Edit Submission"
},
"mydspace": {
},
"sections": {
"general": {
"add-more": "Add more",
"no-sections": "No options available",
@@ -1066,7 +1063,6 @@
"submit.progressbar.license": "Deposit license",
"submit.progressbar.cclicense": "Creative commons license",
"submit.progressbar.detect-duplicate": "Potential duplicates",
"upload": {
"no-entry": "No",
"no-file-uploaded": "No file uploaded yet.",
@@ -1097,6 +1093,21 @@
"info": "This operation can't be undone. Are you sure?"
}
}
},
"describe": {
"relationship-lookup": {
"search": "Go",
"loading": "Loading...",
"title": "Select a {{ label }}",
"toggle-dropdown": "Toggle dropdown",
"select-page": "Select page",
"deselect-page": "Deselect page",
"select-all": "Select all",
"deselect-all": "Deselect all",
"close": "Close",
"selected": "Selected {{ size }} items",
"placeholder": "Search query"
}
}
},
"workflow": {
@@ -1132,7 +1143,6 @@
"reject_help": "If you have reviewed the item and found it is <strong>not</strong> suitable for inclusion in the collection, select \"Reject\". You will then be asked to enter a message indicating why the item is unsuitable, and whether the submitter should change something and resubmit.",
"return": "Return to pool",
"return_help": "Return the task to the pool so that another user may perform the task."
},
"pool": {
"claim": "Claim",

View File

@@ -8,39 +8,24 @@
"input": {
"type": "lookup-relation"
},
"label": "Journal Issue",
"label": "Author",
"mandatory": true,
"repeatable": true,
"mandatoryMessage": "Required field!",
"hints": "Select a journal issue for this publication.",
"selectableMetadata": [
"mandatoryMessage": "At least one author (plain text or relationship) is required",
"hints": "Add an author",
"selectableRelationships": [
{
"metadata": "relation.isPublicationOfJournalIssue",
"label": null,
"authority": null,
"closed": null
"relationshipType": "isAuthorOfPublication",
"filter": null,
"searchConfiguration": "personConfiguration"
}
],
"languageCodes": []
}
]
},
{
"fields": [
{
"input": {
"type": "name"
},
"label": "Authors",
"mandatory": false,
"repeatable": true,
"hints": "Enter the names of the authors of this item.",
"selectableMetadata": [
{
"metadata": "dc.contributor.author",
"label": null,
"authority": null,
"closed": null
"closed": false
}
],
"languageCodes": []

View File

@@ -206,7 +206,7 @@ export class SearchConfigurationService implements OnDestroy {
* @returns {Observable<string>} Emits the current fixed filter as a string
*/
getCurrentFixedFilter(): Observable<string> {
return this.routeService.getRouteParameterValue('fixedFilterQuery').pipe(tap((t) => console.log(t)));
return this.routeService.getRouteParameterValue('fixedFilterQuery');
}
/**

View File

@@ -1,8 +1,26 @@
import { isEmpty } from '../../shared/empty.util';
import { GenericConstructor } from '../shared/generic-constructor';
import { EquatableObject } from './equatable';
const excludedFromEquals = new Map();
const fieldsForEqualsMap = new Map();
export function inheritEquatable(parentCo: GenericConstructor<EquatableObject<any>>) {
return function decorator(childCo: GenericConstructor<EquatableObject<any>>) {
const parentExcludedFields = getExcludedFromEqualsFor(parentCo) || [];
const excludedFields = getExcludedFromEqualsFor(childCo) || [];
excludedFromEquals.set(childCo, [...excludedFields, ...parentExcludedFields]);
const mappedFields = fieldsForEqualsMap.get(childCo) || new Map();
const parentMappedFields = fieldsForEqualsMap.get(parentCo) || new Map();
Array.from(parentMappedFields.keys())
.filter((key) => !Array.from(mappedFields.keys()).includes(key))
.forEach((key) => {
fieldsForEquals(...parentMappedFields.get(key))(new childCo(), key);
});
}
}
export function excludeFromEquals(object: any, propertyName: string): any {
if (!object) {
return;
@@ -14,7 +32,7 @@ export function excludeFromEquals(object: any, propertyName: string): any {
excludedFromEquals.set(object.constructor, [...list, propertyName]);
}
export function getExcludedFromEqualsFor(constructor: Function) {
export function getExcludedFromEqualsFor(constructor: Function): string[] {
return excludedFromEquals.get(constructor) || [];
}
@@ -33,7 +51,7 @@ export function fieldsForEquals(...fields: string[]): any {
}
export function getFieldsForEquals(constructor: Function, field: string) {
export function getFieldsForEquals(constructor: Function, field: string): string[] {
const fieldMap = fieldsForEqualsMap.get(constructor) || new Map();
return fieldMap.get(field);
}

View File

@@ -1,5 +1,5 @@
<div class="modal-header">
<h4 class="modal-title" id="modal-title">Choose a {{fieldName}}</h4>
<h4 class="modal-title" id="modal-title">{{ ('submission.sections.describe.relationship-lookup.title' | translate:{label: label}) }}</h4>
<button type="button" class="close" aria-label="Close button" aria-describedby="modal-title"
(click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
@@ -13,10 +13,10 @@
<div class="col-8">
<form class="input-group mb-3" #queryForm="ngForm"
(ngSubmit)="search(queryForm.value.query)">
<input type="text" class="form-control" name="query" placeholder="Search query"
<input type="text" class="form-control" name="query" [placeholder]="'submission.sections.describe.relationship-lookup.placeholder' | translate"
[ngModel]="searchQuery">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Go</button>
<button class="btn btn-outline-secondary" type="submit">{{ ('submission.sections.describe.relationship-lookup.search' | translate) }}</button>
</div>
</form>
<div *ngIf="repeatable" class="position-absolute">
@@ -46,20 +46,20 @@
class="btn btn-outline-secondary rounded-right">
<span class="spinner-border spinner-border-sm" role="status"
aria-hidden="true"></span>
<span class="sr-only">Loading...</span>
<span class="sr-only">{{ ('submission.sections.describe.relationship-lookup.loading' | translate) }}</span>
</button>
<button *ngIf="!selectAllLoading" id="resultdropdown" type="button"
ngbDropdownToggle
class="btn btn-outline-secondary dropdown-toggle-split"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
<span class="sr-only">{{ ('submission.sections.describe.relationship-lookup.toggle-dropdown' | translate) }}</span>
</button>
<div ngbDropdownMenu aria-labelledby="resultdropdown">
<button class="dropdown-item" (click)="selectPage(resultsRD?.payload?.page)">Select page</button>
<button class="dropdown-item" (click)="deselectPage(resultsRD?.payload?.page)">Deselect page</button>
<button class="dropdown-item" (click)="selectAll()">Select all</button>
<button class="dropdown-item" (click)="deselectAll()">Deselect all</button>
<button class="dropdown-item" (click)="selectPage(resultsRD?.payload?.page)">{{ ('submission.sections.describe.relationship-lookup.select-page' | translate) }}</button>
<button class="dropdown-item" (click)="deselectPage(resultsRD?.payload?.page)">{{ ('submission.sections.describe.relationship-lookup.deselect-page' | translate) }}</button>
<button class="dropdown-item" (click)="selectAll()">{{ ('submission.sections.describe.relationship-lookup.select-all' | translate) }}</button>
<button class="dropdown-item" (click)="deselectAll()">{{ ('submission.sections.describe.relationship-lookup.deselect-all' | translate) }}</button>
</div>
</div>
</div>
@@ -68,15 +68,16 @@
[sortConfig]="this.searchConfig?.sort"
[searchConfig]="this.searchConfig"
[selectable]="true"
[selectionConfig]="{ repeatable: repeatable, listId: listId }">
[selectionConfig]="{ repeatable: repeatable, listId: listId }"
(deselectObject)="allSelected = false"
>
</ds-search-results>
</div>
</div>
</div>
<div class="modal-footer">
<small>Selected {{(selection$ | async)?.length || 0}} items</small>
<small>{{ ('submission.sections.describe.relationship-lookup.selected' | translate:{size: (selection$ | async)?.length || 0}) }}</small>
<div>
<button type="button" class="btn btn-outline-secondary" (click)="modal.dismiss()">Cancel</button>
<button type="button" class="btn btn-danger" (click)="close()">Ok</button>
<button type="button" class="btn btn-danger" (click)="close()">{{ ('submission.sections.describe.relationship-lookup.close' | translate) }}</button>
</div>
</div>

View File

@@ -35,7 +35,9 @@ const RELATION_TYPE_FILTER_PREFIX = 'f.entityType=';
})
export class DsDynamicLookupRelationModalComponent implements OnInit {
relationKey: string;
fieldName: string;
label: string;
filter: string;
searchConfiguration: string;
listId: string;
resultsRD$: Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>;
searchConfig: PaginatedSearchOptions;
@@ -49,21 +51,20 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
pageSize: 10
});
selection$: Observable<ListableObject[]>;
fixedFilter: string;
constructor(public modal: NgbActiveModal, private searchService: SearchService, private router: Router, private selectableListService: SelectableListService, private searchConfigService: SearchConfigurationService, private routeService: RouteService) {
}
ngOnInit(): void {
this.resetRoute();
this.fixedFilter = RELATION_TYPE_FILTER_PREFIX + this.fieldName;
this.routeService.setParameter('fixedFilterQuery', this.fixedFilter);
this.routeService.setParameter('fixedFilterQuery', this.filter);
this.routeService.setParameter('configuration', this.searchConfiguration);
this.selection$ = this.selectableListService.getSelectableList(this.listId).pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []));
this.someSelected$ = this.selection$.pipe(map((selection) => isNotEmpty(selection)));
this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
map((options) => {
return Object.assign(new PaginatedSearchOptions({}), options, { fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.fieldName })
return Object.assign(new PaginatedSearchOptions({}), options, { fixedFilter: this.filter, configuration: this.searchConfiguration })
}),
switchMap((options) => {
this.searchConfig = options;
@@ -85,9 +86,9 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
}
search(query: string) {
this.allSelected = false;
this.searchQuery = query;
this.resetRoute();
this.selectableListService.deselectAll(this.listId);
}
close() {
@@ -100,7 +101,6 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
});
}
selectPage(page: SearchResult<DSpaceObject>[]) {
this.selectableListService.select(this.listId, page);
}

View File

@@ -1,4 +1,3 @@
<script src="dynamic-lookup-relation.component.ts"></script>
<div>
<div *ngIf="model.repeatable || !((model.value | async) && (model.value | async).length > 0)" class="form-row align-items-center">
<div class="col">
@@ -12,29 +11,19 @@
</div>
<div class="col-auto text-center">
<button class="btn btn-secondary"
type="button"
type="submit"
ngbTooltip="{{'form.lookup-help' | translate}}"
placement="top"
(click)="openLookup(); $event.stopPropagation();">{{'form.lookup' | translate}}
</button>
</div>
<!--<div class="col-auto text-center">-->
<!--<button class="btn btn-secondary"-->
<!--type="button"-->
<!--ngbTooltip="{{'form.add-help' | translate}}"-->
<!--#tooltip = ngbTooltip-->
<!--placement="top"-->
<!--[disabled]="!hasResultsSelected()"-->
<!--(click)="add(); tooltip.close(); $event.stopPropagation();">{{'form.add' | translate}}-->
<!--</button>-->
<!--</div>-->
</div>
<div class="mt-3">
<ul class="list-unstyled">
<li *ngFor="let result of (model.value | async)">
<ng-container *ngVar="result.indexableObject as value">
<button type="button" class="close float-left" aria-label="Close button"
(click)="removeSelection(value)">
(click)="removeSelection(result)">
<span aria-hidden="true">&times;</span>
</button>
<span class="d-inline-block align-middle ml-1">{{value.name}}</span>

View File

@@ -1,10 +1,6 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import {
DynamicFormControlComponent,
DynamicFormLayoutService,
DynamicFormValidationService
} from '@ng-dynamic-forms/core';
import { DynamicFormControlComponent, DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
@@ -12,11 +8,9 @@ import { DsDynamicLookupRelationModalComponent } from './dynamic-lookup-relation
import { DynamicLookupRelationModel } from './dynamic-lookup-relation.model';
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
import { RelationshipService } from '../../../../../../core/data/relationship.service';
import { Item } from '../../../../../../core/shared/item.model';
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
import { SelectableListState } from '../../../../../object-list/selectable-list/selectable-list.reducer';
import { Observable } from 'rxjs';
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
import { map } from 'rxjs/operators';
import { SearchResult } from '../../../../../search/search-result.model';
@@ -39,8 +33,10 @@ export class DsDynamicLookupRelationComponent extends DynamicFormControlComponen
modalRef: NgbModalRef;
modalValuesString = '';
fieldName: string;
listId: string;
filter: string;
searchConfig: string;
constructor(private modalService: NgbModal,
protected layoutService: DynamicFormLayoutService,
@@ -52,8 +48,9 @@ export class DsDynamicLookupRelationComponent extends DynamicFormControlComponen
}
ngOnInit(): void {
this.fieldName = this.model.name.substring(RELATION_TYPE_METADATA_PREFIX.length);
this.listId = 'list-' + this.fieldName;
this.filter = this.model.relationship.filter;
this.searchConfig = this.model.relationship.searchConfiguration;
this.listId = 'list-' + this.model.relationship.relationshipType;
this.model.value = this.selectableListService.getSelectableList(this.listId).pipe(
map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []),
);
@@ -69,27 +66,15 @@ export class DsDynamicLookupRelationComponent extends DynamicFormControlComponen
modalComp.repeatable = this.model.repeatable;
modalComp.relationKey = this.model.name;
modalComp.listId = this.listId;
modalComp.fieldName = this.fieldName;
modalComp.filter = this.filter;
modalComp.fieldName = this.searchConfig;
modalComp.label = this.model.label;
this.modalRef.result.then((resultString = '') => {
this.modalValuesString = resultString;
});
}
// add() {
// if (isNotEmpty(this.model.value)) {
// this.model.value = [...this.model.value, ...this.selectedResults];
// } else {
// this.model.value = this.selectedResults;
// }
//
// this.modalValuesString = '';
// this.selectedResults = [];
// this.selectedResults.forEach((item: Item) => {
// this.relationService.addRelationship(this.model.item, item);
// })
// }
removeSelection(object: SearchResult<DSpaceObject>) {
this.selectableListService.deselectSingle(this.listId, object);
}

View File

@@ -1,6 +1,7 @@
import { DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core';
import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model';
import { Item } from '../../../../../../core/shared/item.model';
import { RelationshipOptions } from './model/relationship-options.model';
export const DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION = 'LOOKUP_RELATION';
@@ -8,6 +9,7 @@ export interface DynamicLookupRelationModelConfig extends DsDynamicInputModelCon
value?: any;
repeatable: boolean;
item: Item;
relationship: RelationshipOptions;
}
export class DynamicLookupRelationModel extends DsDynamicInputModel {
@@ -15,6 +17,7 @@ export class DynamicLookupRelationModel extends DsDynamicInputModel {
@serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION;
@serializable() value: any;
@serializable() repeatable: boolean;
relationship: RelationshipOptions;
item: Item;
constructor(config: DynamicLookupRelationModelConfig, layout?: DynamicFormControlLayout) {
@@ -25,6 +28,7 @@ export class DynamicLookupRelationModel extends DsDynamicInputModel {
this.disabled = true;
this.repeatable = config.repeatable;
this.item = config.item;
this.relationship = config.relationship;
this.valueUpdates.next(config.value);
}
}

View File

@@ -0,0 +1,5 @@
export interface RelationshipOptions {
relationshipType: string;
filter: string;
searchConfiguration: string;
}

View File

@@ -2,6 +2,7 @@ import { autoserialize } from 'cerialize';
import { FormRowModel } from '../../../../core/config/models/config-submission-forms.model';
import { LanguageCode } from './form-field-language-value.model';
import { FormFieldMetadataValueObject } from './form-field-metadata-value.model';
import { RelationshipOptions } from '../ds-dynamic-form-ui/models/lookup-relation/model/relationship-options.model';
export class FormFieldModel {
@@ -32,6 +33,9 @@ export class FormFieldModel {
@autoserialize
selectableMetadata: FormFieldMetadataValueObject[];
@autoserialize
selectableRelationships: RelationshipOptions[];
@autoserialize
rows: FormRowModel[];

View File

@@ -1,20 +1,14 @@
import { FieldParser } from './field-parser';
import {
DynamicLookupModel,
DynamicLookupModelConfig
} from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model';
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
import {
DynamicLookupRelationModel,
DynamicLookupRelationModelConfig
} from '../ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model';
import { DynamicLookupRelationModel, DynamicLookupRelationModelConfig } from '../ds-dynamic-form-ui/models/lookup-relation/dynamic-lookup-relation.model';
export class LookupRelationFieldParser extends FieldParser {
public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any {
const lookupModelConfig: DynamicLookupRelationModelConfig = this.initModel(null, label);
lookupModelConfig.repeatable = this.configData.repeatable;
/* TODO what to do with multiple relationships? */
lookupModelConfig.relationship = this.configData.selectableRelationships[0];
return new DynamicLookupRelationModel(lookupModelConfig);
}
}

View File

@@ -7,6 +7,8 @@
(pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"
(sortDirectionChange)="onSortDirectionChange($event)"
(deselectObject)="deselectObject.emit($event)"
(selectObject)="selectObject.emit($event)"
(sortFieldChange)="onSortFieldChange($event)"
[selectable]="selectable"
[selectionConfig]="selectionConfig"

View File

@@ -35,6 +35,8 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
@Input() hideGear = false;
@Input() selectable = false;
@Input() selectionConfig: {repeatable: boolean, listId: string};
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
pageInfo: Observable<PageInfo>;
private sub;

View File

@@ -1,7 +1,9 @@
import { SearchResult } from '../../search/search-result.model';
import { Item } from '../../../core/shared/item.model';
import { searchResultFor } from '../../search/search-result-element-decorator';
import { inheritEquatable } from '../../../core/utilities/equals.decorators';
@searchResultFor(Item)
@inheritEquatable(SearchResult)
export class ItemSearchResult extends SearchResult<Item> {
}

View File

@@ -79,8 +79,11 @@ export class ObjectListComponent {
*/
@Output() sortDirectionChange: EventEmitter<SortDirection> = new EventEmitter<SortDirection>();
@Output() paginationChange: EventEmitter<SortDirection> = new EventEmitter<any>();
@Output() paginationChange: EventEmitter<any> = new EventEmitter<any>();
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
/**
* An event fired when the sort field is changed.
* Event's payload equals to the newly selected sort field.
@@ -108,66 +111,24 @@ export class ObjectListComponent {
this.paginationChange.emit(event);
}
// isDisabled(object: ListableObject): boolean {
// return hasValue(this.previousSelection.find((selected) => selected === object));
// }
selectCheckbox(value: boolean, object: ListableObject) {
if (value) {
this.selectionService.selectSingle(this.selectionConfig.listId, object);
this.selectObject.emit(object);
} else {
this.selectionService.deselectSingle(this.selectionConfig.listId, object);
this.deselectObject.emit(object);
}
}
selectRadio(value: boolean, object: ListableObject) {
if (value) {
this.selectionService.selectSingle(this.selectionConfig.listId, object, false);
this.selectObject.emit(object);
} else {
this.selectionService.deselectSingle(this.selectionConfig.listId, object);
this.deselectObject.emit(object);
}
}
selectPage(page: SearchResult<DSpaceObject>[]) {
this.selectionService.select(this.selectionConfig.listId, this.objects.payload.page);
}
deselectPage(page: SearchResult<DSpaceObject>[]) {
this.selectionService.deselect(this.selectionConfig.listId, this.objects.payload.page);
}
deselectAll() {
this.selectionService.deselectAll(this.selectionConfig.listId);
}
// isAllSelected() {
// return this.allSelected;
// }
//
// isSomeSelected() {
// return isNotEmpty(this.selection);
// }
//
//
// selectAll() {
// this.allSelected = true;
// this.selectAllLoading = true;
// const fullPagination = Object.assign(new PaginationComponentOptions(), {
// query: this.searchQuery,
// currentPage: 1,
// pageSize: Number.POSITIVE_INFINITY
// });
// const fullSearchConfig = Object.assign(this.searchConfig, { pagination: fullPagination });
// const results = this.searchService.search(fullSearchConfig);
// results.pipe(
// getSucceededRemoteData(),
// map((resultsRD) => resultsRD.payload.page),
// tap(() => this.selectAllLoading = false)
// )
// .subscribe((results) =>
// this.selection = results
// .map((searchResult) => searchResult.indexableObject)
// .filter((dso) => hasNoValue(this.previousSelection.find((object) => object === dso)))
// );
// }
}

View File

@@ -71,11 +71,13 @@ function select(state: SelectableListState, action: SelectableListSelectAction)
function selectSingle(state: SelectableListState, action: SelectableListSelectSingleAction) {
let newSelection;
if (action.payload.multipleSelectionsAllowed && !isObjectInSelection(state.selection, action.payload.object)) {
if (!isObjectInSelection(state.selection, action.payload.object)) {
if (action.payload.multipleSelectionsAllowed) {
newSelection = [...state.selection, action.payload.object];
} else {
newSelection = [action.payload.object];
}
}
return Object.assign({}, state, { selection: newSelection });
}

View File

@@ -37,8 +37,9 @@ export class SelectableListService {
* Select an object in a specific list in the store
* @param {string} id The id of the list on which the object should be selected
* @param {ListableObject} object The object to select
* @param {boolean} multipleSelectionsAllowed Defines if the multiple selections are allowed for this selectable list
*/
selectSingle(id: string, object: ListableObject, multipleSelectionsAllowed?) {
selectSingle(id: string, object: ListableObject, multipleSelectionsAllowed?: boolean) {
this.store.dispatch(new SelectableListSelectSingleAction(id, object, multipleSelectionsAllowed));
}
@@ -86,7 +87,7 @@ export class SelectableListService {
isObjectSelected(id: string, object: ListableObject): Observable<boolean> {
return this.getSelectableList(id).pipe(
filter((state: SelectableListState) => hasValue(state)),
map((state: SelectableListState) => isNotEmpty(state.selection) && hasValue(state.selection.find((selected) => selected === object))),
map((state: SelectableListState) => isNotEmpty(state.selection) && hasValue(state.selection.find((selected) => selected.equals(object)))),
startWith(false),
distinctUntilChanged()
);

View File

@@ -7,6 +7,8 @@
[hideGear]="true"
[selectable]="selectable"
[selectionConfig]="selectionConfig"
(deselectObject)="deselectObject.emit($event)"
(selectObject)="selectObject.emit($event)"
>
</ds-viewable-collection>
</div>

View File

@@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { RemoteData } from '../../../core/data/remote-data';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { fadeIn, fadeInOut } from '../../animations/fade';
@@ -8,6 +8,7 @@ import { SearchResult } from '../search-result.model';
import { PaginatedList } from '../../../core/data/paginated-list';
import { hasNoValue, isNotEmpty } from '../../empty.util';
import { SortOptions } from '../../../core/cache/models/sort-options.model';
import { ListableObject } from '../../object-collection/shared/listable-object.model';
@Component({
selector: 'ds-search-results',
@@ -58,6 +59,11 @@ export class SearchResultsComponent {
@Input() selectable = false;
@Input() selectionConfig: {repeatable: boolean, listId: string};
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
/**
* Method to change the given string by surrounding it by quotes if not already present.
*/