[ADF-4468] FormCloud - Be able to display the value of a form variables (#4676)

* Fix the value passed as processDefinition
Be sure we fetch the form variables

* * Rename formId to formKey.

* * Reverted form Changes

* * Fixed failing unit test

* Fix the value passed as processDefinition
Be sure we fetch the form variables

* * Fixed failing unit test

* * Rename formId to formKey.

* * Reverted form Changes

* * Fixed failing unit test

* Fix form representation structure

* Refactor form models

* Change tslint config

* Fix lint

* Refactor form models

* Remove duplicated export

* improve variable names

* Update form mock
This commit is contained in:
Maurizio Vitale 2019-06-12 12:50:10 +01:00 committed by Eugenio Romano
parent 051e8df091
commit 63f00e21cf
20 changed files with 916 additions and 924 deletions

View File

@ -54,8 +54,7 @@ export class FormCloudDemoComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
const formDefinitionJSON = this.automationService.forms.getFormCloudDefinition(); this.formConfig = JSON.stringify(this.automationService.forms.getFormCloudDefinition());
this.formConfig = JSON.stringify(formDefinitionJSON);
this.parseForm(); this.parseForm();
} }

View File

@ -62,8 +62,7 @@ export class FormComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
const formDefinitionJSON: any = this.automationService.forms.getFormDefinition(); this.formConfig = JSON.stringify(this.automationService.forms.getFormDefinition());
this.formConfig = JSON.stringify(formDefinitionJSON);
this.parseForm(); this.parseForm();
} }

View File

@ -30,7 +30,6 @@ export abstract class FormBaseModel {
static START_PROCESS_OUTCOME: string = '$startProcess'; static START_PROCESS_OUTCOME: string = '$startProcess';
json: any; json: any;
isValid: boolean;
values: FormValues = {}; values: FormValues = {};
tabs: TabModel[] = []; tabs: TabModel[] = [];
@ -41,6 +40,8 @@ export abstract class FormBaseModel {
readOnly: boolean = false; readOnly: boolean = false;
taskName; taskName;
isValid: boolean = true;
hasTabs(): boolean { hasTabs(): boolean {
return this.tabs && this.tabs.length > 0; return this.tabs && this.tabs.length > 0;
} }
@ -77,8 +78,11 @@ export abstract class FormBaseModel {
return formFieldModel; return formFieldModel;
} }
markAsInvalid() {
this.isValid = false;
}
abstract validateForm(); abstract validateForm();
abstract validateField(field: FormFieldModel); abstract validateField(field: FormFieldModel);
abstract onFormFieldChanged(field: FormFieldModel); abstract onFormFieldChanged(field: FormFieldModel);
abstract markAsInvalid();
} }

View File

@ -68,7 +68,7 @@ export class FormFieldModel extends FormWidgetModel {
visibilityCondition: WidgetVisibilityModel = null; visibilityCondition: WidgetVisibilityModel = null;
enableFractions: boolean = false; enableFractions: boolean = false;
currency: string = null; currency: string = null;
dateDisplayFormat: string = this.dateDisplayFormat || this.defaultDateFormat; dateDisplayFormat: string = this.defaultDateFormat;
// container model members // container model members
numberOfColumns: number = 1; numberOfColumns: number = 1;

View File

@ -35,7 +35,10 @@ describe('FormModel', () => {
}); });
it('should store original json', () => { it('should store original json', () => {
const json = {}; const json = {
id: '<id>',
name: '<name>'
};
const form = new FormModel(json); const form = new FormModel(json);
expect(form.json).toBe(json); expect(form.json).toBe(json);
}); });

View File

