62849: progress july 5

This commit is contained in:
lotte
2019-07-05 15:19:16 +02:00
parent 998a7107a8
commit 5c39d3c8d1
13 changed files with 243 additions and 52 deletions

View File

@@ -13,7 +13,6 @@ import { combineLatest as combineLatestObservable } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';

View File

@@ -44,7 +44,6 @@ export abstract class BaseResponseParsingService {
}
});
}
this.cache(object, requestUUID);
return object;
}

View File

@@ -10,7 +10,7 @@ import {
getRemoteDataPayload, getResponseFromEntry,
getSucceededRemoteData
} from '../shared/operators';
import { DeleteRequest, RestRequest } from './request.models';
import { DeleteRequest, PostRequest, RestRequest } from './request.models';
import { Observable } from 'rxjs/internal/Observable';
import { RestResponse } from '../cache/response.models';
import { Item } from '../shared/item.model';
@@ -64,6 +64,22 @@ export class RelationshipService {
);
}
/**
* Send a post request for a relationship by ID
* @param item1
* @param item2
*/
addRelationship(item1: Item, item2: Item): Observable<RestResponse> {
return this.halService.getEndpoint(this.linkPath).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
map((endpointURL: string) => new PostRequest(this.requestService.generateRequestId(), endpointURL, `${item1.self} ${item2.self}`)),
configureRequest(this.requestService),
switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
getResponseFromEntry()
);
}
/**
* Get a combined observable containing an array of all relationships in an item, as well as an array of the relationships their types
* This is used for easier access of a relationship's type because they exist as observables

View File

@@ -10,7 +10,6 @@ import { BrowseDefinition } from './browse-definition.model';
import { DSpaceObject } from './dspace-object.model';
import { PaginatedList } from '../data/paginated-list';
import { SearchResult } from '../../+search-page/search-result.model';
import { Item } from './item.model';
import { Router } from '@angular/router';
/**

View File

@@ -72,7 +72,6 @@ import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION } from './models/lookup-relat
import { DsDynamicLookupRelationComponent } from './models/lookup-relation/dynamic-lookup-relation.component';
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
console.log(model.type);
switch (model.type) {
case DYNAMIC_FORM_CONTROL_TYPE_ARRAY:
return DsDynamicFormArrayComponent;

View File

@@ -1,26 +1,88 @@
<div class="container">
<div class="search-page row">
<div class="col-12">
<div class="row">
<div id="search-body">
<div id="search-content" class="col-12" *ngVar="(resultsRD$ | async) as resultsRD">
<div class="modal-header">
<h4 class="modal-title" id="modal-title">Choose a {{fieldName}}</h4>
<button type="button" class="close" aria-label="Close button" aria-describedby="modal-title"
(click)="modal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" *ngVar="(resultsRD$ | async) as resultsRD">
<ds-loading *ngIf="!resultsRD || resultsRD.isLoading"></ds-loading>
<div *ngIf="resultsRD.hasSucceeded">
<div *ngIf="repeatable">
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<!-- In theory we don't need separate checkboxes for this,
but I wasn't able to get this to work correctly without them.
Checkboxes that are in the indeterminate state always switch to checked when clicked
This seemed like the cleanest and clearest solution to solve this issue for now.
-->
<input *ngIf="!isAllSelected() && !isSomeSelected()" type="checkbox"
[indeterminate]="false"
(change)="selectAll()"
>
<input *ngIf="!isAllSelected() && isSomeSelected()" type="checkbox"
[indeterminate]="true"
(change)="deselectAll()"
>
<input *ngIf="isAllSelected()" type="checkbox"
[checked]="true"
(change)="deselectAll()"
>
</div>
</div>
<div ngbDropdown class="input-group-append">
<button 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>
</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>
</div>
</div>
</div>
</div>
<ds-pagination
[paginationOptions]="searchConfig.pagination"
[collectionSize]="resultsRD?.payload?.totalElements"
[sortOptions]="searchConfig.sort"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
(paginationChange)="onPaginationChange($event)">
<ul class="lookup-results list-unstyled">
<li *ngFor="let result of resultsRD?.payload?.page; let i = index" class="my-4 d-flex">
<input type="checkbox" [value]="result.indexableObject.uuid"/>
<ds-wrapper-list-element class="result-list-element" [object]="result" [index]="i"></ds-wrapper-list-element>
</li>
</ul>
(paginationChange)="onPaginationChange($event.pagination)">
<div class="form-check" *ngFor="let result of resultsRD?.payload?.page; let i = index">
<input *ngIf="repeatable" class="form-check-input" type="checkbox"
[name]="'checkbox' + i"
[id]="'object'+i"
[checked]="isSelected(result.indexableObject)"
(change)="selectCheckbox($event.currentTarget.checked, result.indexableObject)">
<input *ngIf="!repeatable" class="form-check-input" type="radio"
[name]="'radio' + i"
[id]="'object'+i"
[checked]="isSelected(result.indexableObject)"
(change)="selectRadio($event.currentTarget.checked, result.indexableObject)">
<label class="form-check-label" [for]="'object'+i">
<ds-wrapper-list-element class="result-list-element"
[object]="result"
[index]="i"></ds-wrapper-list-element>
</label>
</div>
</ds-pagination>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<small>Selected {{selection.length}} items</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>
</div>
</div>

View File

@@ -1,3 +1,7 @@
.result-list-element {
flex: 1;
}
.modal-footer {
justify-content: space-between;
}

View File

@@ -2,11 +2,16 @@ import { Component, OnInit } from '@angular/core';
import { PaginatedList } from '../../../../../../core/data/paginated-list';
import { SearchResult } from '../../../../../../+search-page/search-result.model';
import { RemoteData } from '../../../../../../core/data/remote-data';
import { Observable } from 'rxjs';
import { asyncScheduler, Observable, ReplaySubject } from 'rxjs';
import { SearchService } from '../../../../../../+search-page/search-service/search.service';
import { PaginatedSearchOptions } from '../../../../../../+search-page/paginated-search-options.model';
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { hasNoValue, hasValue, isNotEmpty } from '../../../../../empty.util';
import { getSucceededRemoteData } from '../../../../../../core/shared/operators';
import { finalize, map, takeUntil, takeWhile } from 'rxjs/operators';
import { concat, multicast, take } from 'rxjs/operators';
const RELATION_TYPE_FILTER_PREFIX = 'f.entityType=';
@@ -20,20 +25,111 @@ const RELATION_TYPE_METADATA_PREFIX = 'relation.isPublicationOf';
})
export class DsDynamicLookupRelationModalComponent implements OnInit {
relationKey: string;
fieldName: string;
resultsRD$: Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>;
searchConfig: PaginatedSearchOptions;
repeatable: boolean = true;
selection: DSpaceObject[] = [];
allSelected = false;
constructor(private searchService: SearchService) {
constructor(protected modal: NgbActiveModal, private searchService: SearchService) {
}
ngOnInit(): void {
const pagination = Object.assign(new PaginationComponentOptions(), { pageSize: 5 });
this.searchConfig = new PaginatedSearchOptions({
pagination: pagination,
fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.relationKey.substring(RELATION_TYPE_METADATA_PREFIX.length)
this.fieldName = this.relationKey.substring(RELATION_TYPE_METADATA_PREFIX.length);
const pagination = Object.assign(new PaginationComponentOptions(), {
id: 'submission-relation-list',
pageSize: 5
});
this.resultsRD$ = this.searchService.search(this.searchConfig);
this.onPaginationChange(pagination);
}
onPaginationChange() {}
onPaginationChange(pagination: PaginationComponentOptions) {
this.searchConfig = new PaginatedSearchOptions({
pagination: pagination,
fixedFilter: RELATION_TYPE_FILTER_PREFIX + this.fieldName
});
this.resultsRD$ = this.searchService.search(this.searchConfig).pipe(
/* Make sure to only listen to the first x results, until loading is finished */
/* TODO: in Rxjs 6.4.0 and up, we can replace this by takeWhile(predicate, true) - see https://stackoverflow.com/a/44644237 */
multicast(
() => new ReplaySubject(1),
subject => subject.pipe(
takeWhile((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => rd.isLoading),
concat(subject.pipe(take(1))
)
)
) as any
)
}
close() {
this.modal.close(this.selection);
}
isSelected(dso: DSpaceObject): boolean {
return hasValue(this.selection.find((selected) => selected.uuid === dso.uuid));
}
selectCheckbox(value: boolean, dso: DSpaceObject) {
if (value) {
this.selection = [...this.selection, dso];
} else {
this.allSelected = false;
this.selection = this.selection.filter((selected) => {
return selected.uuid !== dso.uuid
});
}
}
selectRadio(value: boolean, dso: DSpaceObject) {
if (value) {
this.selection = [dso];
}
}
selectPage(page: SearchResult<DSpaceObject>[]) {
const newObjects: DSpaceObject[] = page
.map((searchResult) => searchResult.indexableObject)
.filter((dso) => hasNoValue(this.selection.find((selected) => selected.uuid === dso.uuid)));
this.selection = [...this.selection, ...newObjects]
}
deselectPage(page: SearchResult<DSpaceObject>[]) {
this.allSelected = false;
const objects: DSpaceObject[] = page
.map((searchResult) => searchResult.indexableObject);
this.selection = this.selection.filter((selected) => hasNoValue(objects.find((object) => object.uuid === selected.uuid)));
}
selectAll() {
this.allSelected = true;
const fullPagination = Object.assign(new PaginationComponentOptions(), {
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)
)
.subscribe((results) =>
this.selection = [...this.selection, ...results.map((searchResult) => searchResult.indexableObject)]
);
}
deselectAll() {
this.allSelected = false;
this.selection = [];
}
isAllSelected() {
return this.allSelected;
}
isSomeSelected() {
return isNotEmpty(this.selection);
}
}

