[ADF-907] - Form reacts to data added in input (#2016)

* [ADF-907] Enable activiti form to react on value data changes

* [ADF-907] - Form reacts to data added in input]

* [ADF - 907] added mock json for form

* [ADF-907] added new event of the form to the event list

* [ADF - 907] Added return column to README

* [ADF - 907] Added return column to README
This commit is contained in:
Vito
2017-06-27 06:30:49 -07:00
committed by Eugenio Romano
parent e30a1aed30
commit a431f3934f
10 changed files with 825 additions and 51 deletions

View File

@@ -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.
@@ -17,6 +17,343 @@
export class DemoForm {
static getEasyForm(): any {
return {
'id': 1001,
'name': 'ISSUE_FORM',
'processDefinitionId': 'ISSUE_APP:1:2504',
'processDefinitionName': 'ISSUE_APP',
'processDefinitionKey': 'ISSUE_APP',
'taskId': '2510',
'taskDefinitionKey': 'sid-F67A2996-1684-4774-855A-4591490081FD',
'tabs': [],
'fields': [
{
'fieldType': 'ContainerRepresentation',
'id': '1498212398417',
'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': false,
'options': null,
'restUrl': null,
'restResponsePath': null,
'restIdProperty': null,
'restLabelProperty': null,
'tab': null,
'className': null,
'dateDisplayFormat': null,
'sizeX': 2,
'sizeY': 1,
'row': -1,
'col': -1,
'numberOfColumns': 2,
'fields': {
'1': [
{
'fieldType': 'RestFieldRepresentation',
'id': 'label',
'name': 'Label',
'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': null,
'hasEmptyValue': true,
'options': [
{
'id': 'empty',
'name': 'Choose one...'
},
{
'id': 'option_1',
'name': 'test1'
},
{
'id': 'option_2',
'name': 'test2'
},
{
'id': 'option_3',
'name': 'test3'
}
],
'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
},
{
'fieldType': 'FormFieldRepresentation',
'id': 'Date',
'name': 'Date',
'type': 'date',
'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': 'tab1',
'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
},
{
'fieldType': 'FormFieldRepresentation',
'id': 'label5',
'name': 'Label5',
'type': 'boolean',
'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': 'tab1',
'className': null,
'params': {
'existingColspan': 1,
'maxColspan': 1
},
'dateDisplayFormat': null,
'layout': {
'row': -1,
'column': -1,
'colspan': 1
},
'sizeX': 1,
'sizeY': 1,
'row': -1,
'col': -1,
'visibilityCondition': null
},
{
'fieldType': 'FormFieldRepresentation',
'id': 'label6',
'name': 'Label6',
'type': 'boolean',
'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': 'tab1',
'className': null,
'params': {
'existingColspan': 1,
'maxColspan': 1
},
'dateDisplayFormat': null,
'layout': {
'row': -1,
'column': -1,
'colspan': 1
},
'sizeX': 1,
'sizeY': 1,
'row': -1,
'col': -1,
'visibilityCondition': null
},
{
'fieldType': 'FormFieldRepresentation',
'id': 'label4',
'name': 'Label4',
'type': 'integer',
'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': 'tab1',
'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
},
{
'fieldType': 'RestFieldRepresentation',
'id': 'label12',
'name': 'Label12',
'type': 'radio-buttons',
'value': null,
'required': false,
'readOnly': false,
'overrideId': false,
'colspan': 1,
'placeholder': null,
'minLength': 0,
'maxLength': 0,
'minValue': null,
'maxValue': null,
'regexPatt§12212ern': null,
'optionType': null,
'hasEmptyValue': null,
'options': [
{
'id': 'option_1',
'name': 'Option 1'
},
{
'id': 'option_2',
'name': 'Option 2'
}
],
'restUrl': null,
'restResponsePath': null,
'restIdProperty': null,
'restLabelProperty': null,
'tab': 'tab1',
'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
}
]
}
}
],
'outcomes': [],
'javascriptEvents': [],
'className': '',
'style': '',
'customFieldTemplates': {},
'metadata': {},
'variables': [],
'customFieldsValueInfo': {},
'gridsterForm': false,
'globalDateFormat': 'D-M-YYYY'
};
}
static getDefinition(): any {
return {
'id': 3003,

View File

@@ -1,3 +1,8 @@
.form-container {
padding: 10px;
}
.store-form-container{
width: 80%;
height: 80%;
}

View File

@@ -1,4 +1,32 @@
<div class="form-container">
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__tab-bar mdl-js-ripple-effect">
<a id="demo-form" href="#demo" class="mdl-layout__tab" [class.is-active]="activeTab === 'demo'" (click)="changeToDemoForm()">DEMO</a>
<a id="store-form" href="#store" class="mdl-layout__tab" [class.is-active]="activeTab === 'processes'" (click)="changeToStoreForm()">STORE</a>
</div>
</header>
<main class="mdl-layout__content activiti">
<section class="mdl-layout__tab-panel" [class.is-active]="activeTab === 'demo'" id="demo">
<div class="form-container">
<activiti-form [form]="form">
</activiti-form>
</div>
</section>
<section class="mdl-layout__tab-panel" [class.is-active]="activeTab === 'store'" id="store">
<span>FORM NAME TO VISUALIZE</span>
<input [(ngModel)]="formToLoadName" class="mdl-textfield__input" type="text" (ngModelChange)="loadForm()">
<div *ngIf="formToLoadName && formToLoadName !== '' " class="store-form-container">
<activiti-form [form]="form" [data]="restoredData" #storeForm>
</activiti-form>
<button class="mdl-button mdl-js-button" (click)="store()">STORE</button>
<button class="mdl-button mdl-js-button" (click)="restore()">RESTORE</button>
</div>
<div>
<span> Please create in APS the form with the name you entered</span>
</div>
<div *ngIf="showError">
<span> Form Name not valid or form not present</span>
</div>
</section>
</main>
</div>

View File

@@ -15,9 +15,12 @@
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { FormModel, FormService } from 'ng2-activiti-form';
import { DemoForm } from './demo-form';
import { ActivitiForm } from 'ng2-activiti-form';
declare var componentHandler;
@Component({
selector: 'form-demo',
@@ -26,9 +29,17 @@ import { DemoForm } from './demo-form';
'form-demo.component.css'
]
})
export class FormDemoComponent implements OnInit {
export class FormDemoComponent implements OnInit, AfterViewInit {
@ViewChild(ActivitiForm)
activitiForm: ActivitiForm;
form: FormModel;
activeTab: string = 'demo';
storedData: any = {};
restoredData: any = {};
formToLoadName: string = null;
showError: boolean = false;
constructor(private formService: FormService) {
formService.executeOutcome.subscribe(e => {
@@ -43,4 +54,69 @@ export class FormDemoComponent implements OnInit {
console.log(form);
this.form = form;
}
ngAfterViewInit() {
// workaround for MDL issues with dynamic components
if (componentHandler) {
componentHandler.upgradeAllRegistered();
}
}
changeToStoreForm() {
this.activeTab = 'store';
this.showError = false;
}
changeToDemoForm() {
this.activeTab = 'demo';
let formDefinitionJSON: any = DemoForm.getDefinition();
let demoForm = this.formService.parseForm(formDefinitionJSON);
this.form = demoForm;
}
store() {
this.clone(this.activitiForm.form.values, this.storedData);
console.log('DATA SAVED');
console.log(this.storedData);
console.log('DATA SAVED');
this.restoredData = null;
}
restore() {
this.restoredData = this.storedData;
this.storedData = {};
}
clone(objToCopyFrom, objToCopyTo) {
for (let attribute in objToCopyFrom) {
if (objToCopyFrom.hasOwnProperty(attribute)) {
objToCopyTo[attribute] = objToCopyFrom[attribute];
}
}
return objToCopyTo;
}
loadForm() {
if (this.formToLoadName) {
this.showError = false;
this.formService
.getFormDefinitionByName(this.formToLoadName)
.debounceTime(7000)
.subscribe(
id => {
this.formService.getFormDefinitionById(id).subscribe(
form => {
this.form = this.formService.parseForm((form);
},
(error) => {
this.showError = true;
}
);
},
(error) => {
this.showError = true;
}
);
}
}
}

View File

@@ -174,13 +174,14 @@ The recommended set of properties can be found in the following table:
### Events
| Name | Description |
| --- | --- |
| formLoaded | Invoked when form is loaded or reloaded. |
| formSaved | Invoked when form is submitted with `Save` or custom outcomes. |
| formCompleted | Invoked when form is submitted with `Complete` outcome. |
| executeOutcome | Invoked when any outcome is executed, default behaviour can be prevented via `event.preventDefault()` |
| onError | Invoked at any error |
| Name | Return Type | Description |
| --- | --- | --- |
| formLoaded | [FormModel](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts) | Invoked when form is loaded or reloaded. |
| formSaved | [FormModel](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts) | Invoked when form is submitted with `Save` or custom outcomes. |
| formCompleted | [FormModel](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts) | Invoked when form is submitted with `Complete` outcome. |
| formDataRefreshed | [FormModel](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts) | Invoked when form values are refreshed due a data property change |
| executeOutcome | [FormOutcomeEvent](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-activiti-form/src/components/widgets/core/form-outcome-event.model.ts) | Invoked when any outcome is executed, default behaviour can be prevented via `event.preventDefault()` |
| onError | any | Invoked at any error |
All `form*` events receive an instance of the `FormModel` as event argument for ease of development:

View File

@@ -0,0 +1,275 @@
/*!
* @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.
*/
export let fakeForm = {
id: 1001,
name: 'ISSUE_FORM',
processDefinitionId: 'ISSUE_APP:1:2504',
processDefinitionName: 'ISSUE_APP',
processDefinitionKey: 'ISSUE_APP',
taskId: '7506',
taskDefinitionKey: 'sid-F67A2996-1684-4774-855A-4591490081FD',
tabs: [],
fields: [
{
fieldType: 'ContainerRepresentation',
id: '1498212398417',
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: 'label',
name: 'Label',
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: null,
hasEmptyValue: true,
options: [
{
id: 'empty',
name: 'Choose one...'
},
{
id: 'option_1',
name: 'test1'
},
{
id: 'option_2',
name: 'test2'
},
{
id: 'option_3',
name: 'test3'
}
],
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: [
{
fieldType: 'RestFieldRepresentation',
id: 'raduio',
name: 'raduio',
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: '1498212413062',
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: 'FormFieldRepresentation',
id: 'date',
name: 'date',
type: 'date',
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,
params: {
existingColspan: 1,
maxColspan: 2
},
dateDisplayFormat: null,
layout: {
row: -1,
column: -1,
colspan: 1
},
sizeX: 1,
sizeY: 1,
row: -1,
col: -1,
visibilityCondition: null
}
],
2: []
}
}
],
outcomes: [],
javascriptEvents: [],
className: '',
style: '',
customFieldTemplates: {},
metadata: {},
variables: [],
customFieldsValueInfo: {},
gridsterForm: false,
globalDateFormat: 'D-M-YYYY'
};

View File

@@ -23,6 +23,7 @@ import { FormModel, FormOutcomeModel, FormFieldModel, FormOutcomeEvent, FormFiel
import { FormService } from './../services/form.service';
import { WidgetVisibilityService } from './../services/widget-visibility.service';
import { NodeService } from './../services/node.service';
import { fakeForm } from '../assets/activiti-form.component.mock';
describe('ActivitiForm', () => {
@@ -107,14 +108,14 @@ describe('ActivitiForm', () => {
it('should enable custom outcome buttons', () => {
let formModel = new FormModel();
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: 'action1', name: 'Action 1'});
let outcome = new FormOutcomeModel(formModel, { id: 'action1', name: 'Action 1' });
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
});
it('should allow controlling [complete] button visibility', () => {
let formModel = new FormModel();
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.SAVE_ACTION});
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
@@ -127,7 +128,7 @@ describe('ActivitiForm', () => {
let formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$complete', name: FormOutcomeModel.COMPLETE_ACTION});
let outcome = new FormOutcomeModel(formModel, { id: '$complete', name: FormOutcomeModel.COMPLETE_ACTION });
formComponent.showCompleteButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
@@ -137,23 +138,23 @@ describe('ActivitiForm', () => {
let formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.SAVE_ACTION});
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
});
it('should show [custom-outcome] button with readOnly form and selected custom-outcome', () => {
let formModel = new FormModel({selectedOutcome: 'custom-outcome'});
let formModel = new FormModel({ selectedOutcome: 'custom-outcome' });
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$customoutome', name: 'custom-outcome'});
let outcome = new FormOutcomeModel(formModel, { id: '$customoutome', name: 'custom-outcome' });
formComponent.showCompleteButton = true;
formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
outcome = new FormOutcomeModel(formModel, {id: '$customoutome2', name: 'custom-outcome2'});
outcome = new FormOutcomeModel(formModel, { id: '$customoutome2', name: 'custom-outcome2' });
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
});
@@ -161,7 +162,7 @@ describe('ActivitiForm', () => {
let formModel = new FormModel();
formModel.readOnly = false;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.COMPLETE_ACTION});
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.COMPLETE_ACTION });
formComponent.showCompleteButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
@@ -191,7 +192,7 @@ describe('ActivitiForm', () => {
it('should get process variable if is a process task', () => {
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId});
observer.next({ taskId: taskId });
observer.complete();
});
});
@@ -199,7 +200,7 @@ describe('ActivitiForm', () => {
spyOn(visibilityService, 'getTaskProcessVariable').and.returnValue(Observable.of({}));
spyOn(formService, 'getTask').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId, processDefinitionId: '10201'});
observer.next({ taskId: taskId, processDefinitionId: '10201' });
observer.complete();
});
});
@@ -214,7 +215,7 @@ describe('ActivitiForm', () => {
it('should not get process variable if is not a process task', () => {
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId});
observer.next({ taskId: taskId });
observer.complete();
});
});
@@ -222,7 +223,7 @@ describe('ActivitiForm', () => {
spyOn(visibilityService, 'getTaskProcessVariable').and.returnValue(Observable.of({}));
spyOn(formService, 'getTask').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId, processDefinitionId: 'null'});
observer.next({ taskId: taskId, processDefinitionId: 'null' });
observer.complete();
});
});
@@ -259,7 +260,7 @@ describe('ActivitiForm', () => {
const taskId = '<task id>';
let change = new SimpleChange(null, taskId, true);
formComponent.ngOnChanges({'taskId': change});
formComponent.ngOnChanges({ 'taskId': change });
expect(formComponent.getFormByTaskId).toHaveBeenCalledWith(taskId);
});
@@ -269,7 +270,7 @@ describe('ActivitiForm', () => {
const formId = '123';
let change = new SimpleChange(null, formId, true);
formComponent.ngOnChanges({'formId': change});
formComponent.ngOnChanges({ 'formId': change });
expect(formComponent.getFormDefinitionByFormId).toHaveBeenCalledWith(formId);
});
@@ -279,7 +280,7 @@ describe('ActivitiForm', () => {
const formName = '<form>';
let change = new SimpleChange(null, formName, true);
formComponent.ngOnChanges({'formName': change});
formComponent.ngOnChanges({ 'formName': change });
expect(formComponent.getFormDefinitionByFormName).toHaveBeenCalledWith(formName);
});
@@ -304,7 +305,7 @@ describe('ActivitiForm', () => {
spyOn(formComponent, 'getFormDefinitionByFormId').and.stub();
spyOn(formComponent, 'getFormDefinitionByFormName').and.stub();
formComponent.ngOnChanges({'tag': new SimpleChange(null, 'hello world', true)});
formComponent.ngOnChanges({ 'tag': new SimpleChange(null, 'hello world', true) });
expect(formComponent.getFormByTaskId).not.toHaveBeenCalled();
expect(formComponent.getFormDefinitionByFormId).not.toHaveBeenCalled();
@@ -314,7 +315,7 @@ describe('ActivitiForm', () => {
it('should complete form on custom outcome click', () => {
let formModel = new FormModel();
let outcomeName = 'Custom Action';
let outcome = new FormOutcomeModel(formModel, {id: 'custom1', name: outcomeName});
let outcome = new FormOutcomeModel(formModel, { id: 'custom1', name: outcomeName });
let saved = false;
formComponent.form = formModel;
@@ -379,7 +380,7 @@ describe('ActivitiForm', () => {
it('should do nothing when clicking outcome for readonly form', () => {
let formModel = new FormModel();
const outcomeName = 'Custom Action';
let outcome = new FormOutcomeModel(formModel, {id: 'custom1', name: outcomeName});
let outcome = new FormOutcomeModel(formModel, { id: 'custom1', name: outcomeName });
formComponent.form = formModel;
spyOn(formComponent, 'completeTaskForm').and.stub();
@@ -398,7 +399,7 @@ describe('ActivitiForm', () => {
it('should require loaded form when clicking outcome', () => {
let formModel = new FormModel();
const outcomeName = 'Custom Action';
let outcome = new FormOutcomeModel(formModel, {id: 'custom1', name: outcomeName});
let outcome = new FormOutcomeModel(formModel, { id: 'custom1', name: outcomeName });
formComponent.readOnly = false;
formComponent.form = null;
@@ -407,7 +408,7 @@ describe('ActivitiForm', () => {
it('should not execute unknown system outcome', () => {
let formModel = new FormModel();
let outcome = new FormOutcomeModel(formModel, {id: 'unknown', name: 'Unknown', isSystem: true});
let outcome = new FormOutcomeModel(formModel, { id: 'unknown', name: 'Unknown', isSystem: true });
formComponent.form = formModel;
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
@@ -415,12 +416,12 @@ describe('ActivitiForm', () => {
it('should require custom action name to complete form', () => {
let formModel = new FormModel();
let outcome = new FormOutcomeModel(formModel, {id: 'custom'});
let outcome = new FormOutcomeModel(formModel, { id: 'custom' });
formComponent.form = formModel;
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
outcome = new FormOutcomeModel(formModel, {id: 'custom', name: 'Custom'});
outcome = new FormOutcomeModel(formModel, { id: 'custom', name: 'Custom' });
spyOn(formComponent, 'completeTaskForm').and.stub();
expect(formComponent.onOutcomeClicked(outcome)).toBeTruthy();
});
@@ -429,7 +430,7 @@ describe('ActivitiForm', () => {
spyOn(formService, 'getTask').and.returnValue(Observable.of({}));
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId});
observer.next({ taskId: taskId });
observer.complete();
});
});
@@ -465,7 +466,7 @@ describe('ActivitiForm', () => {
spyOn(formService, 'getTask').and.returnValue(Observable.of({}));
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
return Observable.create(observer => {
observer.next({taskId: taskId});
observer.next({ taskId: taskId });
observer.complete();
});
});
@@ -481,7 +482,7 @@ describe('ActivitiForm', () => {
it('should fetch and parse form definition by id', () => {
spyOn(formService, 'getFormDefinitionById').and.callFake((formId) => {
return Observable.create(observer => {
observer.next({id: formId});
observer.next({ id: formId });
observer.complete();
});
});
@@ -520,7 +521,7 @@ describe('ActivitiForm', () => {
spyOn(formService, 'getFormDefinitionById').and.callFake((formName) => {
return Observable.create(observer => {
observer.next({name: formName});
observer.next({ name: formName });
observer.complete();
});
});
@@ -556,8 +557,8 @@ describe('ActivitiForm', () => {
let formModel = new FormModel({
taskId: '123',
fields: [
{id: 'field1'},
{id: 'field2'}
{ id: 'field1' },
{ id: 'field2' }
]
});
formComponent.form = formModel;
@@ -573,7 +574,7 @@ describe('ActivitiForm', () => {
spyOn(formService, 'saveTaskForm').and.callFake(() => Observable.throw(error));
spyOn(formComponent, 'handleError').and.stub();
formComponent.form = new FormModel({taskId: '123'});
formComponent.form = new FormModel({ taskId: '123' });
formComponent.saveTaskForm();
expect(formComponent.handleError).toHaveBeenCalledWith(error);
@@ -618,8 +619,8 @@ describe('ActivitiForm', () => {
let formModel = new FormModel({
taskId: '123',
fields: [
{id: 'field1'},
{id: 'field2'}
{ id: 'field1' },
{ id: 'field2' }
]
});
@@ -638,7 +639,7 @@ describe('ActivitiForm', () => {
let form = formComponent.parseForm({
id: '<id>',
fields: [
{id: 'field1', type: FormFieldTypes.CONTAINER}
{ id: 'field1', type: FormFieldTypes.CONTAINER }
]
});
@@ -651,7 +652,7 @@ describe('ActivitiForm', () => {
it('should provide outcomes for form definition', () => {
spyOn(formComponent, 'getFormDefinitionOutcomes').and.callThrough();
let form = formComponent.parseForm({id: '<id>'});
let form = formComponent.parseForm({ id: '<id>' });
expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form);
});
@@ -724,7 +725,7 @@ describe('ActivitiForm', () => {
let metadata = {};
spyOn(nodeService, 'getNodeMetadata').and.returnValue(
Observable.create(observer => {
observer.next({metadata: metadata});
observer.next({ metadata: metadata });
observer.complete();
})
);
@@ -814,4 +815,30 @@ describe('ActivitiForm', () => {
formComponent.onOutcomeClicked(outcome);
});
it('should refresh form values when data is changed', () => {
formComponent.form = new FormModel(fakeForm);
let formFields = formComponent.form.getFormFields();
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();
let formValues: any = {};
formValues.label = {
id: 'option_1',
name: 'test1'
};
formValues.raduio = { id: 'option_1', name: 'Option 1' };
let change = new SimpleChange(null, formValues, false);
formComponent.data = formValues;
formComponent.ngOnChanges({ 'data': change });
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');
});
});

View File

@@ -100,6 +100,9 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
@Output()
formLoaded: EventEmitter<FormModel> = new EventEmitter<FormModel>();
@Output()
formDataRefreshed: EventEmitter<FormModel> = new EventEmitter<FormModel>();
@Output()
executeOutcome: EventEmitter<FormOutcomeEvent> = new EventEmitter<FormOutcomeEvent>();
@@ -202,6 +205,12 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
this.loadFormForEcmNode();
return;
}
let data = changes['data'];
if (data && data.currentValue) {
this.refreshFormData();
return;
}
}
/**
@@ -424,6 +433,12 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
}
}
private refreshFormData() {
this.form = new FormModel(this.form.json, this.data, this.readOnly, this.formService);
this.onFormLoaded(this.form);
this.onFormDataRefreshed(this.form);
}
private loadFormForEcmNode(): void {
this.nodeService.getNodeMetadata(this.nodeId).subscribe(data => {
this.data = data.metadata;
@@ -471,6 +486,11 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
this.formService.formLoaded.next(new FormEvent(form));
}
protected onFormDataRefreshed(form: FormModel) {
this.formDataRefreshed.emit(form);
this.formService.formDataRefreshed.next(new FormEvent(form));
}
protected onTaskSaved(form: FormModel) {
this.formSaved.emit(form);
this.formService.taskSaved.next(new FormEvent(form));

View File

@@ -109,7 +109,7 @@ export class FormModel {
}
if (json.fields) {
let saveOutcome = new FormOutcomeModel(this, { id: FormModel.SAVE_OUTCOME, name: 'Save', isSystem: true });
let completeOutcome = new FormOutcomeModel(this, {id: FormModel.COMPLETE_OUTCOME, name: 'Complete', isSystem: true });
let completeOutcome = new FormOutcomeModel(this, { id: FormModel.COMPLETE_OUTCOME, name: 'Complete', isSystem: true });
let startProcessOutcome = new FormOutcomeModel(this, { id: FormModel.START_PROCESS_OUTCOME, name: 'Start Process', isSystem: true });
let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
@@ -137,7 +137,7 @@ export class FormModel {
let field = this.fields[i];
if (field instanceof ContainerModel) {
let container = <ContainerModel> field;
let container = <ContainerModel>field;
result.push(container.field);
result.push(...container.field.fields);
}
@@ -203,7 +203,11 @@ export class FormModel {
for (let field of this.getFormFields()) {
if (data[field.id]) {
field.json.value = data[field.id];
field.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;
}
}
}
}

View File

@@ -33,6 +33,7 @@ export class FormService {
static GENERIC_ERROR_MESSAGE: string = 'Server error';
formLoaded: Subject<FormEvent> = new Subject<FormEvent>();
formDataRefreshed: Subject<FormEvent> = new Subject<FormEvent>();
formFieldValueChanged: Subject<FormFieldEvent> = new Subject<FormFieldEvent>();
formEvents: Subject<Event> = new Subject<Event>();
taskCompleted: Subject<FormEvent> = new Subject<FormEvent>();