[DURACOM-304] add support for findAll method in dynamic-scrollable-dropdown.component.ts

This commit is contained in:
FrancescoMolinaro
2025-01-14 17:42:52 +01:00
parent b892b13529
commit 362364136d
6 changed files with 136 additions and 102 deletions

View File

@@ -1,6 +1,6 @@
<ng-container *ngVar="(bitstreamRD$ | async) as bitstreamRD"> <ng-container *ngVar="(bitstreamRD$ | async) as bitstreamRD">
<div class="container" *ngVar="(bitstreamFormatsRD$ | async) as formatsRD"> <div class="container">
<div class="row" *ngIf="bitstreamRD?.hasSucceeded && formatsRD?.hasSucceeded"> <div class="row" *ngIf="bitstreamRD?.hasSucceeded">
<div class="col-md-2"> <div class="col-md-2">
<ds-themed-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-themed-thumbnail> <ds-themed-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-themed-thumbnail>
</div> </div>
@@ -27,7 +27,7 @@
</div> </div>
</div> </div>
<ds-error *ngIf="bitstreamRD?.hasFailed" message="{{'error.bitstream' | translate}}"></ds-error> <ds-error *ngIf="bitstreamRD?.hasFailed" message="{{'error.bitstream' | translate}}"></ds-error>
<ds-themed-loading *ngIf="!bitstreamRD || !formatsRD || bitstreamRD?.isLoading || formatsRD?.isLoading" <ds-themed-loading *ngIf="!bitstreamRD || bitstreamRD?.isLoading"
message="{{'loading.bitstream' | translate}}"></ds-themed-loading> message="{{'loading.bitstream' | translate}}"></ds-themed-loading>
</div> </div>
</ng-container> </ng-container>

View File