@ -43,11 +43,6 @@ export class FormModel extends FormBaseModel {
readonly taskId: string; readonly taskId: string;
readonly taskName: string = FormModel.UNSET_TASK_NAME; readonly taskName: string = FormModel.UNSET_TASK_NAME;
processDefinitionId: string; processDefinitionId: string;
private _isValid: boolean = true;
get isValid(): boolean {
return this._isValid;
}
customFieldTemplates: FormFieldTemplates = {}; customFieldTemplates: FormFieldTemplates = {};
fieldValidators: FormFieldValidator[] = [...FORM_FIELD_VALIDATORS]; fieldValidators: FormFieldValidator[] = [...FORM_FIELD_VALIDATORS];
@ -55,33 +50,33 @@ export class FormModel extends FormBaseModel {
processVariables: any; processVariables: any;
constructor(json?: any, formValues?: FormValues, readOnly: boolean = false, protected formService?: FormService) { constructor(formRepresentationJSON?: any, formValues?: FormValues, readOnly: boolean = false, protected formService?: FormService) {
super(); super();
this.readOnly = readOnly; this.readOnly = readOnly;
if (json) { if (formRepresentationJSON) {
this.json = json; this.json = formRepresentationJSON;
this.id = json.id; this.id = formRepresentationJSON.id;
this.name = json.name; this.name = formRepresentationJSON.name;
this.taskId = json.taskId; this.taskId = formRepresentationJSON.taskId;
this.taskName = json.taskName || json.name || FormModel.UNSET_TASK_NAME; this.taskName = formRepresentationJSON.taskName || formRepresentationJSON.name || FormModel.UNSET_TASK_NAME;
this.processDefinitionId = json.processDefinitionId; this.processDefinitionId = formRepresentationJSON.processDefinitionId;
this.customFieldTemplates = json.customFieldTemplates || {}; this.customFieldTemplates = formRepresentationJSON.customFieldTemplates || {};
this.selectedOutcome = json.selectedOutcome || {}; this.selectedOutcome = formRepresentationJSON.selectedOutcome || {};
this.className = json.className || ''; this.className = formRepresentationJSON.className || '';
const tabCache: FormWidgetModelCache<TabModel> = {}; const tabCache: FormWidgetModelCache<TabModel> = {};
this.processVariables = json.processVariables; this.processVariables = formRepresentationJSON.processVariables;
this.tabs = (json.tabs || []).map((t) => { this.tabs = (formRepresentationJSON.tabs || []).map((t) => {
const model = new TabModel(this, t); const model = new TabModel(this, t);
tabCache[model.id] = model; tabCache[model.id] = model;
return model; return model;
}); });
this.fields = this.parseRootFields(json); this.fields = this.parseRootFields(formRepresentationJSON);
if (formValues) { if (formValues) {
this.loadData(formValues); this.loadData(formValues);
@ -97,7 +92,7 @@ export class FormModel extends FormBaseModel {
} }
} }
if (json.fields) { if (formRepresentationJSON.fields) {
const saveOutcome = new FormOutcomeModel(this, { const saveOutcome = new FormOutcomeModel(this, {
id: FormModel.SAVE_OUTCOME, id: FormModel.SAVE_OUTCOME,
name: 'SAVE', name: 'SAVE',
@ -114,7 +109,7 @@ export class FormModel extends FormBaseModel {
isSystem: true isSystem: true
}); });
const customOutcomes = (json.outcomes || []).map((obj) => new FormOutcomeModel(this, obj)); const customOutcomes = (formRepresentationJSON.outcomes || []).map((obj) => new FormOutcomeModel(this, obj));
this.outcomes = [saveOutcome].concat( this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome] customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]
@ -132,10 +127,6 @@ export class FormModel extends FormBaseModel {
} }
} }
markAsInvalid() {
this._isValid = false;
}
/** /**
* Validates entire form and all form fields. * Validates entire form and all form fields.
* *
@ -153,10 +144,10 @@ export class FormModel extends FormBaseModel {
} }
} }
this._isValid = errorsField.length > 0 ? false : true; this.isValid = errorsField.length > 0 ? false : true;
if (this.formService) { if (this.formService) {
validateFormEvent.isValid = this._isValid; validateFormEvent.isValid = this.isValid;
validateFormEvent.errorsField = errorsField; validateFormEvent.errorsField = errorsField;
this.formService.validateForm.next(validateFormEvent); this.formService.validateForm.next(validateFormEvent);
} }
@ -181,7 +172,7 @@ export class FormModel extends FormBaseModel {
} }
if (!validateFieldEvent.isValid) { if (!validateFieldEvent.isValid) {
this._isValid = false; this.markAsInvalid();
return; return;
} }
@ -190,7 +181,7 @@ export class FormModel extends FormBaseModel {
} }
if (!field.validate()) { if (!field.validate()) {
this._isValid = false; this.markAsInvalid();
} }
this.validateForm(); this.validateForm();

View File

@ -16,6 +16,7 @@
*/ */
export * from './components/form-base.component'; export * from './components/form-base.component';
export * from './components/form-base.model';
export * from './components/form-list.component'; export * from './components/form-list.component';
export * from './components/widgets/content/content.widget'; export * from './components/widgets/content/content.widget';
export * from './components/form-renderer.component'; export * from './components/form-renderer.component';

View File

@ -215,14 +215,7 @@ export class WidgetVisibilityService {
} }
private getFormVariables(form: FormModel): any[] { private getFormVariables(form: FormModel): any[] {
let variables; return form.json.variables;
if (form.json.formRepresentation) {
variables = form.json.formRepresentation.formDefinition.variables;
} else {
variables = form.json.variables;
}
return variables;
} }
private getProcessVariableValue(name: string, processVarList: TaskProcessVariableModel[]): string { private getProcessVariableValue(name: string, processVarList: TaskProcessVariableModel[]): string {

View File

@ -32,7 +32,7 @@
* limitations under the License. * limitations under the License.
*/ */
export class DemoForms { export class DemoForm {
easyForm = { easyForm = {
'id': 1001, 'id': 1001,

View File

@ -20,13 +20,14 @@ import { AppConfigService } from '../app-config/app-config.service';
import { AlfrescoApiService } from '../services/alfresco-api.service'; import { AlfrescoApiService } from '../services/alfresco-api.service';
import { StorageService } from './storage.service'; import { StorageService } from './storage.service';
import { UserPreferencesService } from './user-preferences.service'; import { UserPreferencesService } from './user-preferences.service';
import { DemoForms } from '../mock/form/demo-form.mock'; import { DemoForm } from '../mock/form/demo-form.mock';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class CoreAutomationService { export class CoreAutomationService {
forms = new DemoForms();
public forms = new DemoForm();
constructor(private appConfigService: AppConfigService, constructor(private appConfigService: AppConfigService,
private alfrescoApiService: AlfrescoApiService, private alfrescoApiService: AlfrescoApiService,

View File

@ -19,13 +19,16 @@ import { SimpleChange, DebugElement, CUSTOM_ELEMENTS_SCHEMA, Component } from '@
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Observable, of, throwError } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService, import {
setupTestBed, AppConfigService, FormRenderingService } from '@alfresco/adf-core'; FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService,
setupTestBed, AppConfigService, FormRenderingService
} from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloudComponent } from './form-cloud.component'; import { FormCloudComponent } from './form-cloud.component';
import { FormCloud } from '../models/form-cloud.model'; import { FormCloud } from '../models/form-cloud.model';
import { cloudFormMock } from '../mocks/cloud-form.mock'; import { cloudFormMock } from '../mocks/cloud-form.mock';
import { FormCloudRepresentation } from '../models/form-cloud-representation.model';
describe('FormCloudComponent', () => { describe('FormCloudComponent', () => {
@ -120,7 +123,7 @@ describe('FormCloudComponent', () => {
}); });
it('should show [custom-outcome] button with readOnly form and selected custom-outcome', () => { it('should show [custom-outcome] button with readOnly form and selected custom-outcome', () => {
const formModel = new FormCloud({formRepresentation: {formDefinition: {selectedOutcome: 'custom-outcome'}}}); const formModel = new FormCloud({ selectedOutcome: 'custom-outcome' });
formModel.readOnly = true; formModel.readOnly = true;
formComponent.form = formModel; formComponent.form = formModel;
let outcome = new FormOutcomeModel(<any> formModel, { id: '$customoutome', name: 'custom-outcome' }); let outcome = new FormOutcomeModel(<any> formModel, { id: '$customoutome', name: 'custom-outcome' });
@ -156,7 +159,7 @@ describe('FormCloudComponent', () => {
it('should get task variables if a task form is rendered', () => { it('should get task variables if a task form is rendered', () => {
spyOn(formCloudService, 'getTaskForm').and.callFake((currentTaskId) => { spyOn(formCloudService, 'getTaskForm').and.callFake((currentTaskId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: { taskId: currentTaskId }}); observer.next({ formRepresentation: { taskId: currentTaskId } });
observer.complete(); observer.complete();
}); });
}); });
@ -164,7 +167,7 @@ describe('FormCloudComponent', () => {
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTask').and.callFake((currentTaskId) => { spyOn(formCloudService, 'getTask').and.callFake((currentTaskId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: { taskId: currentTaskId }}); observer.next({ formRepresentation: { taskId: currentTaskId } });
observer.complete(); observer.complete();
}); });
}); });
@ -210,7 +213,7 @@ describe('FormCloudComponent', () => {
}); });
it('should refresh visibility when the form is loaded', () => { it('should refresh visibility when the form is loaded', () => {
spyOn(formCloudService, 'getForm').and.returnValue(of({formRepresentation: {formDefinition: {}}})); spyOn(formCloudService, 'getForm').and.returnValue(of({ formRepresentation: {} }));
const formId = '123'; const formId = '123';
const appName = 'test-app'; const appName = 'test-app';
@ -388,7 +391,7 @@ describe('FormCloudComponent', () => {
spyOn(formCloudService, 'getTask').and.returnValue(of({})); spyOn(formCloudService, 'getTask').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formCloudService, 'getTaskForm').and.returnValue(of({formRepresentation: {taskId: taskId, formDefinition: {selectedOutcome: 'custom-outcome'}}})); spyOn(formCloudService, 'getTaskForm').and.returnValue(of({ taskId: taskId, selectedOutcome: 'custom-outcome' }));
formComponent.formLoaded.subscribe(() => { formComponent.formLoaded.subscribe(() => {
expect(formCloudService.getTaskForm).toHaveBeenCalledWith(appName, taskId); expect(formCloudService.getTaskForm).toHaveBeenCalledWith(appName, taskId);
@ -421,7 +424,7 @@ describe('FormCloudComponent', () => {
it('should fetch and parse form definition by id', (done) => { it('should fetch and parse form definition by id', (done) => {
spyOn(formCloudService, 'getForm').and.callFake((currentAppName, currentFormId) => { spyOn(formCloudService, 'getForm').and.callFake((currentAppName, currentFormId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: {id: currentFormId, formDefinition: {}}}); observer.next({ id: currentFormId });
observer.complete(); observer.complete();
}); });
}); });
@ -469,16 +472,12 @@ describe('FormCloudComponent', () => {
const processInstanceId = '333-444'; const processInstanceId = '333-444';
const formModel = new FormCloud({ const formModel = new FormCloud({
formRepresentation: { id: '23',
id: '23', taskId: taskId,
taskId: taskId, fields: [
formDefinition: { { id: 'field1' },
fields: [ { id: 'field2' }
{ id: 'field1' }, ]
{ id: 'field2' }
]
}
}
}); });
formComponent.form = formModel; formComponent.form = formModel;
formComponent.taskId = taskId; formComponent.taskId = taskId;
@ -500,16 +499,12 @@ describe('FormCloudComponent', () => {
const taskId = '123-223'; const taskId = '123-223';
const appName = 'test-app'; const appName = 'test-app';
const formModel = new FormCloud({ const formModel = new FormCloud({
formRepresentation: { id: '23',
id: '23', taskId: taskId,
taskId: taskId, fields: [
formDefinition: { { id: 'field1' },
fields: [ { id: 'field2' }
{ id: 'field1' }, ]
{ id: 'field2' }
]
}
}
}); });
formComponent.form = formModel; formComponent.form = formModel;
formComponent.taskId = taskId; formComponent.taskId = taskId;
@ -572,16 +567,12 @@ describe('FormCloudComponent', () => {
const processInstanceId = '333-444'; const processInstanceId = '333-444';
const formModel = new FormCloud({ const formModel = new FormCloud({
formRepresentation: { id: '23',
id: '23', taskId: taskId,
taskId: taskId, fields: [
formDefinition: { { id: 'field1' },
fields: [ { id: 'field2' }
{ id: 'field1' }, ]
{ id: 'field2' }
]
}
}
}); });
formComponent.form = formModel; formComponent.form = formModel;
@ -600,14 +591,10 @@ describe('FormCloudComponent', () => {
it('should parse form from json', () => { it('should parse form from json', () => {
const form = formComponent.parseForm({ const form = formComponent.parseForm({
formRepresentation: { id: '1',
id: '1', fields: [
formDefinition: { { id: 'field1', type: FormFieldTypes.CONTAINER }
fields: [ ]
{ id: 'field1', type: FormFieldTypes.CONTAINER }
]
}
}
}); });
expect(form).toBeDefined(); expect(form).toBeDefined();
@ -619,7 +606,7 @@ describe('FormCloudComponent', () => {
it('should provide outcomes for form definition', () => { it('should provide outcomes for form definition', () => {
spyOn(formComponent, 'getFormDefinitionOutcomes').and.callThrough(); spyOn(formComponent, 'getFormDefinitionOutcomes').and.callThrough();
const form = formComponent.parseForm({ formRepresentation: { id: 1, formDefinition: {}}}); const form = formComponent.parseForm({ id: '1' });
expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form); expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form);
}); });
@ -754,6 +741,7 @@ describe('FormCloudComponent', () => {
it('should refresh form values when data is changed', (done) => { it('should refresh form values when data is changed', (done) => {
formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock))); formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(cloudFormMock)));
let formFields = formComponent.form.getFormFields(); let formFields = formComponent.form.getFormFields();
let labelField = formFields.find((field) => field.id === 'text1'); let labelField = formFields.find((field) => field.id === 'text1');
@ -761,12 +749,12 @@ describe('FormCloudComponent', () => {
expect(labelField.value).toBeNull(); expect(labelField.value).toBeNull();
expect(radioField.value).toBeUndefined(); expect(radioField.value).toBeUndefined();
const formValues: any[] = [{name: 'text1', value: 'test'}, {name: 'number1', value: 99}]; const formValues: any[] = [{ name: 'text1', value: 'test' }, { name: 'number1', value: 99 }];
const change = new SimpleChange(null, formValues, false); const change = new SimpleChange(null, formValues, false);
formComponent.data = formValues; formComponent.data = formValues;
formComponent.formLoaded.subscribe( (form) => { formComponent.formLoaded.subscribe((form) => {
formFields = form.getFormFields(); formFields = form.getFormFields();
labelField = formFields.find((field) => field.id === 'text1'); labelField = formFields.find((field) => field.id === 'text1');
radioField = formFields.find((field) => field.id === 'number1'); radioField = formFields.find((field) => field.id === 'number1');
@ -782,10 +770,11 @@ describe('FormCloudComponent', () => {
it('should refresh radio buttons value when id is given to data', () => { it('should refresh radio buttons value when id is given to data', () => {
formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock))); formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock)));
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(cloudFormMock)));
let formFields = formComponent.form.getFormFields(); let formFields = formComponent.form.getFormFields();
let radioFieldById = formFields.find((field) => field.id === 'radiobuttons1'); let radioFieldById = formFields.find((field) => field.id === 'radiobuttons1');
const formValues: any[] = [{name: 'radiobuttons1', value: 'option_2'}]; const formValues: any[] = [{ name: 'radiobuttons1', value: 'option_2' }];
const change = new SimpleChange(null, formValues, false); const change = new SimpleChange(null, formValues, false);
formComponent.data = formValues; formComponent.data = formValues;
formComponent.ngOnChanges({ 'data': change }); formComponent.ngOnChanges({ 'data': change });
@ -797,7 +786,7 @@ describe('FormCloudComponent', () => {
}); });
@Component({ @Component({
selector: 'adf-form-cloud-with-custom-outcomes', selector: 'adf-cloud-form-with-custom-outcomes',
template: ` template: `
<adf-cloud-form #adfCloudForm> <adf-cloud-form #adfCloudForm>
<adf-cloud-form-custom-outcomes> <adf-cloud-form-custom-outcomes>
@ -813,7 +802,7 @@ describe('FormCloudComponent', () => {
class FormCloudWithCustomOutComesComponent { class FormCloudWithCustomOutComesComponent {
onButtonClick() {} onButtonClick() { }
} }
describe('FormCloudWithCustomOutComesComponent', () => { describe('FormCloudWithCustomOutComesComponent', () => {

View File

@ -92,6 +92,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
nodeId: string; nodeId: string;
formCloudRepresentationJSON: any;
protected onDestroy$ = new Subject<boolean>(); protected onDestroy$ = new Subject<boolean>();
@ -181,7 +182,8 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
.subscribe( .subscribe(
(data) => { (data) => {
this.data = data[1]; this.data = data[1];
const parsedForm = this.parseForm(data[0]); this.formCloudRepresentationJSON = data[0];
const parsedForm = this.parseForm(this.formCloudRepresentationJSON);
this.visibilityService.refreshVisibility(<any> parsedForm); this.visibilityService.refreshVisibility(<any> parsedForm);
parsedForm.validateForm(); parsedForm.validateForm();
this.form = parsedForm; this.form = parsedForm;
@ -268,10 +270,10 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
} }
} }
parseForm(json: any): FormCloud { parseForm(formCloudRepresentationJSON: any): FormCloud {
if (json) { if (formCloudRepresentationJSON) {
const form = new FormCloud(json, this.data, this.readOnly, this.formCloudService); const form = new FormCloud(formCloudRepresentationJSON, this.data, this.readOnly, this.formCloudService);
if (!json.formRepresentation.formDefinition || !json.formRepresentation.formDefinition.fields) { if (!form || !form.fields.length) {
form.outcomes = this.getFormDefinitionOutcomes(form); form.outcomes = this.getFormDefinitionOutcomes(form);
} }
if (this.fieldValidators && this.fieldValidators.length > 0) { if (this.fieldValidators && this.fieldValidators.length > 0) {
@ -299,7 +301,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
} }
private refreshFormData() { private refreshFormData() {
this.form = this.parseForm(this.form.json); this.form = this.parseForm(this.formCloudRepresentationJSON);
this.onFormLoaded(this.form); this.onFormLoaded(this.form);
this.onFormDataRefreshed(this.form); this.onFormDataRefreshed(this.form);
} }

View File

@ -0,0 +1,51 @@
/*!
* @license
* Copyright 2019 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.
*/
export class FormCloudRepresentation {
id?: string;
name?: string;
description?: string;
version?: number;
tabs?: any[];
fields?: any[];
outcomes?: any[];
metadata?: any;
variables?: any[];
taskId?: string;
taskName?: string;
processDefinitionId?: string;
processInstanceId?: string;
selectedOutcome?: string;
constructor(obj?: any) {
this.id = obj.id || null;
this.name = obj.name || null;
this.description = obj.description || null;
this.version = obj.version || null;
this.tabs = obj.tabs || null;
this.fields = obj.fields || null;
this.outcomes = obj.outcomes || null;
this.metadata = obj.metadata || null;
this.variables = obj.variables || null;
this.taskId = obj.taskId || null;
this.taskName = obj.taskName || null;
this.processDefinitionId = obj.processDefinitionId || null;
this.processInstanceId = obj.processInstanceId || null;
this.selectedOutcome = obj.selectedOutcome || null;
}
}

View File

@ -18,6 +18,7 @@
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloud } from './form-cloud.model'; import { FormCloud } from './form-cloud.model';
import { TabModel, FormFieldModel, ContainerModel, FormOutcomeModel, FormFieldTypes, AppConfigService } from '@alfresco/adf-core'; import { TabModel, FormFieldModel, ContainerModel, FormOutcomeModel, FormFieldTypes, AppConfigService } from '@alfresco/adf-core';
import { FormCloudRepresentation } from './form-cloud-representation.model';
describe('FormCloud', () => { describe('FormCloud', () => {
@ -28,33 +29,33 @@ describe('FormCloud', () => {
}); });
it('should store original json', () => { it('should store original json', () => {
const json = {formRepresentation: {formDefinition: {}}}; const formRepresentation = {fields: []};
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.json).toBe(json); expect(form.json).toEqual(formRepresentation);
}); });
it('should setup properties with json', () => { it('should setup properties with json', () => {
const json = {formRepresentation: { const formRepresentation: FormCloudRepresentation = {
id: '<id>', id: '<id>',
name: '<name>', name: '<name>',
taskId: '<task-id>', taskId: '<task-id>',
taskName: '<task-name>' taskName: '<task-name>'
}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
Object.keys(json).forEach((key) => { Object.keys(formRepresentation).forEach((key) => {
expect(form[key]).toEqual(form[key]); expect(form[key]).toEqual(form[key]);
}); });
}); });
it('should take form name when task name is missing', () => { it('should take form name when task name is missing', () => {
const json = {formRepresentation: { const formRepresentation = {
id: '<id>', id: '<id>',
name: '<name>', name: '<name>',
formDefinition: {} formDefinition: {}
}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.taskName).toBe(json.formRepresentation.name); expect(form.taskName).toBe(formRepresentation.name);
}); });
it('should set readonly state from params', () => { it('should set readonly state from params', () => {
@ -103,21 +104,21 @@ describe('FormCloud', () => {
}); });
it('should parse tabs', () => { it('should parse tabs', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
tabs: [ tabs: [
{ id: 'tab1' }, { id: 'tab1' },
{ id: 'tab2' } { id: 'tab2' }
] ]
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.tabs.length).toBe(2); expect(form.tabs.length).toBe(2);
expect(form.tabs[0].id).toBe('tab1'); expect(form.tabs[0].id).toBe('tab1');
expect(form.tabs[1].id).toBe('tab2'); expect(form.tabs[1].id).toBe('tab2');
}); });
it('should parse fields', () => { it('should parse fields', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
fields: [ fields: [
{ {
id: 'field1', id: 'field1',
@ -128,26 +129,26 @@ describe('FormCloud', () => {
type: FormFieldTypes.CONTAINER type: FormFieldTypes.CONTAINER
} }
] ]
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.fields.length).toBe(2); expect(form.fields.length).toBe(2);
expect(form.fields[0].id).toBe('field1'); expect(form.fields[0].id).toBe('field1');
expect(form.fields[1].id).toBe('field2'); expect(form.fields[1].id).toBe('field2');
}); });
it('should convert missing fields to empty collection', () => { it('should convert missing fields to empty collection', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
fields: null fields: null
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.fields).toBeDefined(); expect(form.fields).toBeDefined();
expect(form.fields.length).toBe(0); expect(form.fields.length).toBe(0);
}); });
it('should put fields into corresponding tabs', () => { it('should put fields into corresponding tabs', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
tabs: [ tabs: [
{ id: 'tab1' }, { id: 'tab1' },
{ id: 'tab2' } { id: 'tab2' }
@ -158,9 +159,9 @@ describe('FormCloud', () => {
{ id: 'field3', tab: 'tab1', type: FormFieldTypes.DYNAMIC_TABLE }, { id: 'field3', tab: 'tab1', type: FormFieldTypes.DYNAMIC_TABLE },
{ id: 'field4', tab: 'missing-tab', type: FormFieldTypes.DYNAMIC_TABLE } { id: 'field4', tab: 'missing-tab', type: FormFieldTypes.DYNAMIC_TABLE }
] ]
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.tabs.length).toBe(2); expect(form.tabs.length).toBe(2);
expect(form.fields.length).toBe(4); expect(form.fields.length).toBe(4);
@ -175,13 +176,13 @@ describe('FormCloud', () => {
}); });
it('should create standard form outcomes', () => { it('should create standard form outcomes', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
fields: [ fields: [
{ id: 'container1' } { id: 'container1' }
] ]
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.outcomes.length).toBe(3); expect(form.outcomes.length).toBe(3);
expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME); expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME);
@ -195,24 +196,24 @@ describe('FormCloud', () => {
}); });
it('should create outcomes only when fields available', () => { it('should create outcomes only when fields available', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
fields: null fields: null
}}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.outcomes.length).toBe(0); expect(form.outcomes.length).toBe(0);
}); });
it('should use custom form outcomes', () => { it('should use custom form outcomes', () => {
const json = {formRepresentation: {formDefinition: { const formRepresentation = {
fields: [ fields: [
{ id: 'container1' } { id: 'container1' }
]}, ],
outcomes: [ outcomes: [
{ id: 'custom-1', name: 'custom 1' } { id: 'custom-1', name: 'custom 1' }
] ]
}}; };
const form = new FormCloud(json); const form = new FormCloud(formRepresentation);
expect(form.outcomes.length).toBe(2); expect(form.outcomes.length).toBe(2);
expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME); expect(form.outcomes[0].id).toBe(FormCloud.SAVE_OUTCOME);

View File

@ -18,11 +18,11 @@
import { import {
TabModel, FormWidgetModel, FormOutcomeModel, FormValues, TabModel, FormWidgetModel, FormOutcomeModel, FormValues,
FormWidgetModelCache, FormFieldModel, ContainerModel, FormFieldTypes, FormWidgetModelCache, FormFieldModel, ContainerModel, FormFieldTypes,
ValidateFormFieldEvent, FormFieldValidator, FormFieldTemplates } from '@alfresco/adf-core'; ValidateFormFieldEvent, FormFieldValidator, FormFieldTemplates, FormBaseModel } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { TaskVariableCloud } from './task-variable-cloud.model'; import { TaskVariableCloud } from './task-variable-cloud.model';
export class FormCloud { export class FormCloud extends FormBaseModel {
static SAVE_OUTCOME: string = '$save'; static SAVE_OUTCOME: string = '$save';
static COMPLETE_OUTCOME: string = '$complete'; static COMPLETE_OUTCOME: string = '$complete';
@ -34,14 +34,8 @@ export class FormCloud {
readonly name: string; readonly name: string;
readonly taskId: string; readonly taskId: string;
readonly taskName: string; readonly taskName: string;
private _isValid: boolean = true;
get isValid(): boolean {
return this._isValid;
}
readonly selectedOutcome: string; readonly selectedOutcome: string;
readonly json: any;
readOnly: boolean; readOnly: boolean;
processDefinitionId: any; processDefinitionId: any;
@ -54,29 +48,28 @@ export class FormCloud {
customFieldTemplates: FormFieldTemplates = {}; customFieldTemplates: FormFieldTemplates = {};
fieldValidators: FormFieldValidator[] = []; fieldValidators: FormFieldValidator[] = [];
constructor(json?: any, formData?: TaskVariableCloud[], readOnly: boolean = false, protected formService?: FormCloudService) { constructor(formCloudRepresentationJSON?: any, formData?: TaskVariableCloud[], readOnly: boolean = false, protected formService?: FormCloudService) {
super();
this.readOnly = readOnly; this.readOnly = readOnly;
if (json && json.formRepresentation && json.formRepresentation.formDefinition) { if (formCloudRepresentationJSON) {
this.json = json; this.json = formCloudRepresentationJSON;
this.id = json.formRepresentation.id; this.id = formCloudRepresentationJSON.id;
this.name = json.formRepresentation.name; this.name = formCloudRepresentationJSON.name;
this.taskId = json.formRepresentation.taskId; this.taskId = formCloudRepresentationJSON.taskId;
this.taskName = json.formRepresentation.taskName || json.formRepresentation.name; this.taskName = formCloudRepresentationJSON.taskName || formCloudRepresentationJSON.name;
this.processDefinitionId = json.formRepresentation.processDefinitionId; this.processDefinitionId = formCloudRepresentationJSON.processDefinitionId;
this.customFieldTemplates = json.formRepresentation.formDefinition.customFieldTemplates || {}; this.selectedOutcome = formCloudRepresentationJSON.selectedOutcome || '';
this.selectedOutcome = json.formRepresentation.formDefinition.selectedOutcome || {};
this.className = json.formRepresentation.formDefinition.className || '';
const tabCache: FormWidgetModelCache<TabModel> = {}; const tabCache: FormWidgetModelCache<TabModel> = {};
this.tabs = (json.formRepresentation.formDefinition.tabs || []).map((t) => { this.tabs = (formCloudRepresentationJSON.tabs || []).map((t) => {
const model = new TabModel(<any> this, t); const model = new TabModel(<any> this, t);
tabCache[model.id] = model; tabCache[model.id] = model;
return model; return model;
}); });
this.fields = this.parseRootFields(json); this.fields = this.parseRootFields(formCloudRepresentationJSON);
if (formData && formData.length > 0) { if (formData && formData.length > 0) {
this.loadData(formData); this.loadData(formData);
@ -93,7 +86,7 @@ export class FormCloud {
} }
} }
if (json.formRepresentation.formDefinition.fields) { if (formCloudRepresentationJSON.fields) {
const saveOutcome = new FormOutcomeModel(<any> this, { const saveOutcome = new FormOutcomeModel(<any> this, {
id: FormCloud.SAVE_OUTCOME, id: FormCloud.SAVE_OUTCOME,
name: 'SAVE', name: 'SAVE',
@ -110,7 +103,7 @@ export class FormCloud {
isSystem: true isSystem: true
}); });
const customOutcomes = (json.formRepresentation.outcomes || []).map((obj) => new FormOutcomeModel(<any> this, obj)); const customOutcomes = (formCloudRepresentationJSON.outcomes || []).map((obj) => new FormOutcomeModel(<any> this, obj));
this.outcomes = [saveOutcome].concat( this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome] customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]
@ -142,37 +135,10 @@ export class FormCloud {
return this.outcomes && this.outcomes.length > 0; return this.outcomes && this.outcomes.length > 0;
} }
getFieldById(fieldId: string): FormFieldModel {
return this.getFormFields().find((field) => field.id === fieldId);
}
onFormFieldChanged(field: FormFieldModel) { onFormFieldChanged(field: FormFieldModel) {
this.validateField(field); this.validateField(field);
} }
getFormFields(): FormFieldModel[] {
const formFields: FormFieldModel[] = [];
for (let i = 0; i < this.fields.length; i++) {
const field = this.fields[i];
if (field instanceof ContainerModel) {
const container = <ContainerModel> field;
formFields.push(container.field);
container.field.columns.forEach((column) => {
formFields.push(...column.fields);
});
}
}
return formFields;
}
markAsInvalid() {
this._isValid = false;
}
validateForm() { validateForm() {
const errorsField: FormFieldModel[] = []; const errorsField: FormFieldModel[] = [];
@ -183,7 +149,7 @@ export class FormCloud {
} }
} }
this._isValid = errorsField.length > 0 ? false : true; this.isValid = errorsField.length > 0 ? false : true;
} }
/** /**
@ -200,7 +166,7 @@ export class FormCloud {
const validateFieldEvent = new ValidateFormFieldEvent(<any> this, field); const validateFieldEvent = new ValidateFormFieldEvent(<any> this, field);
if (!validateFieldEvent.isValid) { if (!validateFieldEvent.isValid) {
this._isValid = false; this.isValid = false;
return; return;
} }
@ -209,7 +175,7 @@ export class FormCloud {
} }
if (!field.validate()) { if (!field.validate()) {
this._isValid = false; this.isValid = false;
} }
this.validateForm(); this.validateForm();
@ -219,10 +185,8 @@ export class FormCloud {
private parseRootFields(json: any): FormWidgetModel[] { private parseRootFields(json: any): FormWidgetModel[] {
let fields = []; let fields = [];
if (json.formRepresentation.fields) { if (json.fields) {
fields = json.formRepresentation.fields; fields = json.fields;
} else if (json.formRepresentation.formDefinition && json.formRepresentation.formDefinition.fields) {
fields = json.formRepresentation.formDefinition.fields;
} }
const formWidgetModel: FormWidgetModel[] = []; const formWidgetModel: FormWidgetModel[] = [];

View File

@ -150,15 +150,17 @@ describe('Form Cloud service', () => {
}); });
it('should fetch task form', (done) => { it('should fetch task form flattened', (done) => {
spyOn(service, 'getTask').and.returnValue(of(responseBody.entry)); spyOn(service, 'getTask').and.returnValue(of(responseBody.entry));
spyOn(service, 'getForm').and.returnValue(of({ formRepresentation: { name: 'task-form' } })); spyOn(service, 'getForm').and.returnValue(of({
formRepresentation: {name: 'task-form', formDefinition: {} }
}));
service.getTaskForm(appName, taskId).subscribe((result) => { service.getTaskForm(appName, taskId).subscribe((result) => {
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result.formRepresentation.name).toBe('task-form'); expect(result.name).toBe('task-form');
expect(result.formRepresentation.taskId).toBe(responseBody.entry.id); expect(result.taskId).toBe(responseBody.entry.id);
expect(result.formRepresentation.taskName).toBe(responseBody.entry.name); expect(result.taskName).toBe(responseBody.entry.name);
done(); done();
}); });

View File

@ -55,11 +55,13 @@ export class FormCloudService extends BaseCloudService {
switchMap((task: TaskDetailsCloudModel) => { switchMap((task: TaskDetailsCloudModel) => {
return this.getForm(appName, task.formKey).pipe( return this.getForm(appName, task.formKey).pipe(
map((form: any) => { map((form: any) => {
form.formRepresentation.taskId = task.id; const flattenForm = {...form.formRepresentation, ...form.formRepresentation.formDefinition};
form.formRepresentation.taskName = task.name; delete flattenForm.formDefinition;
form.formRepresentation.processDefinitionId = task.processDefinitionId; flattenForm.taskId = task.id;
form.formRepresentation.processInstanceId = task.processInstanceId; flattenForm.taskName = task.name;
return form; flattenForm.processDefinitionId = task.processDefinitionId;
flattenForm.processInstanceId = task.processInstanceId;
return flattenForm;
}) })
); );
}) })
@ -258,7 +260,10 @@ export class FormCloudService extends BaseCloudService {
*/ */
parseForm(json: any, data?: TaskVariableCloud[], readOnly: boolean = false): FormCloud { parseForm(json: any, data?: TaskVariableCloud[], readOnly: boolean = false): FormCloud {
if (json) { if (json) {
const form = new FormCloud(json, data, readOnly, this); const flattenForm = {...json.formRepresentation, ...json.formRepresentation.formDefinition};
delete flattenForm.formDefinition;
const form = new FormCloud(flattenForm, data, readOnly, this);
if (!json.fields) { if (!json.fields) {
form.outcomes = [ form.outcomes = [
new FormOutcomeModel(<any> form, { new FormOutcomeModel(<any> form, {

View File

@ -81,65 +81,61 @@ export let fakeProcessPayload = new ProcessPayloadCloud({
}); });
export let fakeStartForm = { export let fakeStartForm = {
'formRepresentation': { 'id': 'form-a5d50817-5183-4850-802d-17af54b2632f',
'id': 'form-a5d50817-5183-4850-802d-17af54b2632f', 'name': 'simpleform',
'name': 'simpleform', 'description': '',
'description': '', 'version': 0,
'version': 0, 'tabs': [],
'formDefinition': { 'fields': [
'tabs': [], {
'fields': [
{
'type': 'container', 'type': 'container',
'id': '5a6b24c1-db2b-45e9-9aff-142395433d23', 'id': '5a6b24c1-db2b-45e9-9aff-142395433d23',
'name': 'Label', 'name': 'Label',
'tab': null, 'tab': null,
'fields': { 'fields': {
'1': [ '1': [
{ {
'type': 'text', 'type': 'text',
'id': 'firstName', 'id': 'firstName',
'name': 'firstName', 'name': 'firstName',
'colspan': 1, 'colspan': 1,
'params': { 'params': {
'existingColspan': 1, 'existingColspan': 1,
'maxColspan': 2 'maxColspan': 2
}, },
'visibilityCondition': null, 'visibilityCondition': null,
'placeholder': null, 'placeholder': null,
'value': null, 'value': null,
'required': false, 'required': false,
'minLength': 0, 'minLength': 0,
'maxLength': 0, 'maxLength': 0,
'regexPattern': null 'regexPattern': null
} }
], ],
'2': [ '2': [
{ {
'type': 'text', 'type': 'text',
'id': 'lastName', 'id': 'lastName',
'name': 'lastName', 'name': 'lastName',
'colspan': 1, 'colspan': 1,
'params': { 'params': {
'existingColspan': 1, 'existingColspan': 1,
'maxColspan': 2 'maxColspan': 2
}, },
'visibilityCondition': null, 'visibilityCondition': null,
'placeholder': null, 'placeholder': null,
'value': null, 'value': null,
'required': false, 'required': false,
'minLength': 0, 'minLength': 0,
'maxLength': 0, 'maxLength': 0,
'regexPattern': null 'regexPattern': null
} }
] ]
}, },
'numberOfColumns': 2 'numberOfColumns': 2
} }
], ],
'outcomes': [], 'outcomes': [],
'metadata': {}, 'metadata': {},
'variables': [] 'variables': []
} };
}
};

View File

@ -282,10 +282,10 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnDestro
this.error.emit(err); this.error.emit(err);
} }
parseForm(json: any): FormModel { parseForm(formRepresentationJSON: any): FormModel {
if (json) { if (formRepresentationJSON) {
const form = new FormModel(json, this.data, this.readOnly, this.formService); const form = new FormModel(formRepresentationJSON, this.data, this.readOnly, this.formService);
if (!json.fields) { if (!formRepresentationJSON.fields) {
form.outcomes = this.getFormDefinitionOutcomes(form); form.outcomes = this.getFormDefinitionOutcomes(form);
} }
if (this.fieldValidators && this.fieldValidators.length > 0) { if (this.fieldValidators && this.fieldValidators.length > 0) {