use reorderables instead of relationships

This commit is contained in:
Art Lowel
2019-12-02 18:30:35 +01:00
parent bc8e7d8fe6
commit 78d1c5ee2b
5 changed files with 119 additions and 88 deletions

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { metadataRepresentationComponent } from '../../../../shared/metadata-representation/metadata-representation.decorator'; import { metadataRepresentationComponent } from '../../../../shared/metadata-representation/metadata-representation.decorator';
import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component'; import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
@@ -11,5 +11,8 @@ import { ItemMetadataRepresentationListElementComponent } from '../../../../shar
/** /**
* The component for displaying an item of the type Person as a metadata field * The component for displaying an item of the type Person as a metadata field
*/ */
export class PersonItemMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent { export class PersonItemMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent implements OnInit {
ngOnInit(): void {
console.log('this.metadataRepresentation', this.metadataRepresentation);
}
} }

View File

@@ -54,8 +54,8 @@
<div *ngIf="hasRelationLookup" class="mt-3"> <div *ngIf="hasRelationLookup" class="mt-3">
<ul class="list-unstyled" cdkDropList (cdkDropListDropped)="moveSelection($event)"> <ul class="list-unstyled" cdkDropList (cdkDropListDropped)="moveSelection($event)">
<ds-existing-metadata-list-element cdkDrag [cdkDragData]="relationship" *ngFor="let relationship of relationships; trackBy: trackRelationship" <ds-existing-metadata-list-element cdkDrag [cdkDragData]="relationship" *ngFor="let reorderable of reorderables; trackBy: trackReorderable"
[relationship]="relationship" [reoRel]="reorderable"
[submissionItem]="item" [submissionItem]="item"
[listId]="listId" [listId]="listId"
[metadataFields]="model.metadataFields" [metadataFields]="model.metadataFields"

View File

