Adds "external autocomplete" field on DMP profile editor. (Issue #134)

This commit is contained in:
gkolokythas 2019-07-23 18:01:51 +03:00
parent f31435de94
commit 81c5a492c1
16 changed files with 209 additions and 69 deletions

View File

@ -14,22 +14,17 @@ import java.util.UUID;
public class Field implements XmlSerializable<Field> {
private UUID id;
private DMPProfileType type;
private DMPProfileFieldDataType dataType;
private Boolean required;
private String label;
private Object value;
private DmpProfileExternalAutoComplete externalAutocomplete;
public UUID getId() {
if (this.id == null) this.id = UUID.randomUUID();
return id;
}
public void setId(UUID id) {
this.id = id;
}
@ -37,7 +32,6 @@ public class Field implements XmlSerializable<Field> {
public Integer getType() {
return type.getValue();
}
public void setType(DMPProfileType type) {
this.type = type;
}
@ -45,7 +39,6 @@ public class Field implements XmlSerializable<Field> {
public Integer getDataType() {
return dataType.getValue();
}
public void setDataType(DMPProfileFieldDataType dataType) {
this.dataType = dataType;
}
@ -53,7 +46,6 @@ public class Field implements XmlSerializable<Field> {
public Boolean getRequired() {
return required;
}
public void setRequired(Boolean required) {
this.required = required;
}
@ -61,7 +53,6 @@ public class Field implements XmlSerializable<Field> {
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@ -69,12 +60,18 @@ public class Field implements XmlSerializable<Field> {
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
@Override
public DmpProfileExternalAutoComplete getExternalAutocomplete() {
return externalAutocomplete;
}
public void setExternalAutocomplete(DmpProfileExternalAutoComplete externalAutocomplete) {
this.externalAutocomplete = externalAutocomplete;
}
@Override
public Element toXml(Document doc) {
Element rootElement = doc.createElement("field");
rootElement.setAttribute("id", this.getId().toString());
@ -82,6 +79,9 @@ public class Field implements XmlSerializable<Field> {
rootElement.setAttribute("datatype", "" + this.dataType.getValue().toString());
rootElement.setAttribute("required", this.required.toString());
rootElement.setAttribute("label", this.label);
if (this.externalAutocomplete != null)
rootElement.appendChild(this.externalAutocomplete.toXml(doc));
return rootElement;
}
@ -92,6 +92,10 @@ public class Field implements XmlSerializable<Field> {
this.dataType = DMPProfileFieldDataType.fromInteger(Integer.parseInt(item.getAttribute("datatype")));
this.required = Boolean.valueOf(item.getAttribute("required"));
this.type = DMPProfileType.fromInteger(Integer.parseInt(item.getAttribute("type")));
Element optionElement = (Element) item.getElementsByTagName("externalAutocomplete").item(0);
if (optionElement != null) {
this.externalAutocomplete = new DmpProfileExternalAutoComplete().fromXml(optionElement);
}
return this;
}
}

View File

@ -4,7 +4,7 @@ package eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.types;
* Created by ikalyvas on 3/21/2018.
*/
public enum DMPProfileFieldDataType {
DATE(0), NUMBER(1), TEXT(2);
DATE(0), NUMBER(1), TEXT(2), EXTERNAL_AUTOCOMPLETE(3);
private Integer value;
@ -24,6 +24,8 @@ public enum DMPProfileFieldDataType {
return NUMBER;
case 2:
return TEXT;
case 3:
return EXTERNAL_AUTOCOMPLETE;
default:
throw new RuntimeException("Unsupported DMPProfileFieldData Type");
}

View File

@ -1,5 +1,6 @@
export enum DmpProfileFieldDataType {
Date = 0,
Number = 1,
Text = 2
}
Text = 2,
ExternalAutocomplete = 3
}

View File

@ -0,0 +1,8 @@
export interface DmpProfileExternalAutoCompleteField {
placeholder: string;
url: string;
optionsRoot: string;
multiAutoComplete: boolean;
label: string;
value: string;
}

View File

@ -1,5 +1,6 @@
import { DmpProfileFieldDataType } from '../../common/enum/dmp-profile-field-type';
import { DmpProfileType } from '../../common/enum/dmp-profile-type';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from '../../../ui/admin/dmp-profile/editor/external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
export interface DmpProfileField {
id: string;
@ -8,4 +9,5 @@ export interface DmpProfileField {
required: boolean;
label: string;
value: any;
externalAutocomplete?: DmpProfileExternalAutoCompleteFieldDataEditorModel;
}

View File

@ -0,0 +1,6 @@
import { BaseCriteria } from "../base-criteria";
export class DmpProfileExternalAutocompleteCriteria extends BaseCriteria {
public profileID: String;
public fieldID: String;
}

View File

@ -34,6 +34,7 @@ export class EnumUtils {
case DmpProfileFieldDataType.Date: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.DATE');
case DmpProfileFieldDataType.Number: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.NUMBER');
case DmpProfileFieldDataType.Text: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.TEXT');
case DmpProfileFieldDataType.ExternalAutocomplete: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.EXTERNAL-AUTOCOMPLETE');
}
}

View File

@ -8,6 +8,7 @@ import { DmpProfileEditorComponent } from "./editor/dmp-profile-editor.component
import { DmpProfileCriteriaComponent } from "./listing/criteria/dmp-profile-criteria.component";
import { DmpProfileListingComponent } from "./listing/dmp-profile-listing.component";
import { DialodConfirmationUploadDmpProfiles } from "./listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component";
import { DmpProfileExternalAutocompleteFieldEditorComponent } from "./editor/external-autocomplete/dmp-profile-external-autocomplete-field-editor.component";
@NgModule({
imports: [
@ -21,10 +22,11 @@ import { DialodConfirmationUploadDmpProfiles } from "./listing/criteria/dialog-c
DmpProfileEditorComponent,
DmpProfileListingComponent,
DmpProfileCriteriaComponent,
DialodConfirmationUploadDmpProfiles
DialodConfirmationUploadDmpProfiles,
DmpProfileExternalAutocompleteFieldEditorComponent
],
entryComponents: [
DialodConfirmationUploadDmpProfiles
]
})
export class DmpProfileModule { }
export class DmpProfileModule { }

View File

@ -15,8 +15,8 @@
<mat-card-content>
<div class="row pl-5">
<mat-form-field class="col-10">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.LABEL' | translate}}" type="text"
name="label" formControlName="label" required>
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.LABEL' | translate}}" type="text" name="label" formControlName="label"
required>
<mat-error *ngIf="formGroup.get('label').hasError('backendError')">
{{formGroup.get('label').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('label').hasError('required')">
@ -25,15 +25,12 @@
<h4 class="col-12">{{'DMP-PROFILE-EDITOR.FIELDS.TITLE' | translate}}</h4>
<div class="col-12">
<div class="row"
*ngFor="let fieldFormGroup of formGroup.get('definition').get('fields')['controls'];let i=index">
<div class="row" *ngFor="let fieldFormGroup of formGroup.get('definition').get('fields')['controls'];let i=index">
<div class="col-10">
<div class="row">
<mat-form-field class="col">
<input matInput
placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.LABEL' | translate}}"
type="text" name="label" [formControl]="fieldFormGroup.get('label')"
required>
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.LABEL' | translate}}" type="text" name="label" [formControl]="fieldFormGroup.get('label')"
required>
<mat-error *ngIf="fieldFormGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="fieldFormGroup.get('label').hasError('backendError')">
@ -41,11 +38,10 @@
</mat-error>
</mat-form-field>
<mat-form-field class="col">
<mat-select placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.TYPE' | translate}}"
[formControl]="fieldFormGroup.get('type')" required>
<mat-option *ngFor="let fieldType of getDMPProfileFieldTypeValues()"
[value]="fieldType">{{
getDMPProfileFieldTypeWithLanguage(fieldType) | translate}}</mat-option>
<mat-select placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.TYPE' | translate}}" [formControl]="fieldFormGroup.get('type')" required>
<mat-option *ngFor="let fieldType of getDMPProfileFieldTypeValues()" [value]="fieldType">
{{ getDMPProfileFieldTypeWithLanguage(fieldType) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="fieldFormGroup.get('type').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -54,13 +50,11 @@
</mat-error>
</mat-form-field>
<mat-form-field class="col">
<mat-select
placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.DATATYPE' | translate}}"
[formControl]="fieldFormGroup.get('dataType')" required>
<mat-option
*ngFor="let fieldDataType of getDMPProfileFieldDataTypeValues()"
[value]="fieldDataType">{{
getDMPProfileFieldDataTypeWithLanguage(fieldDataType) | translate}}</mat-option>
<mat-select placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.DATATYPE' | translate}}" [formControl]="fieldFormGroup.get('dataType')"
required>
<mat-option *ngFor="let fieldDataType of getDMPProfileFieldDataTypeValues()" [value]="fieldDataType">
{{ getDMPProfileFieldDataTypeWithLanguage(fieldDataType) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="fieldFormGroup.get('dataType').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -70,7 +64,12 @@
</mat-form-field>
<div class="centered-row-item col-auto">
<mat-checkbox [formControl]="fieldFormGroup.get('required')">
{{'DMP-PROFILE-EDITOR.FIELDS.REQUIRED' | translate}}</mat-checkbox>
{{'DMP-PROFILE-EDITOR.FIELDS.REQUIRED' | translate}}
</mat-checkbox>
</div>
<div *ngIf="isExternalAutocomplete(fieldFormGroup)" class="row">
<app-dmp-profile-external-autocomplete-field-editor-component [form]="fieldFormGroup">
</app-dmp-profile-external-autocomplete-field-editor-component>
</div>
</div>
</div>
@ -78,28 +77,22 @@
<div class="col-2">
<div class="row">
<div class="col-auto" *ngIf="!isNew">
<button mat-icon-button type="button"
(click)="removeField(i)" [disabled]="viewOnly">
<button mat-icon-button type="button" (click)="removeField(i)" [disabled]="viewOnly">
<mat-icon class="mat-24">delete</mat-icon>
</button>
</div>
<div class="col-auto"
*ngIf="!isNew && formGroup.get('status').value==0 && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()"
[disabled]="viewOnly">
<div class="col-auto" *ngIf="!isNew && formGroup.get('status').value==0 && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()" [disabled]="viewOnly">
<mat-icon class="mat-24">add</mat-icon>
</button>
</div>
<div class="col-auto" *ngIf="isNew && i != 0">
<button mat-mini-fab class="remove" type="button"
(click)="removeField(i)">
<button mat-mini-fab class="remove" type="button" (click)="removeField(i)">
<mat-icon class="mat-24">remove</mat-icon>
</button>
</div>
<div class="col-auto"
*ngIf="isNew && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()"
[disabled]="viewOnly">
<div class="col-auto" *ngIf="isNew && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()" [disabled]="viewOnly">
<mat-icon class="mat-24">add</mat-icon>
</button>
</div>
@ -109,18 +102,17 @@
</div>
</div>
<div class="row mt-4 pl-5 pr-4">
<div class="col-auto"><button mat-raised-button color="primary" (click)="cancel()"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.CANCEL' | translate}}</button></div>
<div class="col"></div>
<div class="col-auto" *ngIf="!isNew"><button mat-raised-button color="primary" type="button"
(click)="delete()">{{'DMP-PROFILE-EDITOR.ACTIONS.DELETE' | translate}}</button>
<div class="col-auto">
<button mat-raised-button color="primary" (click)="cancel()" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<button mat-raised-button *ngIf="formGroup.get('status').value!=1" class="col-auto"
color="primary" (click)="finalize()" [disabled]="!formGroup.valid"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<button mat-raised-button *ngIf="formGroup.get('status').value==1" class="col-auto"
color="primary" (click)="downloadXML()"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.DOWNLOAD-XML' | translate }}</button>
<div class="col"></div>
<div class="col-auto" *ngIf="!isNew">
<button mat-raised-button color="primary" type="button" (click)="delete()">{{'DMP-PROFILE-EDITOR.ACTIONS.DELETE' | translate}}</button>
</div>
<button mat-raised-button *ngIf="formGroup.get('status').value!=1" class="col-auto" color="primary" (click)="finalize()"
[disabled]="!formGroup.valid" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<button mat-raised-button *ngIf="formGroup.get('status').value==1" class="col-auto" color="primary" (click)="downloadXML()"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.DOWNLOAD-XML' | translate }}</button>
<div class="col-auto" *ngIf="!viewOnly">
<button mat-raised-button color="primary" type="submit" [disabled]="!formGroup.valid">
{{'DMP-PROFILE-EDITOR.ACTIONS.SAVE' | translate}}

View File

@ -17,6 +17,7 @@ import { EnumUtils } from '../../../../core/services/utilities/enum-utils.servic
import { DmpProfileEditorModel, DmpProfileFieldEditorModel } from './dmp-profile-editor.model';
import { BreadcrumbItem } from '../../../misc/breadcrumb/definition/breadcrumb-item';
import { Observable } from 'rxjs';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from './external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
@Component({
selector: 'app-dmp-profile-editor-component',
@ -96,8 +97,8 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
this.dmpProfileService.createDmp(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
@ -199,8 +200,8 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
this.dmpProfileService.createDmp(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
@ -240,4 +241,18 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
return filename;
}
isExternalAutocomplete(formGroup: FormGroup) {
if (formGroup.get('dataType').value == DmpProfileFieldDataType.ExternalAutocomplete) {
this.addControl(formGroup);
return true;
} else {
return false;
}
}
addControl(formGroup: FormGroup) {
if (formGroup.get('dataType').value == 3)
formGroup.addControl('externalAutocomplete', new DmpProfileExternalAutoCompleteFieldDataEditorModel().buildForm());
}
}

View File

@ -4,6 +4,7 @@ import { DmpProfileFieldDataType } from '../../../../core/common/enum/dmp-profil
import { DmpProfileType } from '../../../../core/common/enum/dmp-profile-type';
import { DmpProfile, DmpProfileDefinition } from '../../../../core/model/dmp-profile/dmp-profile';
import { DmpProfileField } from '../../../../core/model/dmp-profile/dmp-profile-field';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from './external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
export class DmpProfileEditorModel {
@ -67,6 +68,7 @@ export class DmpProfileFieldEditorModel {
public required = false;
public label: string;
public value: any;
public externalAutocomplete?: DmpProfileExternalAutoCompleteFieldDataEditorModel;
fromModel(item: DmpProfileField): DmpProfileFieldEditorModel {
this.type = item.type;
@ -75,16 +77,22 @@ export class DmpProfileFieldEditorModel {
this.label = item.label;
this.id = item.id;
this.value = item.value;
if (item.externalAutocomplete)
this.externalAutocomplete = new DmpProfileExternalAutoCompleteFieldDataEditorModel().fromModel(item.externalAutocomplete);
return this;
}
buildForm(): FormGroup {
return new FormBuilder().group({
const formGroup = new FormBuilder().group({
type: [this.type],
id: [this.id],
dataType: [this.dataType],
required: [this.required],
label: [this.label]
});
if (this.externalAutocomplete) {
formGroup.addControl('externalAutocomplete', this.externalAutocomplete.buildForm());
}
return formGroup;
}
}
}

View File

@ -0,0 +1,26 @@
<div class="container external-autocomplete">
<div class="row external-autocomplete-field" *ngIf="form.get('externalAutocomplete')">
<h5 style="font-weight: bold" class="col-auto">{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.TITLE' | translate}}</h5>
<mat-checkbox class="col-auto" [formControl]="this.form.get('externalAutocomplete').get('multiAutoComplete')">
{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.MULTIPLE-AUTOCOMPLETE' | translate}}
</mat-checkbox>
<mat-form-field class="col-12">
<input matInput type="string" placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.PLACEHOLDER' | translate}}"
[formControl]="form.get('externalAutocomplete').get('placeholder')">
</mat-form-field>
<mat-form-field class="col-md-12">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.URL' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('url')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.OPTIONS-ROOT' | translate}}"
[formControl]="this.form.get('externalAutocomplete').get('optionsRoot')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.LABEL' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('label')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.VALUE' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('value')">
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,11 @@
.external-autocomplete {
.centered-row-item {
align-items: center;
display: flex;
}
}
.external-autocomplete-field {
margin-left: auto;
background: aliceblue;
}

View File

@ -0,0 +1,17 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from './dmp-profile-external-autocomplete-field-editor.model';
@Component({
selector: 'app-dmp-profile-external-autocomplete-field-editor-component',
styleUrls: ['./dmp-profile-external-autocomplete-field-editor.component.scss'],
templateUrl: './dmp-profile-external-autocomplete-field-editor.component.html'
})
export class DmpProfileExternalAutocompleteFieldEditorComponent implements OnInit {
@Input() form: FormGroup
private externalAutocomplete: DmpProfileExternalAutoCompleteFieldDataEditorModel;
ngOnInit() {
}
}

View File

@ -0,0 +1,35 @@
import { FormGroup, FormBuilder } from "@angular/forms";
import { DmpProfileExternalAutoCompleteField } from "../../../../../core/model/dmp-profile/dmp-profile-external-autocomplete";
export class DmpProfileExternalAutoCompleteFieldDataEditorModel {
public placeholder: string;
public url: string;
public optionsRoot: string;
public multiAutoComplete: boolean = false;
public label: string;
public value: string;
buildForm(disabled: boolean = false, skipDisable: Array<String> = []): FormGroup {
const formGroup = new FormBuilder().group({
placeholder: [{ value: this.placeholder, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.placeholder')) }],
url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.url')) }],
optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.optionsRoot')) }],
multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.multiAutoComplete')) }],
label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.label')) }],
value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.value')) }],
});
return formGroup;
}
fromModel(item: DmpProfileExternalAutoCompleteField): DmpProfileExternalAutoCompleteFieldDataEditorModel {
this.placeholder = item.placeholder;
this.url = item.url;
this.optionsRoot = item.optionsRoot;
this.multiAutoComplete = item.multiAutoComplete;
this.label = item.label;
this.value = item.value;
return this;
}
}

View File

@ -494,7 +494,16 @@
"LABEL": "Name",
"TYPE": "Type",
"DATATYPE": "Data Type",
"REQUIRED": "Required"
"REQUIRED": "Required",
"EXTERNAL-AUTOCOMPLETE": {
"TITLE": "Autocomplete Data",
"MULTIPLE-AUTOCOMPLETE": "Multiple Autocomplete",
"PLACEHOLDER": "Input Placeholder",
"URL": "Url",
"OPTIONS-ROOT": "Options Root",
"LABEL": "Label",
"VALUE": "Value"
}
},
"ACTIONS": {
"SAVE": "Save",
@ -713,7 +722,8 @@
"DATA-TYPE": {
"DATE": "Date",
"NUMBER": "Number",
"TEXT": "Text"
"TEXT": "Text",
"EXTERNAL-AUTOCOMPLETE": "External AutoComplete"
},
"TYPE": {
"INPUT": "Input"