View File

@@ -3,15 +3,20 @@
<!--Simple lookup, first field -->
<div class="col right-addon">
<input class="form-control"
[attr.autoComplete]="model.autoComplete"
[class.is-invalid]="showErrorMessages"
[dynamicId]="bindId && model.id"
[name]="model.name"
[type]="model.inputType"
[(ngModel)]="model.value"
[value]="modalValuesString"
[disabled]="model.disabled"
[type]="model.inputType"
[placeholder]="model.placeholder | translate"
[readonly]="model.readOnly">
<ng-container *ngFor="let value of model.value">
<input [dynamicId]="bindId && model.id"
[name]="model.name"
type="hidden"
[ngModel]="value.uuid"
[disabled]="true"
[readonly]="true">
</ng-container>
</div>
<div class="col-auto text-center">
<button class="btn btn-secondary"

View File

@@ -6,10 +6,12 @@ import {
DynamicFormValidationService
} from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms';
import { DynamicRelationGroupModel } from '../relation-group/dynamic-relation-group.model';
import { isNotEmpty } from '../../../../../empty.util';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { DsDynamicLookupRelationModalComponent } from './dynamic-lookup-relation-modal.component';
import { DynamicLookupRelationModel } from './dynamic-lookup-relation.model';
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
import { RelationshipService } from '../../../../../../core/data/relationship.service';
@Component({
selector: 'ds-dynamic-lookup-relation',
@@ -20,22 +22,26 @@ export class DsDynamicLookupRelationComponent extends DynamicFormControlComponen
@Input() formId: string;
@Input() group: FormGroup;
@Input() model: DynamicRelationGroupModel;
@Input() model: DynamicLookupRelationModel;
@Output() blur: EventEmitter<any> = new EventEmitter<any>();
@Output() change: EventEmitter<any> = new EventEmitter<any>();
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
modalRef: NgbModalRef;
modalValuesString = '';
currentObjects;
constructor(private modalService: NgbModal,
protected layoutService: DynamicFormLayoutService,
protected validationService: DynamicFormValidationService
protected validationService: DynamicFormValidationService,
private relationshipService: RelationshipService
) {
super(layoutService, validationService);
}
ngOnInit(): void {
this.currentObjects = this.relationshipService.getItemRelationshipLabels();
}
public hasEmptyValue() {
@@ -45,8 +51,9 @@ export class DsDynamicLookupRelationComponent extends DynamicFormControlComponen
openLookup() {
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent);
this.modalRef.componentInstance.relationKey = this.model.name;
this.modalRef.result.then((result) => {
this.model.value = result;
this.modalRef.result.then((resultList) => {
this.model.value = resultList;
this.modalValuesString = resultList.map((dso: DSpaceObject) => dso.name).join('; ');
});
}

View File

@@ -10,7 +10,7 @@ export interface DynamicLookupRelationModelConfig extends DsDynamicInputModelCon
export class DynamicLookupRelationModel extends DsDynamicInputModel {
@serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_LOOKUP_RELATION;
@serializable() value: any;
@serializable() value: any[];
constructor(config: DynamicLookupRelationModelConfig, layout?: DynamicFormControlLayout) {

View File

@@ -12,7 +12,6 @@
(dfFocus)="onFocus($event)">
<ng-template modelType="ARRAY" let-group let-index="index" let-context="context">
<!--Array with repeteable items-->
<div *ngIf="!context.notRepeatable"
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end">

View File

@@ -328,13 +328,19 @@ export class PaginationComponent implements OnDestroy, OnInit {
* Method to emit a general pagination change event
*/
private emitPaginationChange() {
this.paginationChange.emit({
pageId: this.id,
page: this.currentPage,
this.paginationChange.emit(
{
pagination: Object.assign(
new PaginationComponentOptions(),
{
id: this.id,
currentPage: this.currentPage,
pageSize: this.pageSize,
sortDirection: this.sortDirection,
sortField: this.sortField
});
}),
sort: Object.assign(
new SortOptions(this.sortField, this.sortDirection)
)
})
}
/**