switch to patch for reorderin

This commit is contained in:
Art Lowel
2020-04-17 11:24:10 +02:00
parent 9a68969eec
commit 19bd36a80f
4 changed files with 38 additions and 228 deletions

View File

@@ -48,7 +48,9 @@ export abstract class Reorderable {
/** /**
* Update the Reorderable * Update the Reorderable
*/ */
abstract update(): Observable<any>; update(): void {
this.oldIndex = this.newIndex;
}
/** /**
* Returns true if the oldIndex of this Reorderable * Returns true if the oldIndex of this Reorderable
@@ -95,14 +97,6 @@ export class ReorderableFormFieldMetadataValue extends Reorderable {
return this.metadataValue.place; return this.metadataValue.place;
} }
/**
* Update the Reorderable
*/
update(): Observable<FormFieldMetadataValueObject> {
this.oldIndex = this.newIndex;
return observableOf(this.metadataValue);
}
} }
/** /**
@@ -140,23 +134,6 @@ export class ReorderableRelationship extends Reorderable {
return this.relationship.leftPlace return this.relationship.leftPlace
} }
} }
/**
* Update the Reorderable
*/
update(): Observable<RemoteData<Relationship>> {
this.store.dispatch(new UpdateRelationshipAction(this.relationship, this.submissionID))
const updatedRelationship$ = this.relationshipService.updatePlace(this).pipe(
getSucceededRemoteData(),
);
updatedRelationship$.subscribe(() => {
this.oldIndex = this.newIndex;
});
return updatedRelationship$;
}
} }
/** /**

View File

@@ -35,8 +35,8 @@
[model]="_model" [model]="_model"
[templates]="templates" [templates]="templates"
[ngClass]="[getClass('element', 'host', _model), getClass('grid', 'host', _model)]" [ngClass]="[getClass('element', 'host', _model), getClass('grid', 'host', _model)]"
(dfBlur)="update(idx)" (dfBlur)="update($event, idx)"
(dfChange)="update(idx)" (dfChange)="update($event, idx)"
(dfFocus)="onFocus($event)" (dfFocus)="onFocus($event)"
(ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container> (ngbEvent)="onCustomEvent($event, null, true)"></ds-dynamic-form-control-container>
</ng-template> </ng-template>

View File

@@ -1,60 +1,25 @@
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { import { Component, EventEmitter, Input, Output, QueryList } from '@angular/core';
ChangeDetectorRef, import { FormGroup } from '@angular/forms';
Component,
EventEmitter,
Input,
NgZone,
OnInit,
Output,
QueryList
} from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { import {
DynamicFormArrayComponent, DynamicFormArrayComponent,
DynamicFormArrayGroupModel,
DynamicFormControlCustomEvent, DynamicFormControlCustomEvent,
DynamicFormControlEvent, DynamicFormControlEventType, DynamicFormControlEvent,
DynamicFormControlEventType,
DynamicFormLayout, DynamicFormLayout,
DynamicFormLayoutService, DynamicFormLayoutService,
DynamicFormService,
DynamicFormValidationService, DynamicFormValidationService,
DynamicTemplateDirective DynamicTemplateDirective
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { map, switchMap, take, tap, filter } from 'rxjs/operators';
import { RelationshipService } from '../../../../../../core/data/relationship.service';
import { RemoteData } from '../../../../../../core/data/remote-data';
import { Relationship } from '../../../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../../../core/shared/item-relationships/relationship.model';
import { Item } from '../../../../../../core/shared/item.model';
import { MetadataValue } from '../../../../../../core/shared/metadata.models';
import {
getRemoteDataPayload,
getSucceededRemoteData
} from '../../../../../../core/shared/operators';
import { SubmissionObject } from '../../../../../../core/submission/models/submission-object.model';
import { SubmissionObjectDataService } from '../../../../../../core/submission/submission-object-data.service';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import {
Reorderable,
ReorderableFormFieldMetadataValue,
ReorderableRelationship
} from '../../existing-metadata-list-element/existing-metadata-list-element.component';
import { DynamicConcatModel } from '../ds-dynamic-concat.model';
import { DynamicRowArrayModel } from '../ds-dynamic-row-array-model'; import { DynamicRowArrayModel } from '../ds-dynamic-row-array-model';
import { Store } from '@ngrx/store';
import { SubmissionService } from '../../../../../../submission/submission.service';
import { AppState } from '../../../../../../app.reducer';
import { followLink } from '../../../../../utils/follow-link-config.model';
import { ObjectCacheService } from '../../../../../../core/cache/object-cache.service';
@Component({ @Component({
selector: 'ds-dynamic-form-array', selector: 'ds-dynamic-form-array',
templateUrl: './dynamic-form-array.component.html', templateUrl: './dynamic-form-array.component.html',
styleUrls: ['./dynamic-form-array.component.scss'] styleUrls: ['./dynamic-form-array.component.scss']
}) })
export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent implements OnInit { export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent {
@Input() bindId = true; @Input() bindId = true;
@Input() group: FormGroup; @Input() group: FormGroup;
@@ -68,169 +33,37 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple
@Output('dfFocus') focus: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>(); @Output('dfFocus') focus: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>();
@Output('ngbEvent') customEvent: EventEmitter<DynamicFormControlCustomEvent> = new EventEmitter(); @Output('ngbEvent') customEvent: EventEmitter<DynamicFormControlCustomEvent> = new EventEmitter();
private submissionItem: Item;
private reorderables: Reorderable[] = [];
/* tslint:enable:no-output-rename */ /* tslint:enable:no-output-rename */
constructor(protected layoutService: DynamicFormLayoutService, constructor(protected layoutService: DynamicFormLayoutService,
protected validationService: DynamicFormValidationService, protected validationService: DynamicFormValidationService,
protected objectCacheService: ObjectCacheService,
protected relationshipService: RelationshipService,
protected changeDetectorRef: ChangeDetectorRef,
protected submissionObjectService: SubmissionObjectDataService,
protected zone: NgZone,
protected formService: DynamicFormService,
private submissionService: SubmissionService,
private store: Store<AppState>,
) { ) {
super(layoutService, validationService); super(layoutService, validationService);
} }
ngOnInit(): void {
this.submissionObjectService
.findById(this.model.submissionId, followLink('item')).pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>)
.pipe(
getSucceededRemoteData(),
getRemoteDataPayload()
)
)
).subscribe((item) => {
this.submissionItem = item;
this.updateReorderables(false);
});
}
private updateReorderables(shouldPropagateChanges = true): void {
this.zone.runOutsideAngular(() => {
let groups = this.model.groups.map((group, index) => [group, (this.control as any).controls[index]]);
groups = [...groups, groups[0]];
const reorderable$arr: Array<Observable<Reorderable>> = groups
.filter(([group, control], index) => index > 0 && hasValue((group.group[0] as any).value)) // disregard the first group, it is always empty to ensure the first field remains empty
.map(([group, control]: [DynamicFormArrayGroupModel, AbstractControl], index: number) => {
const model = group.group[0] as DynamicConcatModel;
let formFieldMetadataValue: FormFieldMetadataValueObject = model.value as FormFieldMetadataValueObject;
if (hasValue(formFieldMetadataValue)) {
const metadataValue = Object.assign(new MetadataValue(), {
value: formFieldMetadataValue.display,
language: formFieldMetadataValue.language,
place: formFieldMetadataValue.place,
authority: formFieldMetadataValue.authority,
confidence: formFieldMetadataValue.confidence
});
if (metadataValue.isVirtual) {
return this.relationshipService.findById(metadataValue.virtualValue, followLink('leftItem'))
.pipe(
filter((relRD: RemoteData<Relationship>) => hasValue(relRD.payload)),
take(1),
getRemoteDataPayload(),
switchMap((relationship: Relationship) =>
relationship.leftItem.pipe(
filter((itemRD: RemoteData<Item>) => hasValue(itemRD.payload)),
take(1),
getRemoteDataPayload(),
map((leftItem: Item) => {
return new ReorderableRelationship(
relationship,
leftItem.uuid !== this.submissionItem.uuid,
this.relationshipService,
this.store,
this.model.submissionId,
index,
index
);
}),
)
)
);
} else {
if (typeof formFieldMetadataValue === 'string') {
formFieldMetadataValue = Object.assign(new FormFieldMetadataValueObject(), {
value: formFieldMetadataValue,
display: formFieldMetadataValue,
place: index,
});
}
return observableOf(new ReorderableFormFieldMetadataValue(formFieldMetadataValue, model as any, control as FormControl, group, index, index));
}
} else {
formFieldMetadataValue = Object.assign(new FormFieldMetadataValueObject(), {
value: '',
display: '',
place: index,
});
return observableOf(new ReorderableFormFieldMetadataValue(formFieldMetadataValue, model as any, control as FormControl, group, index, index));
}
});
observableCombineLatest(reorderable$arr)
.subscribe((reorderables: Reorderable[]) => {
reorderables.forEach((newReorderable: Reorderable) => {
const match = this.reorderables.find((reo: Reorderable) => reo.getId() === newReorderable.getId());
if (hasValue(match)) {
newReorderable.oldIndex = match.newIndex;
} else {
newReorderable.oldIndex = -1;
}
});
this.reorderables = reorderables;
if (shouldPropagateChanges) {
const movedReoRels: Array<Reorderable> = [];
let hasMetadataField = false;
this.reorderables.forEach((reorderable: Reorderable, index: number) => {
if (reorderable.hasMoved) {
if (reorderable instanceof ReorderableFormFieldMetadataValue) {
const prevIndex = reorderable.oldIndex;
hasMetadataField = true;
reorderable.update().pipe(take(1)).subscribe((v) => {
const reoMD = reorderable as ReorderableFormFieldMetadataValue;
reoMD.model.value = reoMD.metadataValue;
this.onChange({
$event: { previousIndex: prevIndex },
context: { index },
control: reoMD.control,
group: this.group,
model: reoMD.model,
type: DynamicFormControlEventType.Change
});
});
} else if (reorderable instanceof ReorderableRelationship) {
movedReoRels.push(reorderable)
}
}
});
if (isNotEmpty(movedReoRels) && hasMetadataField && hasValue(this.model.relationshipConfig)) {
// if it's a mix between entities and regular metadata fields,
// we need to save, since they use different endpoints and
// otherwise they'll get out of sync.
this.submissionService.dispatchSave(this.model.submissionId);
}
observableCombineLatest(
movedReoRels.map((movedReoRel) => movedReoRel.update().pipe(take(1)))
).subscribe(() => {
this.changeDetectorRef.detectChanges();
});
}
});
})
}
moveSelection(event: CdkDragDrop<Relationship>) { moveSelection(event: CdkDragDrop<Relationship>) {
this.model.moveGroup(event.previousIndex, event.currentIndex - event.previousIndex); this.model.moveGroup(event.previousIndex, event.currentIndex - event.previousIndex);
this.updateReorderables(); const prevIndex = event.previousIndex - 1;
const index = event.currentIndex - 1;
const $event = {
$event: { previousIndex: prevIndex },
context: { index },
control: (this.control as any).controls[index],
group: this.group,
model: this.model.groups[index].group[0],
type: DynamicFormControlEventType.Change
};
this.onChange($event);
} }
update(index: number) { update(event: any, index: number) {
if (index > 0) { const $event = Object.assign({}, event, {
this.updateReorderables(); context: { index: index - 1}
} });
console.log('$event', $event);
this.onChange($event)
} }
} }

