diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java index ca42d2b97..6511679db 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java @@ -10,12 +10,11 @@ public class AutoCompleteData extends ComboBoxData { private String url; private Option autoCompleteOptions; private String optionsRoot; - + private Boolean multiAutoComplete; public String getOptionsRoot() { return optionsRoot; } - public void setOptionsRoot(String optionsRoot) { this.optionsRoot = optionsRoot; } @@ -23,7 +22,6 @@ public class AutoCompleteData extends ComboBoxData { public String getUrl() { return url; } - public void setUrl(String url) { this.url = url; } @@ -31,17 +29,20 @@ public class AutoCompleteData extends ComboBoxData { public Option getAutoCompleteOptions() { return autoCompleteOptions; } - public void setAutoCompleteOptions(Option autoCompleteOptions) { this.autoCompleteOptions = autoCompleteOptions; } + public Boolean getMultiAutoComplete() { return multiAutoComplete; } + public void setMultiAutoComplete(Boolean multiAutoComplete) { this.multiAutoComplete = multiAutoComplete; } + @Override public Element toXml(Document doc) { Element root = super.toXml(doc); root.setAttribute("url", this.url); root.setAttribute("optionsRoot", this.optionsRoot); + root.setAttribute("multiAutoComplete", this.multiAutoComplete.toString()); Element element = doc.createElement("option"); element.setAttribute("label", this.autoCompleteOptions.getLabel()); element.setAttribute("value", autoCompleteOptions.getValue()); @@ -54,6 +55,7 @@ public class AutoCompleteData extends ComboBoxData { super.fromXml(item); this.url = item.getAttribute("url"); this.optionsRoot = item.getAttribute("optionsRoot"); + this.multiAutoComplete = Boolean.parseBoolean(item.getAttribute("multiAutoComplete")); Element optionElement = (Element) item.getElementsByTagName("option").item(0); if (optionElement != null) { this.autoCompleteOptions = new Option(); @@ -70,6 +72,7 @@ public class AutoCompleteData extends ComboBoxData { if (data != null) { this.url = (String) ((Map) data).get("url"); this.optionsRoot = (String) ((Map) data).get("optionsRoot"); + this.multiAutoComplete = (Boolean) ((Map) data).get("multiAutoComplete"); Map options = ((Map>) data).get("autoCompleteOptions"); if (options != null) { this.autoCompleteOptions.setLabel(options.get("label")); diff --git a/dmp-frontend/src/app/core/formatting.module.ts b/dmp-frontend/src/app/core/formatting.module.ts index 83b0f4ef0..91311f536 100644 --- a/dmp-frontend/src/app/core/formatting.module.ts +++ b/dmp-frontend/src/app/core/formatting.module.ts @@ -5,6 +5,7 @@ import { DateTimeFormatPipe } from './pipes/date-time-format.pipe'; import { NgForLimitPipe } from './pipes/ng-for-limit.pipe'; import { TimezoneInfoDisplayPipe } from './pipes/timezone-info-display.pipe'; import { EnumUtils } from './services/utilities/enum-utils.service'; +import { JsonParserPipe } from './pipes/json-parser.pipe'; // // @@ -17,13 +18,15 @@ import { EnumUtils } from './services/utilities/enum-utils.service'; NgForLimitPipe, TimezoneInfoDisplayPipe, DateFormatPipe, - DateTimeFormatPipe + DateTimeFormatPipe, + JsonParserPipe ], exports: [ NgForLimitPipe, TimezoneInfoDisplayPipe, DateFormatPipe, - DateTimeFormatPipe + DateTimeFormatPipe, + JsonParserPipe ], providers: [ EnumUtils, @@ -31,7 +34,8 @@ import { EnumUtils } from './services/utilities/enum-utils.service'; NgForLimitPipe, TimezoneInfoDisplayPipe, DateFormatPipe, - DateTimeFormatPipe + DateTimeFormatPipe, + JsonParserPipe ] }) export class FormattingModule { } diff --git a/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts b/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts index ab96fd237..43a5d8e88 100644 --- a/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts +++ b/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts @@ -9,6 +9,7 @@ export interface AutoCompleteFieldData extends FieldData { url: string; optionsRoot: string; autoCompleteOptions: FieldDataOption; + multiAutoComplete: boolean; } export interface CheckBoxFieldData extends FieldData { @@ -43,4 +44,4 @@ export interface FieldDataOption extends FieldData { export interface DatePickerFieldData extends FieldData { -} \ No newline at end of file +} diff --git a/dmp-frontend/src/app/core/pipes/json-parser.pipe.ts b/dmp-frontend/src/app/core/pipes/json-parser.pipe.ts new file mode 100644 index 000000000..625bf02a0 --- /dev/null +++ b/dmp-frontend/src/app/core/pipes/json-parser.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'jsonParser' +}) +export class JsonParserPipe implements PipeTransform { + + transform(val) { + if (typeof val === 'string') { + return JSON.parse(val) + } + else { + return val; + } + } +} diff --git a/dmp-frontend/src/app/library/auto-complete/auto-complete.module.ts b/dmp-frontend/src/app/library/auto-complete/auto-complete.module.ts index 00a7b0ff2..b7cf221e6 100644 --- a/dmp-frontend/src/app/library/auto-complete/auto-complete.module.ts +++ b/dmp-frontend/src/app/library/auto-complete/auto-complete.module.ts @@ -3,12 +3,14 @@ import { CommonFormsModule } from '../../common/forms/common-forms.module'; import { CommonUiModule } from '../../common/ui/common-ui.module'; import { MultipleAutoCompleteComponent } from './multiple/multiple-auto-complete.component'; import { SingleAutoCompleteComponent } from './single/single-auto-complete.component'; +import { FormattingModule } from '../../core/formatting.module'; @NgModule({ imports: [ CommonUiModule, - CommonFormsModule + CommonFormsModule, + FormattingModule ], declarations: [ SingleAutoCompleteComponent, diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts index 5eedff7a5..98c86ee8d 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts @@ -20,4 +20,6 @@ export interface MultipleAutoCompleteConfiguration { titleFn?: (item: any) => string; // Display function for the drop down subtitle subtitleFn?: (item: any) => string; + // Callback to intercept value assignment based on item selection + valueAssign?: (selectedItem: any) => any; } diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html index 8b035d27c..5c6d1b02b 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html @@ -1,6 +1,6 @@
- + {{this._displayFn(selectedItem)}} cancel diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts index b29306f1b..9c9fdf719 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts @@ -87,16 +87,9 @@ export class MultipleAutoCompleteComponent implements OnInit, MatFormFieldContro } private _selectedValue; - - - - @ViewChild('textInput') textInput: ElementRef; @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger; - - - constructor( private fm: FocusMonitor, private elRef: ElementRef, @@ -115,7 +108,6 @@ export class MultipleAutoCompleteComponent implements OnInit, MatFormFieldContro } ngOnInit() { - } filter(query: string): Observable { @@ -168,13 +160,31 @@ export class MultipleAutoCompleteComponent implements OnInit, MatFormFieldContro return this.value || []; } + _arraysEqual(arr1, arr2) { + if (arr1.length !== arr2.length) + return false; + for (var i = arr1.length; i--;) { + if (arr1[i].id !== arr2[i].id) + return false; + } + + return true; + } + _optionSelected(event: MatAutocompleteSelectedEvent) { - const newValue = this.value || []; - newValue.push(event.option.value); - this._setValue(newValue); - this.stateChanges.next(); - this.optionSelected.emit(newValue); - this.textInput.nativeElement.value = ''; + if (this.configuration.valueAssign) { + const newValue = this.configuration.valueAssign(this.value) || []; + newValue.push(event.option.value); + this._setValue(this.configuration.valueAssign(newValue)); + } + else { + const newValue = this.value || []; + newValue.push(event.option.value); + this._setValue(newValue); + this.stateChanges.next(); + this.optionSelected.emit(newValue); + this.textInput.nativeElement.value = ''; + } } private _setValue(value: any) { @@ -221,13 +231,19 @@ export class MultipleAutoCompleteComponent implements OnInit, MatFormFieldContro } _removeSelectedItem(item: any): void { - const index = this.value.indexOf(item); - if (index >= 0) { - this.value.splice(index, 1); + if (this.configuration.valueAssign) { this.optionRemoved.emit(item); + this.textInput.nativeElement.focus(); + } + else { + const index = this.value.indexOf(item); + if (index >= 0) { + this.value.splice(index, 1); + this.optionRemoved.emit(item); + } + this.textInput.nativeElement.focus(); + this.pushChanges(this.value); } - this.textInput.nativeElement.focus(); - this.pushChanges(this.value); } _onInputClick(item: any) { diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-field-data-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-field-data-editor-model.ts index fbcc694f6..3f31b849a 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-field-data-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-field-data-editor-model.ts @@ -9,16 +9,20 @@ export class AutoCompleteFieldDataEditorModel extends FieldDataEditorModel -
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TITLE' | translate}}
+
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TITLE' | translate}}
+ + DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE + - + - + diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html index 2a99bad3b..16f83e6c5 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html @@ -1,4 +1,5 @@ -
+
{{this.form.get('title').value}}
info @@ -6,20 +7,28 @@
{{this.form.get('description').value}}
- {{this.form.get('extendedDescription').value}}
+ {{this.form.get('extendedDescription').value}} + - {{'GENERAL.VALIDATION.REQUIRED' - | translate}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
- - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - +
+ + +
+
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
@@ -38,12 +47,12 @@
- + - {{'GENERAL.VALIDATION.REQUIRED' - | translate}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -66,7 +75,8 @@
- + diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts index 1709ec5a9..aa2716ee3 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts @@ -9,6 +9,7 @@ import { RequestItem } from '../../../../../core/query/request-item'; import { DatasetExternalAutocompleteService } from '../../../../../core/services/dataset/dataset-external-autocomplete.service'; import { SingleAutoCompleteConfiguration } from '../../../../../library/auto-complete/single/single-auto-complete-configuration'; import { VisibilityRulesService } from '../../visibility-rules/visibility-rules.service'; +import { MultipleAutoCompleteConfiguration } from '../../../../../library/auto-complete/multiple/multiple-auto-complete-configuration'; @Component({ selector: 'app-form-field', @@ -26,6 +27,7 @@ export class FormFieldComponent extends BaseComponent implements OnInit { // trackByFn = (index, item) => item ? item['id'] : null; public singleAutoCompleteConfiguration: SingleAutoCompleteConfiguration; + public multipleAutoCompleteConfiguration: MultipleAutoCompleteConfiguration; datasetProfileFieldViewStyleEnum = DatasetProfileFieldViewStyle; datasetProfileComboBoxTypeEnum = DatasetProfileComboBoxType; @@ -41,13 +43,24 @@ export class FormFieldComponent extends BaseComponent implements OnInit { // Setup autocomplete configuration if needed if (this.form.get('viewStyle').value.renderStyle === DatasetProfileFieldViewStyle.ComboBox && this.form.get('data').value.type === DatasetProfileComboBoxType.Autocomplete) { - this.singleAutoCompleteConfiguration = { - filterFn: this.searchFromAutocomplete.bind(this), - initialItems: (extraData) => this.searchFromAutocomplete(''), - displayFn: (item) => (item != null && item.length > 1) ? JSON.parse(item).label : item['label'], - titleFn: (item) => item['label'], - valueAssign: (item) => JSON.stringify(item) - }; + if (!(this.form.controls['data'].value.multiAutoComplete)) { + this.singleAutoCompleteConfiguration = { + filterFn: this.searchFromAutocomplete.bind(this), + initialItems: (extraData) => this.searchFromAutocomplete(''), + displayFn: (item) => (item != null && item.length > 1) ? JSON.parse(item).label : item['label'], + titleFn: (item) => item['label'], + valueAssign: (item) => JSON.stringify(item) + }; + } + else { + this.multipleAutoCompleteConfiguration = { + filterFn: this.searchFromAutocomplete.bind(this), + initialItems: (extraData) => this.searchFromAutocomplete(''), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'], + valueAssign: this._transformValue + } + } } // this.form = this.visibilityRulesService.getFormGroup(this.field.id); @@ -57,7 +70,22 @@ export class FormFieldComponent extends BaseComponent implements OnInit { this.visibilityRulesService.updateValueAndVisibility(this.form.get('id').value, item); }); } - // } + + _optionRemove(event) { + const array = JSON.parse(this.form.get('value').value); + if (array) { + const index = array.map(x => x.id).indexOf(event.id); + if (index >= 0) { + array.splice(index, 1); + } + this.form.get('value').patchValue(JSON.stringify(array)); + } + } + + _transformValue(item: any) { + if (!item) return []; + return item && typeof item === 'string' ? JSON.parse(item) : JSON.stringify(item); + } searchFromAutocomplete(query: string) { const autocompleteRequestItem: RequestItem = new RequestItem(); @@ -68,5 +96,4 @@ export class FormFieldComponent extends BaseComponent implements OnInit { autocompleteRequestItem.criteria.like = query; return this.datasetExternalAutocompleteService.queryAutocomplete(autocompleteRequestItem); } - }