@@ -239,7 +239,7 @@ describe('EditBitstreamPageComponent', () => {
}); });
it('should select the correct format', () => { it('should select the correct format', () => {
expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.id); expect(rawForm.formatContainer.selectedFormat).toEqual(selectedFormat.shortDescription);
}); });
it('should put the \"New Format\" input on invisible', () => { it('should put the \"New Format\" input on invisible', () => {
@@ -270,7 +270,13 @@ describe('EditBitstreamPageComponent', () => {
describe('when an unknown format is selected', () => { describe('when an unknown format is selected', () => {
beforeEach(() => { beforeEach(() => {
comp.updateNewFormatLayout(allFormats[0].id); comp.onChange({
model: {
id: 'selectedFormat',
value: allFormats[0],
},
});
comp.updateNewFormatLayout();
}); });
it('should remove the invisible class from the \"New Format\" input', () => { it('should remove the invisible class from the \"New Format\" input', () => {
@@ -372,10 +378,11 @@ describe('EditBitstreamPageComponent', () => {
describe('when selected format has changed', () => { describe('when selected format has changed', () => {
beforeEach(() => { beforeEach(() => {
comp.formGroup.patchValue({ comp.onChange({
formatContainer: { model: {
selectedFormat: allFormats[2].id id: 'selectedFormat',
} value: allFormats[2],
},
}); });
fixture.detectChanges(); fixture.detectChanges();
comp.onSubmit(); comp.onSubmit();

View File

@@ -8,7 +8,7 @@ import {
expand, expand,
Observable, Observable,
of as observableOf, reduce, of as observableOf, reduce,
Subscription Subscription, take
} from 'rxjs'; } from 'rxjs';
import { DynamicFormControlModel, DynamicFormGroupModel, DynamicFormLayout, DynamicFormService, DynamicInputModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; import { DynamicFormControlModel, DynamicFormGroupModel, DynamicFormLayout, DynamicFormService, DynamicInputModel, DynamicSelectModel } from '@ng-dynamic-forms/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
@@ -33,6 +33,8 @@ import { Item } from '../../core/shared/item.model';
import { DsDynamicInputModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { DsDynamicInputModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model';
import { DsDynamicTextAreaModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-textarea.model'; import { DsDynamicTextAreaModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-textarea.model';
import { PrimaryBitstreamService } from '../../core/data/primary-bitstream.service'; import { PrimaryBitstreamService } from '../../core/data/primary-bitstream.service';
import { DynamicScrollableDropdownModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model';
import { FindAllDataImpl } from "../../core/data/base/find-all-data";
@Component({ @Component({
selector: 'ds-edit-bitstream-page', selector: 'ds-edit-bitstream-page',
@@ -51,12 +53,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/ */
bitstreamRD$: Observable<RemoteData<Bitstream>>; bitstreamRD$: Observable<RemoteData<Bitstream>>;
/**
* The formats their remote data observable
* Tracks changes and updates the view
*/
bitstreamFormatsRD$: Observable<RemoteData<PaginatedList<BitstreamFormat>>>;
/** /**
* The UUID of the primary bitstream for this bundle * The UUID of the primary bitstream for this bundle
*/ */
@@ -72,11 +68,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/ */
originalFormat: BitstreamFormat; originalFormat: BitstreamFormat;
/**
* A list of all available bitstream formats
*/
formats: BitstreamFormat[];
/** /**
* @type {string} Key prefix used to generate form messages * @type {string} Key prefix used to generate form messages
*/ */
@@ -163,9 +154,22 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/** /**
* The Dynamic Input Model for the selected format * The Dynamic Input Model for the selected format
*/ */
selectedFormatModel = new DynamicSelectModel({ selectedFormatModel = new DynamicScrollableDropdownModel({
id: 'selectedFormat', id: 'selectedFormat',
name: 'selectedFormat' name: 'selectedFormat',
displayKey: 'shortDescription',
repeatable: false,
metadataFields: [],
submissionId: '',
hasSelectableMetadata: false,
findAllFactory: this.findAllFormatsServiceFactory(),
formatFunction: (format: BitstreamFormat | string) => {
if (format instanceof BitstreamFormat) {
return hasValue(format) && format.supportLevel === BitstreamFormatSupportLevel.Unknown ? this.translate.instant(this.KEY_PREFIX + 'selectedFormat.unknown') : format.shortDescription;
} else {
return format;
}
},
}); });
/** /**
@@ -380,6 +384,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
* @private * @private
*/ */
private bundle: Bundle; private bundle: Bundle;
/**
* The currently selected format
* @private
*/
private selectedFormat: BitstreamFormat;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private router: Router, private router: Router,
@@ -407,25 +416,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
this.entityType = this.route.snapshot.queryParams.entityType; this.entityType = this.route.snapshot.queryParams.entityType;
this.bitstreamRD$ = this.route.data.pipe(map((data: any) => data.bitstream)); this.bitstreamRD$ = this.route.data.pipe(map((data: any) => data.bitstream));
this.bitstreamFormatsRD$ = this.bitstreamFormatService.findAll(this.findAllOptions).pipe(
getFirstSucceededRemoteData(),
expand((response: RemoteData<PaginatedList<BitstreamFormat>>) => {
const pageInfo = response.payload.pageInfo;
if (pageInfo.currentPage < pageInfo.totalPages) {
const nextPageOptions = { ...this.findAllOptions, currentPage: pageInfo.currentPage + 1 };
return this.bitstreamFormatService.findAll(nextPageOptions).pipe(getFirstSucceededRemoteData());
} else {
return EMPTY;
}
}),
);
const bitstreamFormats$ = this.bitstreamFormatsRD$.pipe(
reduce((acc: BitstreamFormat[], response: RemoteData<PaginatedList<BitstreamFormat>>) => {
return acc.concat(response.payload.page);
}, [])
)
const bitstream$ = this.bitstreamRD$.pipe( const bitstream$ = this.bitstreamRD$.pipe(
getFirstSucceededRemoteData(), getFirstSucceededRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
@@ -446,24 +436,31 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
switchMap((bundle: Bundle) => bundle.item), switchMap((bundle: Bundle) => bundle.item),
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
); );
const format$ = bitstream$.pipe(
switchMap(bitstream => bitstream.format),
getFirstSucceededRemoteDataPayload(),
);
this.subs.push( this.subs.push(
observableCombineLatest( observableCombineLatest(
bitstream$, bitstream$,
bitstreamFormats$,
bundle$, bundle$,
primaryBitstream$, primaryBitstream$,
item$, item$,
).pipe() format$,
.subscribe(([bitstream, allFormats, bundle, primaryBitstream, item]) => { ).subscribe(([bitstream, bundle, primaryBitstream, item, format]) => {
this.bitstream = bitstream as Bitstream; this.bitstream = bitstream as Bitstream;
this.formats = allFormats;
this.bundle = bundle; this.bundle = bundle;
this.selectedFormat = format;
// hasValue(primaryBitstream) because if there's no primaryBitstream on the bundle it will // hasValue(primaryBitstream) because if there's no primaryBitstream on the bundle it will
// be a success response, but empty // be a success response, but empty
this.primaryBitstreamUUID = hasValue(primaryBitstream) ? primaryBitstream.uuid : null; this.primaryBitstreamUUID = hasValue(primaryBitstream) ? primaryBitstream.uuid : null;
this.itemId = item.uuid; this.itemId = item.uuid;
this.setIiifStatus(this.bitstream); this.setIiifStatus(this.bitstream);
}) }),
format$.pipe(take(1)).subscribe(
(format) => this.originalFormat = format,
),
); );
this.subs.push( this.subs.push(
@@ -479,7 +476,6 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/ */
setForm() { setForm() {
this.formGroup = this.formService.createFormGroup(this.formModel); this.formGroup = this.formService.createFormGroup(this.formModel);
this.updateFormatModel();
this.updateForm(this.bitstream); this.updateForm(this.bitstream);
this.updateFieldTranslations(); this.updateFieldTranslations();
} }
@@ -498,8 +494,9 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
description: bitstream.firstMetadataValue('dc.description') description: bitstream.firstMetadataValue('dc.description')
}, },
formatContainer: { formatContainer: {
newFormat: hasValue(bitstream.firstMetadata('dc.format')) ? bitstream.firstMetadata('dc.format').value : undefined selectedFormat: this.selectedFormat.shortDescription,
} newFormat: hasValue(bitstream.firstMetadata('dc.format')) ? bitstream.firstMetadata('dc.format').value : undefined,
},
}); });
if (this.isIIIF) { if (this.isIIIF) {
this.formGroup.patchValue({ this.formGroup.patchValue({
@@ -517,36 +514,16 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
} }
}); });
} }
this.bitstream.format.pipe( this.updateNewFormatLayout();
getAllSucceededRemoteDataPayload()
).subscribe((format: BitstreamFormat) => {
this.originalFormat = format;
this.formGroup.patchValue({
formatContainer: {
selectedFormat: format.id
}
});
this.updateNewFormatLayout(format.id);
});
} }
/**
* Create the list of unknown format IDs an add options to the selectedFormatModel
*/
updateFormatModel() {
this.selectedFormatModel.options = this.formats.map((format: BitstreamFormat) =>
Object.assign({
value: format.id,
label: this.isUnknownFormat(format.id) ? this.translate.instant(this.KEY_PREFIX + 'selectedFormat.unknown') : format.shortDescription
}));
}
/** /**
* Update the layout of the "Other Format" input depending on the selected format * Update the layout of the "Other Format" input depending on the selected format
* @param selectedId * @param selectedId
*/ */
updateNewFormatLayout(selectedId: string) { updateNewFormatLayout() {
if (this.isUnknownFormat(selectedId)) { if (this.isUnknownFormat()) {
this.formLayout.newFormat.grid.host = this.newFormatBaseLayout; this.formLayout.newFormat.grid.host = this.newFormatBaseLayout;
} else { } else {
this.formLayout.newFormat.grid.host = this.newFormatBaseLayout + ' invisible'; this.formLayout.newFormat.grid.host = this.newFormatBaseLayout + ' invisible';
@@ -557,9 +534,8 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
* Is the provided format (id) part of the list of unknown formats? * Is the provided format (id) part of the list of unknown formats?
* @param id * @param id
*/ */
isUnknownFormat(id: string): boolean { isUnknownFormat(): boolean {
const format = this.formats.find((f: BitstreamFormat) => f.id === id); return hasValue(this.selectedFormat) && this.selectedFormat.supportLevel === BitstreamFormatSupportLevel.Unknown;
return hasValue(format) && format.supportLevel === BitstreamFormatSupportLevel.Unknown;
} }
/** /**
@@ -591,7 +567,8 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
onChange(event) { onChange(event) {
const model = event.model; const model = event.model;
if (model.id === this.selectedFormatModel.id) { if (model.id === this.selectedFormatModel.id) {
this.updateNewFormatLayout(model.value); this.selectedFormat = model.value;
this.updateNewFormatLayout();
} }
} }
@@ -601,8 +578,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
onSubmit() { onSubmit() {
const updatedValues = this.formGroup.getRawValue(); const updatedValues = this.formGroup.getRawValue();
const updatedBitstream = this.formToBitstream(updatedValues); const updatedBitstream = this.formToBitstream(updatedValues);
const selectedFormat = this.formats.find((f: BitstreamFormat) => f.id === updatedValues.formatContainer.selectedFormat); const isNewFormat = this.selectedFormat.id !== this.originalFormat.id;
const isNewFormat = selectedFormat.id !== this.originalFormat.id;
const isPrimary = updatedValues.fileNamePrimaryContainer.primaryBitstream; const isPrimary = updatedValues.fileNamePrimaryContainer.primaryBitstream;
const wasPrimary = this.primaryBitstreamUUID === this.bitstream.uuid; const wasPrimary = this.primaryBitstreamUUID === this.bitstream.uuid;
@@ -654,7 +630,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
bundle$ = observableOf(this.bundle); bundle$ = observableOf(this.bundle);
} }
if (isNewFormat) { if (isNewFormat) {
bitstream$ = this.bitstreamService.updateFormat(this.bitstream, selectedFormat).pipe( bitstream$ = this.bitstreamService.updateFormat(this.bitstream, this.selectedFormat).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((formatResponse: RemoteData<Bitstream>) => { map((formatResponse: RemoteData<Bitstream>) => {
if (hasValue(formatResponse) && formatResponse.hasFailed) { if (hasValue(formatResponse) && formatResponse.hasFailed) {
@@ -812,4 +788,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
.forEach((subscription) => subscription.unsubscribe()); .forEach((subscription) => subscription.unsubscribe());
} }
findAllFormatsServiceFactory() {
return () => this.bitstreamFormatService as any as FindAllDataImpl<BitstreamFormat>;
}
} }

View File

@@ -40,20 +40,18 @@
(scrolled)="onScroll()" (scrolled)="onScroll()"
[scrollWindow]="false"> [scrollWindow]="false">
<button class="dropdown-item disabled" type="button" *ngIf="optionsList && optionsList.length === 0"> <button class="dropdown-item disabled" *ngIf="optionsList && optionsList.length === 0">{{'form.no-results' | translate}}</button>
{{ 'form.no-results' | translate }}
</button>
<button class="dropdown-item collection-item text-truncate" <button class="dropdown-item collection-item text-truncate"
(click)="onSelect(undefined); sdRef.close()" (mousedown)="onSelect(undefined); sdRef.close()" (click)="onSelect(undefined); sdRef.close()" (mousedown)="onSelect(undefined); sdRef.close()"
title="{{ 'dropdown.clear.tooltip' | translate }}" role="option" title="{{ 'dropdown.clear.tooltip' | translate }}" role="option"
type="button"> >
<i>{{ 'dropdown.clear' | translate }}</i> <i>{{ 'dropdown.clear' | translate }}</i>
</button> </button>
<button class="dropdown-item collection-item text-truncate" *ngFor="let listEntry of optionsList; let i = index" <button class="dropdown-item collection-item text-truncate" *ngFor="let listEntry of optionsList; let i = index"
[class.active]="i === selectedIndex" [class.active]="i === selectedIndex"
(keydown.enter)="onSelect(listEntry); sdRef.close()" (mousedown)="onSelect(listEntry); sdRef.close()" (keydown.enter)="onSelect(listEntry); sdRef.close()" (mousedown)="onSelect(listEntry); sdRef.close()"
title="{{ listEntry.display }}" role="option" type="button" title="{{ inputFormatter(listEntry) }}" role="option"
[attr.id]="listEntry.display === (currentValue|async) ? ('combobox_' + id + '_selected') : null"> [attr.id]="inputFormatter(listEntry) === (currentValue|async) ? ('combobox_' + id + '_selected') : null">
{{inputFormatter(listEntry)}} {{inputFormatter(listEntry)}}
</button> </button>
<div class="scrollable-dropdown-loading text-center" *ngIf="loading"><p>{{'form.loading' | translate}}</p></div> <div class="scrollable-dropdown-loading text-center" *ngIf="loading"><p>{{'form.loading' | translate}}</p></div>

View File

@@ -18,7 +18,7 @@ import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dyna
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { DynamicScrollableDropdownModel } from './dynamic-scrollable-dropdown.model'; import { DynamicScrollableDropdownModel } from './dynamic-scrollable-dropdown.model';
import { PageInfo } from '../../../../../../core/shared/page-info.model'; import { PageInfo } from '../../../../../../core/shared/page-info.model';
import { isEmpty } from '../../../../../empty.util'; import { hasValue, isEmpty } from '../../../../../empty.util';
import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service';
import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators';
import { import {
@@ -27,6 +27,8 @@ import {
} from '../../../../../../core/data/paginated-list.model'; } from '../../../../../../core/data/paginated-list.model';
import { DsDynamicVocabularyComponent } from '../dynamic-vocabulary.component'; import { DsDynamicVocabularyComponent } from '../dynamic-vocabulary.component';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import { FindAllData } from "../../../../../../core/data/base/find-all-data";
import { CacheableObject } from "../../../../../../core/cache/cacheable-object.model";
/** /**
* Component representing a dropdown input field * Component representing a dropdown input field
@@ -55,6 +57,21 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
public selectedIndex = 0; public selectedIndex = 0;
public acceptableKeys = ['Space', 'NumpadMultiply', 'NumpadAdd', 'NumpadSubtract', 'NumpadDecimal', 'Semicolon', 'Equal', 'Comma', 'Minus', 'Period', 'Quote', 'Backquote']; public acceptableKeys = ['Space', 'NumpadMultiply', 'NumpadAdd', 'NumpadSubtract', 'NumpadDecimal', 'Semicolon', 'Equal', 'Comma', 'Minus', 'Period', 'Quote', 'Backquote'];
/**
* If true the component can rely on the findAll method for data loading.
* This is a behaviour activated by dependency injection through the dropdown config.
* If a service that implements findAll is not provided in the config the component falls back on the standard vocabulary service.
*
* @private
*/
private useFindAllService: boolean;
/**
* A service that implements FindAllData.
* If is provided in the config will be used for data loading in stead of the VocabularyService
* @private
*/
private findAllService: FindAllData<CacheableObject>;
constructor(protected vocabularyService: VocabularyService, constructor(protected vocabularyService: VocabularyService,
protected cdr: ChangeDetectorRef, protected cdr: ChangeDetectorRef,
protected layoutService: DynamicFormLayoutService, protected layoutService: DynamicFormLayoutService,
@@ -67,6 +84,9 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
* Initialize the component, setting up the init form value * Initialize the component, setting up the init form value
*/ */
ngOnInit() { ngOnInit() {
this.findAllService = this.model?.findAllFactory();
this.useFindAllService = hasValue(this.findAllService?.findAll) && typeof this.findAllService.findAll === 'function';
this.updatePageInfo(this.model.maxOptions, 1); this.updatePageInfo(this.model.maxOptions, 1);
this.loadOptions(true); this.loadOptions(true);
this.group.get(this.model.id).valueChanges.pipe(distinctUntilChanged()) this.group.get(this.model.id).valueChanges.pipe(distinctUntilChanged())
@@ -75,13 +95,24 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
}); });
} }
/**
* Get service and method to use to retrieve dropdown options
*/
getDataFromService(): Observable<RemoteData<PaginatedList<CacheableObject>>> {
if (this.useFindAllService) {
return this.findAllService.findAll({ elementsPerPage: this.pageInfo.elementsPerPage, currentPage: this.pageInfo.currentPage });
} else {
return this.vocabularyService.getVocabularyEntriesByValue(this.inputText, false, this.model.vocabularyOptions, this.pageInfo);
}
}
loadOptions(fromInit: boolean) { loadOptions(fromInit: boolean) {
this.loading = true; this.loading = true;
this.vocabularyService.getVocabularyEntriesByValue(this.inputText, false, this.model.vocabularyOptions, this.pageInfo).pipe( this.getDataFromService().pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
catchError(() => observableOf(buildPaginatedList(new PageInfo(), []))), catchError(() => observableOf(buildPaginatedList(new PageInfo(), []))),
tap(() => this.loading = false) tap(() => this.loading = false),
).subscribe((list: PaginatedList<VocabularyEntry>) => { ).subscribe((list: PaginatedList<CacheableObject>) => {
this.optionsList = list.page; this.optionsList = list.page;
if (fromInit && this.model.value) { if (fromInit && this.model.value) {
this.setCurrentValue(this.model.value, true); this.setCurrentValue(this.model.value, true);
@@ -101,7 +132,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
/** /**
* Converts an item from the result list to a `string` to display in the `<input>` field. * Converts an item from the result list to a `string` to display in the `<input>` field.
*/ */
inputFormatter = (x: VocabularyEntry): string => x.display || x.value; inputFormatter = (x: any): string => (this.model.formatFunction ? this.model.formatFunction(x) : (x.display || x.value));
/** /**
* Opens dropdown menu * Opens dropdown menu
@@ -204,7 +235,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
this.pageInfo.totalElements, this.pageInfo.totalElements,
this.pageInfo.totalPages this.pageInfo.totalPages
); );
this.vocabularyService.getVocabularyEntriesByValue(this.inputText, false, this.model.vocabularyOptions, this.pageInfo).pipe( this.getDataFromService().pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
catchError(() => observableOf(buildPaginatedList( catchError(() => observableOf(buildPaginatedList(
new PageInfo(), new PageInfo(),
@@ -212,7 +243,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
)) ))
), ),
tap(() => this.loading = false)) tap(() => this.loading = false))
.subscribe((list: PaginatedList<VocabularyEntry>) => { .subscribe((list: PaginatedList<any>) => {
this.optionsList = this.optionsList.concat(list.page); this.optionsList = this.optionsList.concat(list.page);
this.updatePageInfo( this.updatePageInfo(
list.pageInfo.elementsPerPage, list.pageInfo.elementsPerPage,
@@ -243,7 +274,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
setCurrentValue(value: any, init = false): void { setCurrentValue(value: any, init = false): void {
let result: Observable<string>; let result: Observable<string>;
if (init) { if (init && !this.useFindAllService) {
result = this.getInitValueFromModel().pipe( result = this.getInitValueFromModel().pipe(
map((formValue: FormFieldMetadataValueObject) => formValue.display) map((formValue: FormFieldMetadataValueObject) => formValue.display)
); );
@@ -252,6 +283,8 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
result = observableOf(''); result = observableOf('');
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
result = observableOf(value); result = observableOf(value);
} else if (this.useFindAllService) {
result = observableOf(value[this.model.displayKey]);
} else { } else {
result = observableOf(value.display); result = observableOf(value.display);
} }

View File

@@ -1,19 +1,33 @@
import { AUTOCOMPLETE_OFF, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; import { AUTOCOMPLETE_OFF, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core';
import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model'; import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model';
import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model';
import { FindAllDataImpl } from "../../../../../../core/data/base/find-all-data";
import { CacheableObject } from "../../../../../../core/cache/cacheable-object.model";
export const DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN = 'SCROLLABLE_DROPDOWN'; export const DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN = 'SCROLLABLE_DROPDOWN';
export interface DynamicScrollableDropdownModelConfig extends DsDynamicInputModelConfig { export interface DynamicScrollableDropdownModelConfig extends DsDynamicInputModelConfig {
vocabularyOptions: VocabularyOptions; vocabularyOptions?: VocabularyOptions;
maxOptions?: number; maxOptions?: number;
value?: any; value?: any;
displayKey?: string;
formatFunction?: (value: any) => string;
findAllFactory?: () => FindAllDataImpl<CacheableObject>;
} }
export class DynamicScrollableDropdownModel extends DsDynamicInputModel { export class DynamicScrollableDropdownModel extends DsDynamicInputModel {
@serializable() maxOptions: number; @serializable() maxOptions: number;
@serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN; @serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN;
@serializable() displayKey: string;
/**
* Configurable function for display value formatting in input
*/
formatFunction: (value: any) => string;
/**
* Factory for a service that implements FindAllData
*/
findAllFactory: () => FindAllDataImpl<CacheableObject>;
constructor(config: DynamicScrollableDropdownModelConfig, layout?: DynamicFormControlLayout) { constructor(config: DynamicScrollableDropdownModelConfig, layout?: DynamicFormControlLayout) {
@@ -22,6 +36,9 @@ export class DynamicScrollableDropdownModel extends DsDynamicInputModel {
this.autoComplete = AUTOCOMPLETE_OFF; this.autoComplete = AUTOCOMPLETE_OFF;
this.vocabularyOptions = config.vocabularyOptions; this.vocabularyOptions = config.vocabularyOptions;
this.maxOptions = config.maxOptions || 10; this.maxOptions = config.maxOptions || 10;
this.displayKey = config.displayKey || 'display';
this.formatFunction = config.formatFunction;
this.findAllFactory = config.findAllFactory || (() => null);
} }
} }