diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index 1896f8ab17..20eb25d03c 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -26,6 +26,7 @@ "DATATABLE_LAZY": "Datatable (Lazy)", "FORM": "Form", "FORM_LIST": "Form List", + "FORM_LOADING": "Form Loading", "UPLOADER": "Uploader", "WEBSCRIPT": "Webscript", "TAG": "Tag", @@ -126,6 +127,13 @@ "STORE": "Store", "RESTORE": "Restore" }, + "FORM-LOADING": { + "FORM_DATA" : "Form Data", + "FORM_DATA_MESSAGE": "Type values to populate the form", + "TYPEAHEAD_PLACEHOLDER": "Typeahead", + "RADIO_PLACEHOLDER": "Radio Button", + "SELECT_PLACEHOLDER": "DropDown" + }, "LOGIN": { "CONTENT_SERVICES": "Content Services", "PROCESS_SERVICES": "Process Services", diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts index 267ec44d16..c739a355b9 100644 --- a/demo-shell/src/app/app.module.ts +++ b/demo-shell/src/app/app.module.ts @@ -20,6 +20,7 @@ import { SearchExtendedComponent } from './components/search/search-extended.com import { AboutComponent } from './components/about/about.component'; import { FormComponent } from './components/form/form.component'; import { FormListComponent } from './components/form/form-list.component'; +import { FormLoadingComponent } from './components/form/form-loading.component'; import { CustomSourcesComponent } from './components/files/custom-sources.component'; import { OverlayViewerComponent } from './components/overlay-viewer/overlay-viewer.component'; @@ -92,7 +93,8 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li TaskAttachmentsComponent, ProcessAttachmentsComponent, OverlayViewerComponent, - SharedLinkViewComponent + SharedLinkViewComponent, + FormLoadingComponent ], providers: [ { provide: AppConfigService, useClass: DebugAppConfigService }, diff --git a/demo-shell/src/app/app.routes.ts b/demo-shell/src/app/app.routes.ts index 0302f0a23b..30e317e3cc 100644 --- a/demo-shell/src/app/app.routes.ts +++ b/demo-shell/src/app/app.routes.ts @@ -45,6 +45,7 @@ import { CustomSourcesComponent } from './components/files/custom-sources.compon import { FormListComponent } from './components/form/form-list.component'; import { OverlayViewerComponent } from './components/overlay-viewer/overlay-viewer.component'; import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component'; +import { FormLoadingComponent } from './components/form/form-loading.component'; export const appRoutes: Routes = [ { path: 'login', component: LoginComponent }, @@ -167,6 +168,7 @@ export const appRoutes: Routes = [ { path: 'about', component: AboutComponent }, { path: 'form', component: FormComponent }, { path: 'form-list', component: FormListComponent }, + { path: 'form-loading', component: FormLoadingComponent }, { path: 'overlay-viewer', component: OverlayViewerComponent, diff --git a/demo-shell/src/app/components/app-layout/app-layout.component.ts b/demo-shell/src/app/components/app-layout/app-layout.component.ts index 74a0634768..546f0c92c7 100644 --- a/demo-shell/src/app/components/app-layout/app-layout.component.ts +++ b/demo-shell/src/app/components/app-layout/app-layout.component.ts @@ -38,6 +38,7 @@ export class AppLayoutComponent { { href: '/datatable-lazy', icon: 'view_module', title: 'APP_LAYOUT.DATATABLE_LAZY' }, { href: '/form', icon: 'poll', title: 'APP_LAYOUT.FORM' }, { href: '/form-list', icon: 'library_books', title: 'APP_LAYOUT.FORM_LIST' }, + { href: '/form-loading', icon: 'cached', title: 'APP_LAYOUT.FORM_LOADING' }, { href: '/uploader', icon: 'file_upload', title: 'APP_LAYOUT.UPLOADER' }, { href: '/webscript', icon: 'extension', title: 'APP_LAYOUT.WEBSCRIPT' }, { href: '/tag', icon: 'local_offer', title: 'APP_LAYOUT.TAG' }, diff --git a/demo-shell/src/app/components/form/demo-form.ts b/demo-shell/src/app/components/form/demo-form.ts index a56d3ca55c..5349303a93 100644 --- a/demo-shell/src/app/components/form/demo-form.ts +++ b/demo-shell/src/app/components/form/demo-form.ts @@ -2,14 +2,14 @@ * @license * Copyright 2016 Alfresco Software, Ltd. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -1450,4 +1450,271 @@ export class DemoForm { }; } + static getSimpleFormDefinition(): any { + return { + 'id': 1001, + 'name': 'SIMPLE_FORM_EXAMPLE', + 'description': '', + 'version': 1, + 'lastUpdatedBy': 2, + 'lastUpdatedByFullName': 'Test01 01Test', + 'lastUpdated': '2018-02-26T17:44:04.543+0000', + 'stencilSetId': 0, + 'referenceId': null, + 'taskId': '9999', + 'formDefinition': { + 'tabs': [], + 'fields': [ + { + 'fieldType': 'ContainerRepresentation', + 'id': '1519666726245', + 'name': 'Label', + 'type': 'container', + 'value': null, + 'required': false, + 'readOnly': false, + 'overrideId': false, + 'colspan': 1, + 'placeholder': null, + 'minLength': 0, + 'maxLength': 0, + 'minValue': null, + 'maxValue': null, + 'regexPattern': null, + 'optionType': null, + 'hasEmptyValue': null, + 'options': null, + 'restUrl': null, + 'restResponsePath': null, + 'restIdProperty': null, + 'restLabelProperty': null, + 'tab': null, + 'className': null, + 'dateDisplayFormat': null, + 'layout': null, + 'sizeX': 2, + 'sizeY': 1, + 'row': -1, + 'col': -1, + 'visibilityCondition': null, + 'numberOfColumns': 2, + 'fields': { + '1': [ + { + 'fieldType': 'RestFieldRepresentation', + 'id': 'typeahedField', + 'name': 'TypeahedField', + 'type': 'typeahead', + 'value': null, + 'required': false, + 'readOnly': false, + 'overrideId': false, + 'colspan': 1, + 'placeholder': null, + 'minLength': 0, + 'maxLength': 0, + 'minValue': null, + 'maxValue': null, + 'regexPattern': null, + 'optionType': null, + 'hasEmptyValue': null, + 'options': null, + 'restUrl': 'https://jsonplaceholder.typicode.com/users', + 'restResponsePath': null, + 'restIdProperty': 'id', + 'restLabelProperty': 'name', + 'tab': null, + 'className': null, + 'params': { + 'existingColspan': 1, + 'maxColspan': 2 + }, + 'dateDisplayFormat': null, + 'layout': { + 'row': -1, + 'column': -1, + 'colspan': 1 + }, + 'sizeX': 1, + 'sizeY': 1, + 'row': -1, + 'col': -1, + 'visibilityCondition': null, + 'endpoint': null, + 'requestHeaders': null + } + ], + '2': [ + { + 'fieldType': 'RestFieldRepresentation', + 'id': 'radioButton', + 'name': 'RadioButtons', + 'type': 'radio-buttons', + 'value': null, + 'required': false, + 'readOnly': false, + 'overrideId': false, + 'colspan': 1, + 'placeholder': null, + 'minLength': 0, + 'maxLength': 0, + 'minValue': null, + 'maxValue': null, + 'regexPattern': null, + 'optionType': null, + 'hasEmptyValue': null, + 'options': [ + { + 'id': 'option_1', + 'name': 'Option 1' + }, + { + 'id': 'option_2', + 'name': 'Option 2' + }, + { + 'id': 'option_3', + 'name': 'Option 3' + } + ], + 'restUrl': null, + 'restResponsePath': null, + 'restIdProperty': null, + 'restLabelProperty': null, + 'tab': null, + 'className': null, + 'params': { + 'existingColspan': 1, + 'maxColspan': 1 + }, + 'dateDisplayFormat': null, + 'layout': { + 'row': -1, + 'column': -1, + 'colspan': 1 + }, + 'sizeX': 1, + 'sizeY': 2, + 'row': -1, + 'col': -1, + 'visibilityCondition': null, + 'endpoint': null, + 'requestHeaders': null + } + ] + } + }, + { + 'fieldType': 'ContainerRepresentation', + 'id': '1519666735185', + 'name': 'Label', + 'type': 'container', + 'value': null, + 'required': false, + 'readOnly': false, + 'overrideId': false, + 'colspan': 1, + 'placeholder': null, + 'minLength': 0, + 'maxLength': 0, + 'minValue': null, + 'maxValue': null, + 'regexPattern': null, + 'optionType': null, + 'hasEmptyValue': null, + 'options': null, + 'restUrl': null, + 'restResponsePath': null, + 'restIdProperty': null, + 'restLabelProperty': null, + 'tab': null, + 'className': null, + 'dateDisplayFormat': null, + 'layout': null, + 'sizeX': 2, + 'sizeY': 1, + 'row': -1, + 'col': -1, + 'visibilityCondition': null, + 'numberOfColumns': 2, + 'fields': { + '1': [ + { + 'fieldType': 'RestFieldRepresentation', + 'id': 'selectBox', + 'name': 'SelectBox', + 'type': 'dropdown', + 'value': 'Choose one...', + 'required': false, + 'readOnly': false, + 'overrideId': false, + 'colspan': 1, + 'placeholder': null, + 'minLength': 0, + 'maxLength': 0, + 'minValue': null, + 'maxValue': null, + 'regexPattern': null, + 'optionType': 'manual', + 'hasEmptyValue': true, + 'options': [ + { + 'id': 'empty', + 'name': 'Choose one...' + }, + { + 'id': 'option_1', + 'name': '1' + }, + { + 'id': 'option_2', + 'name': '2' + }, + { + 'id': 'option_3', + 'name': '3' + } + ], + 'restUrl': null, + 'restResponsePath': null, + 'restIdProperty': null, + 'restLabelProperty': null, + 'tab': null, + 'className': null, + 'params': { + 'existingColspan': 1, + 'maxColspan': 2 + }, + 'dateDisplayFormat': null, + 'layout': { + 'row': -1, + 'column': -1, + 'colspan': 1 + }, + 'sizeX': 1, + 'sizeY': 1, + 'row': -1, + 'col': -1, + 'visibilityCondition': null, + 'endpoint': null, + 'requestHeaders': null + } + ], + '2': [] + } + } + ], + 'outcomes': [], + 'javascriptEvents': [], + 'className': '', + 'style': '', + 'customFieldTemplates': {}, + 'metadata': {}, + 'variables': [], + 'customFieldsValueInfo': {}, + 'gridsterForm': false + } + }; + } + } diff --git a/demo-shell/src/app/components/form/fake-form.service.ts b/demo-shell/src/app/components/form/fake-form.service.ts new file mode 100644 index 0000000000..5825b5981e --- /dev/null +++ b/demo-shell/src/app/components/form/fake-form.service.ts @@ -0,0 +1,54 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Observable } from 'rxjs/Observable'; +import { Injectable } from '@angular/core'; +import { + AppConfigService, AlfrescoApiService, EcmModelService, LogService, FormService, FormOutcomeEvent +} from '@alfresco/adf-core'; +import { Subject } from 'rxjs/Subject'; + +@Injectable() +export class FakeFormService extends FormService { + + executeOutcome = new Subject(); + + constructor(appConfig: AppConfigService, + ecmModelService: EcmModelService, + apiService: AlfrescoApiService, + protected logService: LogService) { + super(ecmModelService, apiService, logService); + } + + public getRestFieldValues(taskId: string, fieldId: string): Observable { + if (fieldId === 'typeahedField') { + return Observable.of([ + { 'id': '1', 'name': 'Leanne Graham' }, + { 'id': '2', 'name': 'Ervin Howell' }, + { 'id': '3', 'name': 'Clementine Bauch' }, + { 'id': '4', 'name': 'Patricia Lebsack' }, + { 'id': '5', 'name': 'Chelsey Dietrich' }, + { 'id': '6', 'name': 'Mrs. Dennis Schulist' }, + { 'id': '7', 'name': 'Kurtis Weissnat' }, + { 'id': '8', 'name': 'Nicholas Runolfsdottir V' }, + { 'id': '9', 'name': 'Glenna Reichert' }, + { 'id': '10', 'name': 'Clementina DuBuque' }]); + } else { + return super.getRestFieldValues(taskId, fieldId); + } + } +} diff --git a/demo-shell/src/app/components/form/form-loading.component.html b/demo-shell/src/app/components/form/form-loading.component.html new file mode 100644 index 0000000000..7acf94a397 --- /dev/null +++ b/demo-shell/src/app/components/form/form-loading.component.html @@ -0,0 +1,41 @@ +
+ + + + + {{'FORM-LOADING.FORM_DATA' | translate}} + + + {{'FORM-LOADING.FORM_DATA_MESSAGE' | translate}} + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/demo-shell/src/app/components/form/form-loading.component.scss b/demo-shell/src/app/components/form/form-loading.component.scss new file mode 100644 index 0000000000..3608df8b91 --- /dev/null +++ b/demo-shell/src/app/components/form/form-loading.component.scss @@ -0,0 +1,3 @@ +.form-container { + padding: 10px; +} diff --git a/demo-shell/src/app/components/form/form-loading.component.ts b/demo-shell/src/app/components/form/form-loading.component.ts new file mode 100644 index 0000000000..0d6d9deb72 --- /dev/null +++ b/demo-shell/src/app/components/form/form-loading.component.ts @@ -0,0 +1,60 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Inject, OnInit } from '@angular/core'; +import { FormModel, FormService, FormOutcomeEvent } from '@alfresco/adf-core'; +import { InMemoryFormService } from '../../services/in-memory-form.service'; +import { DemoForm } from './demo-form'; +import { FakeFormService } from './fake-form.service'; + +@Component({ + selector: 'app-form-loading', + templateUrl: 'form-loading.component.html', + styleUrls: ['form-loading.component.scss'], + providers: [ + { provide: FormService, useClass: FakeFormService }, + ] +}) +export class FormLoadingComponent implements OnInit { + + form: FormModel; + typeaheadFieldValue = ''; + selectFieldValue = ''; + radioButtonFieldValue = ''; + formattedData = {}; + + constructor(@Inject(FormService) private formService: InMemoryFormService) { + formService.executeOutcome.subscribe((formOutcomeEvent: FormOutcomeEvent) => { + formOutcomeEvent.preventDefault(); + }); + } + + ngOnInit() { + this.formattedData = {}; + const formDefinitionJSON: any = DemoForm.getSimpleFormDefinition(); + this.form = this.formService.parseForm(formDefinitionJSON); + } + + onLoadButtonClicked() { + this.formattedData = { + 'typeahedField': this.typeaheadFieldValue, + 'selectBox': this.selectFieldValue, + 'radioButton': this.radioButtonFieldValue + }; + } + +} diff --git a/lib/core/form/components/form.component.spec.ts b/lib/core/form/components/form.component.spec.ts index c4e894b3e1..981632a55c 100644 --- a/lib/core/form/components/form.component.spec.ts +++ b/lib/core/form/components/form.component.spec.ts @@ -822,14 +822,14 @@ describe('FormComponent', () => { let labelField = formFields.find(field => field.id === 'label'); let radioField = formFields.find(field => field.id === 'raduio'); expect(labelField.value).toBe('empty'); - expect(radioField.value).toBeNull(); + expect(radioField.value).toBe('option_1'); let formValues: any = {}; formValues.label = { - id: 'option_1', - name: 'test1' + id: 'option_2', + name: 'test2' }; - formValues.raduio = { id: 'option_1', name: 'Option 1' }; + formValues.raduio = { id: 'option_2', name: 'Option 2' }; let change = new SimpleChange(null, formValues, false); formComponent.data = formValues; formComponent.ngOnChanges({ 'data': change }); @@ -837,7 +837,25 @@ describe('FormComponent', () => { formFields = formComponent.form.getFormFields(); labelField = formFields.find(field => field.id === 'label'); radioField = formFields.find(field => field.id === 'raduio'); - expect(labelField.value).toBe('option_1'); - expect(radioField.value).toBe('option_1'); + expect(labelField.value).toBe('option_2'); + expect(radioField.value).toBe('option_2'); + }); + + it('should refresh radio buttons value when id is given to data', () => { + formComponent.form = new FormModel(fakeForm); + let formFields = formComponent.form.getFormFields(); + let radioFieldById = formFields.find(field => field.id === 'raduio'); + + expect(radioFieldById.value).toBe('option_2'); + + let formValues: any = {}; + formValues.raduio = 'option_3'; + let change = new SimpleChange(null, formValues, false); + formComponent.data = formValues; + formComponent.ngOnChanges({ 'data': change }); + + formFields = formComponent.form.getFormFields(); + radioFieldById = formFields.find(field => field.id === 'raduio'); + expect(radioFieldById.value).toBe('option_3'); }); }); diff --git a/lib/core/form/components/widgets/core/form-field-validator.spec.ts b/lib/core/form/components/widgets/core/form-field-validator.spec.ts index 03a601054c..c8099a7b55 100644 --- a/lib/core/form/components/widgets/core/form-field-validator.spec.ts +++ b/lib/core/form/components/widgets/core/form-field-validator.spec.ts @@ -82,9 +82,9 @@ describe('FormFieldValidator', () => { let field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.RADIO_BUTTONS, required: true, - value: 'one', options: [{ id: 'two', name: 'two' }] }); + field.value = 'one'; expect(validator.validate(field)).toBeFalsy(); }); diff --git a/lib/core/form/components/widgets/core/form-field.model.spec.ts b/lib/core/form/components/widgets/core/form-field.model.spec.ts index 2cdaa66045..95e8852cbe 100644 --- a/lib/core/form/components/widgets/core/form-field.model.spec.ts +++ b/lib/core/form/components/widgets/core/form-field.model.spec.ts @@ -266,8 +266,8 @@ describe('FormFieldModel', () => { let field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.RADIO_BUTTONS, options: [ - {id: 'opt1', value: 'Option 1'}, - {id: 'opt2', value: 'Option 2'} + {id: 'opt1', name: 'Option 1'}, + {id: 'opt2', name: 'Option 2'} ], value: 'opt2' }); @@ -319,8 +319,8 @@ describe('FormFieldModel', () => { id: 'radio-1', type: FormFieldTypes.RADIO_BUTTONS, options: [ - {id: 'opt1', value: 'Option 1'}, - {id: 'opt2', value: 'Option 2'} + {id: 'opt1', name: 'Option 1'}, + {id: 'opt2', name: 'Option 2'} ] }); @@ -334,8 +334,8 @@ describe('FormFieldModel', () => { id: 'radio-2', type: FormFieldTypes.RADIO_BUTTONS, options: [ - {id: 'opt1', value: 'Option 1'}, - {id: 'opt2', value: 'Option 2'} + {id: 'opt1', name: 'Option 1'}, + {id: 'opt2', name: 'Option 2'} ] }); diff --git a/lib/core/form/components/widgets/core/form-field.model.ts b/lib/core/form/components/widgets/core/form-field.model.ts index 4e4fd6a816..ec9cc3bd63 100644 --- a/lib/core/form/components/widgets/core/form-field.model.ts +++ b/lib/core/form/components/widgets/core/form-field.model.ts @@ -298,6 +298,8 @@ export class FormFieldModel extends FormWidgetModel { let emptyOption = json.options[0]; if (value === '' || value === emptyOption.id || value === emptyOption.name) { value = emptyOption.id; + } else if (value.id && value.name) { + value = value.id; } } } @@ -311,9 +313,12 @@ export class FormFieldModel extends FormWidgetModel { // Activiti has a bug with default radio button value where initial selection passed as `name` value // so try resolving current one with a fallback to first entry via name or id // TODO: needs to be reported and fixed at Activiti side - let entry: FormFieldOption[] = this.options.filter(opt => opt.id === value || opt.name === value); + let entry: FormFieldOption[] = this.options.filter(opt => + opt.id === value || opt.name === value || (value && (opt.id === value.id || opt.name === value.name))); if (entry.length > 0) { value = entry[0].id; + } else { + value = this.options[0] ? this.options[0].id : json.value; } } @@ -376,7 +381,7 @@ export class FormFieldModel extends FormWidgetModel { } break; case FormFieldTypes.TYPEAHEAD: - let taEntry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value); + let taEntry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value || opt.name === this.value); if (taEntry.length > 0) { this.form.values[this.id] = taEntry[0]; } else if (this.options.length > 0) { diff --git a/lib/core/form/components/widgets/core/form.model.ts b/lib/core/form/components/widgets/core/form.model.ts index 87834dcb43..ab63af27a6 100644 --- a/lib/core/form/components/widgets/core/form.model.ts +++ b/lib/core/form/components/widgets/core/form.model.ts @@ -268,10 +268,6 @@ export class FormModel { if (data[field.id]) { field.json.value = data[field.id]; field.value = field.parseValue(field.json); - if (field.type === FormFieldTypes.DROPDOWN || - field.type === FormFieldTypes.RADIO_BUTTONS) { - field.value = data[field.id].id; - } } } } diff --git a/lib/core/form/components/widgets/typeahead/typeahead.widget.ts b/lib/core/form/components/widgets/typeahead/typeahead.widget.ts index 86f5b51e43..8af38a9ce6 100644 --- a/lib/core/form/components/widgets/typeahead/typeahead.widget.ts +++ b/lib/core/form/components/widgets/typeahead/typeahead.widget.ts @@ -67,7 +67,7 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit let fieldValue = this.field.value; if (fieldValue) { - let toSelect = options.find(item => item.id === fieldValue); + let toSelect = options.find(item => item.id === fieldValue || item.name.toLocaleLowerCase() === fieldValue.toLocaleLowerCase()); if (toSelect) { this.value = toSelect.name; }