View File

@@ -366,14 +366,7 @@ export class SectionFormOperationsService {
value, true); value, true);
} else if (previousValue.isPathEqual(this.formBuilder.getPath(event.model)) || hasStoredValue) { } else if (previousValue.isPathEqual(this.formBuilder.getPath(event.model)) || hasStoredValue) {
// Here model has a previous value changed or stored in the server // Here model has a previous value changed or stored in the server
if (!value.hasValue()) { if (hasValue(event.$event) && hasValue(event.$event.previousIndex)) {
// New value is empty, so dispatch a remove operation
if (this.getArrayIndexFromEvent(event) === 0) {
this.operationsBuilder.remove(pathCombiner.getPath(segmentedPath));
} else {
this.operationsBuilder.remove(pathCombiner.getPath(path));
}
} else if (hasValue(event.$event) && hasValue(event.$event.previousIndex)) {
if (event.$event.previousIndex < 0) { if (event.$event.previousIndex < 0) {
this.operationsBuilder.add( this.operationsBuilder.add(
pathCombiner.getPath(segmentedPath), pathCombiner.getPath(segmentedPath),
@@ -384,6 +377,13 @@ export class SectionFormOperationsService {
pathCombiner.getPath(segmentedPath + '/' + event.$event.previousIndex).path pathCombiner.getPath(segmentedPath + '/' + event.$event.previousIndex).path
) )
} }
} else if (!value.hasValue()) {
// New value is empty, so dispatch a remove operation
if (this.getArrayIndexFromEvent(event) === 0) {
this.operationsBuilder.remove(pathCombiner.getPath(segmentedPath));
} else {
this.operationsBuilder.remove(pathCombiner.getPath(path));
}
} else { } else {
// New value is not equal from the previous one, so dispatch a replace operation // New value is not equal from the previous one, so dispatch a replace operation
this.operationsBuilder.replace( this.operationsBuilder.replace(