@@ -50,6 +50,10 @@ import {
DynamicNGBootstrapTimePickerComponent DynamicNGBootstrapTimePickerComponent
} from '@ng-dynamic-forms/ui-ng-bootstrap'; } from '@ng-dynamic-forms/ui-ng-bootstrap';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import {
Reorderable,
ReorderableRelationship
} from './existing-metadata-list-element/existing-metadata-list-element.component';
import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model'; import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model';
import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model';
@@ -176,15 +180,14 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() hasErrorMessaging = false; @Input() hasErrorMessaging = false;
@Input() layout = null as DynamicFormLayout; @Input() layout = null as DynamicFormLayout;
@Input() model: any; @Input() model: any;
relationships$: Observable<Relationship[]>; reorderables$: Observable<ReorderableRelationship[]>;
relationships: Relationship[]; reorderables: ReorderableRelationship[];
hasRelationLookup: boolean; hasRelationLookup: boolean;
modalRef: NgbModalRef; modalRef: NgbModalRef;
item: Item; item: Item;
listId: string; listId: string;
searchConfig: string; searchConfig: string;
/** /**
* List of subscriptions to unsubscribe from * List of subscriptions to unsubscribe from
*/ */
@@ -223,6 +226,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
ngOnInit(): void { ngOnInit(): void {
this.hasRelationLookup = hasValue(this.model.relationship); this.hasRelationLookup = hasValue(this.model.relationship);
this.reorderables = [];
if (this.hasRelationLookup) { if (this.hasRelationLookup) {
this.listId = 'list-' + this.model.relationship.relationshipType; this.listId = 'list-' + this.model.relationship.relationshipType;
const item$ = this.submissionObjectService const item$ = this.submissionObjectService
@@ -238,7 +242,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
); );
this.subs.push(item$.subscribe((item) => this.item = item)); this.subs.push(item$.subscribe((item) => this.item = item));
this.relationships$ = item$.pipe( this.reorderables$ = item$.pipe(
switchMap((item) => this.relationService.getItemRelationshipsByLabel(item, this.model.relationship.relationshipType) switchMap((item) => this.relationService.getItemRelationshipsByLabel(item, this.model.relationship.relationshipType)
.pipe( .pipe(
getAllSucceededRemoteData(), getAllSucceededRemoteData(),
@@ -251,28 +255,24 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
relationship.leftItem.pipe( relationship.leftItem.pipe(
getSucceededRemoteData(), getSucceededRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
map((item: Item) => { map((leftItem: Item) => {
return { relationship, left: item.uuid === this.item.uuid } return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid)
}), }),
) )
))), ))),
map((relationships: { relationship: Relationship, left: boolean }[]) => map((relationships: ReorderableRelationship[]) =>
relationships relationships
.sort(( .sort((a: Reorderable, b: Reorderable) => {
a: { relationship: Relationship, left: boolean }, return Math.sign(a.getPlace() - b.getPlace());
b: { relationship: Relationship, left: boolean }
) => {
const placeA: number = a.left ? a.relationship.leftPlace : a.relationship.rightPlace;
const placeB: number = b.left ? b.relationship.leftPlace : b.relationship.rightPlace;
return Math.sign(placeA - placeB);
}) })
.map((relationship) => relationship.relationship)
) )
) )
) )
); );
this.relationships$.subscribe((rels) => this.relationships = rels); this.subs.push(this.reorderables$.subscribe((rs) => {
this.reorderables = rs
}));
this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe( this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe(
map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))), map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))),
@@ -334,27 +334,18 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
modalComp.item = this.item; modalComp.item = this.item;
} }
moveSelection(event: CdkDragDrop<Relationship>) { moveSelection(event: CdkDragDrop<Relationship>) {
moveItemInArray(this.relationships, event.previousIndex, event.currentIndex); moveItemInArray(this.reorderables, event.previousIndex, event.currentIndex);
this.zone.runOutsideAngular(() => { this.zone.runOutsideAngular(() => {
observableCombineLatest( observableCombineLatest(
this.relationships.map((relationship: Relationship, index: number) => this.reorderables.map((reo: Reorderable, index: number) => {
relationship.leftItem.pipe( reo.oldIndex = reo.getPlace();
getSucceededRemoteData(), reo.newIndex = index;
getRemoteDataPayload(), return reo;
map((item: Item) => {
const left: boolean = item.uuid === this.item.uuid;
if (left) {
return { relationship, left: item.uuid === this.item.uuid, oldIndex: relationship.leftPlace, newIndex: index }
} else {
return { relationship, left: item.uuid === this.item.uuid, oldIndex: relationship.rightPlace, newIndex: index }
} }
}),
)
) )
).pipe( ).pipe(
switchMap((relationships: { relationship: Relationship, left: boolean, oldIndex: number, newIndex: number }[]) => switchMap((relationships: Array<{ relationship: Relationship, left: boolean, oldIndex: number, newIndex: number }>) =>
observableCombineLatest(relationships.map((rel: { relationship: Relationship, left: boolean, oldIndex: number, newIndex: number }) => { observableCombineLatest(relationships.map((rel: { relationship: Relationship, left: boolean, oldIndex: number, newIndex: number }) => {
if (rel.oldIndex !== rel.newIndex) { if (rel.oldIndex !== rel.newIndex) {
return this.relationshipService.updatePlace(rel.relationship, rel.newIndex, rel.left); return this.relationshipService.updatePlace(rel.relationship, rel.newIndex, rel.left);
@@ -381,7 +372,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
/** /**
* Prevent unnecessary rerendering so fields don't lose focus * Prevent unnecessary rerendering so fields don't lose focus
*/ */
trackRelationship(index, relationship: Relationship) { trackReorderable(index, reorderable: Reorderable) {
return hasValue(relationship) ? relationship.id : undefined; return hasValue(reorderable) ? reorderable.getId() : undefined;
} }
} }

View File

@@ -1,4 +1,4 @@
<li *ngIf="metadataRepresentation$ | async"> <li *ngIf="metadataRepresentation">
<button type="button" class="close float-left" aria-label="Move button" cdkDragHandle> <button type="button" class="close float-left" aria-label="Move button" cdkDragHandle>
<i aria-hidden="true" class="fas fa-arrows-alt fa-xs"></i> <i aria-hidden="true" class="fas fa-arrows-alt fa-xs"></i>
</button> </button>
@@ -6,6 +6,6 @@
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
<span class="d-inline-block align-middle ml-1"> <span class="d-inline-block align-middle ml-1">
<ds-metadata-representation-loader [mdRepresentation]="metadataRepresentation$ | async"></ds-metadata-representation-loader> <ds-metadata-representation-loader [mdRepresentation]="metadataRepresentation"></ds-metadata-representation-loader>
</span> </span>
</li> </li>

View File

@@ -1,10 +1,16 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model'; import { Item } from '../../../../../core/shared/item.model';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators'; import { MetadataRepresentation } from '../../../../../core/shared/metadata-representation/metadata-representation.model';
import {
getAllSucceededRemoteData,
getRemoteDataPayload,
getSucceededRemoteData
} from '../../../../../core/shared/operators';
import { hasValue, isNotEmpty } from '../../../../empty.util'; import { hasValue, isNotEmpty } from '../../../../empty.util';
import { filter, map, take } from 'rxjs/operators'; import { of as observableOf, Subscription } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
import { combineLatest as observableCombineLatest } from 'rxjs'; import { combineLatest as observableCombineLatest, of } from 'rxjs';
import { MetadataValue } from '../../../../../core/shared/metadata.models'; import { MetadataValue } from '../../../../../core/shared/metadata.models';
import { ItemMetadataRepresentation } from '../../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { ItemMetadataRepresentation } from '../../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { RelationshipOptions } from '../../models/relationship-options.model'; import { RelationshipOptions } from '../../models/relationship-options.model';
@@ -14,19 +20,55 @@ import { Store } from '@ngrx/store';
import { AppState } from '../../../../../app.reducer'; import { AppState } from '../../../../../app.reducer';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
export abstract class Reorderable {
constructor(public oldIndex?: number, public newIndex?: number) {
}
abstract getId(): string;
abstract getPlace(): number;
}
export class ReorderableRelationship extends Reorderable {
relationship: Relationship;
useLeftItem: boolean;
constructor(relationship: Relationship, useLeftItem: boolean, oldIndex?: number, newIndex?: number) {
super(oldIndex, newIndex);
this.relationship = relationship;
this.useLeftItem = useLeftItem;
}
getId(): string {
return this.relationship.id;
}
getPlace(): number {
if (this.useLeftItem) {
return this.relationship.rightPlace
} else {
return this.relationship.leftPlace
}
}
}
@Component({ @Component({
selector: 'ds-existing-metadata-list-element', selector: 'ds-existing-metadata-list-element',
templateUrl: './existing-metadata-list-element.component.html', templateUrl: './existing-metadata-list-element.component.html',
styleUrls: ['./existing-metadata-list-element.component.scss'] styleUrls: ['./existing-metadata-list-element.component.scss']
}) })
export class ExistingMetadataListElementComponent implements OnChanges { export class ExistingMetadataListElementComponent implements OnInit, OnChanges, OnDestroy {
@Input() listId: string; @Input() listId: string;
@Input() submissionItem: Item; @Input() submissionItem: Item;
@Input() relationship: Relationship; @Input() reoRel: ReorderableRelationship;
@Input() metadataFields: string[]; @Input() metadataFields: string[];
@Input() relationshipOptions: RelationshipOptions; @Input() relationshipOptions: RelationshipOptions;
metadataRepresentation$; metadataRepresentation: MetadataRepresentation;
relatedItem$; relatedItem: Item;
/**
* List of subscriptions to unsubscribe from
*/
private subs: Subscription[] = [];
constructor( constructor(
private selectableListService: SelectableListService, private selectableListService: SelectableListService,
@@ -34,49 +76,44 @@ export class ExistingMetadataListElementComponent implements OnChanges {
) { ) {
} }
ngOnInit(): void {
console.log('reoRel', this.reoRel);
}
ngOnChanges() { ngOnChanges() {
const leftItem$ = this.relationship.leftItem.pipe( const item$ = this.reoRel.useLeftItem ?
getSucceededRemoteData(), this.reoRel.relationship.leftItem : this.reoRel.relationship.rightItem;
this.subs.push(item$.pipe(
getAllSucceededRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid)) filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid))
); ).subscribe((item: Item) => {
this.relatedItem = item;
const rightItem$ = this.relationship.rightItem.pipe( const relationMD: MetadataValue = this.submissionItem.firstMetadata(this.relationshipOptions.metadataField, { value: this.relatedItem.uuid });
getSucceededRemoteData(),
getRemoteDataPayload(),
filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid))
);
this.relatedItem$ = observableCombineLatest(
leftItem$,
rightItem$,
).pipe(
map((items: Item[]) =>
items.find((item) => item.uuid !== this.submissionItem.uuid)
)
);
this.metadataRepresentation$ = this.relatedItem$.pipe(
map((relatedItem: Item) => {
console.log(relatedItem);
const relationMD: MetadataValue = this.submissionItem.firstMetadata(this.relationshipOptions.metadataField, { value: relatedItem.uuid });
console.log(relationMD);
if (hasValue(relationMD)) { if (hasValue(relationMD)) {
const metadataRepresentationMD: MetadataValue = this.submissionItem.firstMetadata(this.metadataFields, { authority: relationMD.authority }); const metadataRepresentationMD: MetadataValue = this.submissionItem.firstMetadata(this.metadataFields, { authority: relationMD.authority });
return Object.assign( this.metadataRepresentation = Object.assign(
new ItemMetadataRepresentation(metadataRepresentationMD), new ItemMetadataRepresentation(metadataRepresentationMD),
relatedItem this.relatedItem
) )
} }
} }));
)
);
} }
removeSelection() { removeSelection() {
this.relatedItem$.pipe(take(1)).subscribe((relatedItem: Item) => { this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem }));
this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: relatedItem })); this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType))
this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, relatedItem, this.relationshipOptions.relationshipType))
})
} }
/**
* Unsubscribe from all subscriptions
*/
ngOnDestroy(): void {
this.subs
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
}
} }