mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
Merge pull request #583 from Alfresco/dev-denys-572
#572 Activiti Form enhancements
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, AfterViewChecked, ViewChild } from '@angular/core';
|
import { Component, AfterViewChecked, ViewChild } from '@angular/core';
|
||||||
import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist';
|
import { ALFRESCO_TASKLIST_DIRECTIVES } from 'ng2-activiti-tasklist';
|
||||||
import { ActivitiForm } from 'ng2-activiti-form';
|
import { ActivitiForm } from 'ng2-activiti-form';
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ declare var componentHandler;
|
|||||||
styleUrls: ['./activiti-demo.component.css'],
|
styleUrls: ['./activiti-demo.component.css'],
|
||||||
directives: [ALFRESCO_TASKLIST_DIRECTIVES, ActivitiForm]
|
directives: [ALFRESCO_TASKLIST_DIRECTIVES, ActivitiForm]
|
||||||
})
|
})
|
||||||
export class ActivitiDemoComponent implements OnInit, AfterViewChecked {
|
export class ActivitiDemoComponent implements AfterViewChecked {
|
||||||
|
|
||||||
currentChoice: string = 'task-list';
|
currentChoice: string = 'task-list';
|
||||||
|
|
||||||
|
@@ -40,10 +40,88 @@ Also make sure you include these dependencies in your `index.html` file:
|
|||||||
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
|
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic usage example
|
## Basic usage examples
|
||||||
|
|
||||||
|
### Display form instance by task id
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<activiti-form [taskId]="selectedTask?.id"></activiti-form>
|
<activiti-form
|
||||||
|
[taskId]="selectedTask?.id">
|
||||||
|
</activiti-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
For an existing Task both form and values will be fetched and displayed.
|
||||||
|
|
||||||
|
### Display form definition by form id
|
||||||
|
|
||||||
|
```html
|
||||||
|
<activiti-form
|
||||||
|
[formId]="selectedFormDefinition?.id"
|
||||||
|
[data]="customData">
|
||||||
|
</activiti-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
Only form definition will be fetched
|
||||||
|
|
||||||
|
### Display form definition by form name
|
||||||
|
|
||||||
|
```html
|
||||||
|
<activiti-form
|
||||||
|
[formName]="selectedFormDefinition?.name"
|
||||||
|
[data]="customData">
|
||||||
|
</activiti-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
The recommended set of properties can be found in the following table:
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| taskId | string | | Task id to fetch corresponding form and values. |
|
||||||
|
| formId | string | | The id of the form definition to load and display with custom values. |
|
||||||
|
| formName | string | | Name of hte form definition to load and display with custom values. |
|
||||||
|
| data | `FormValues` | | Custom form values map to be used with the rendered form. |
|
||||||
|
| showTitle | boolean | true | Toggle rendering of the form title. |
|
||||||
|
| showCompleteButton | boolean | true | Toggle rendering of the `Complete` outcome button. |
|
||||||
|
| showSaveButton | boolean | true | Toggle rendering of the `Save` outcome button. |
|
||||||
|
| readOnly | boolean | false | Toggle readonly state of the form. Enforces all form widgets render readonly if enabled. |
|
||||||
|
| showRefreshButton | boolean | true | Toggle rendering of the `Refresh` button. |
|
||||||
|
|
||||||
|
#### Advanced properties
|
||||||
|
|
||||||
|
The following properties are for complex customisation purposes:
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| form | `FormModel` | | Underlying form model instance. |
|
||||||
|
| debugMode | boolean | false | Toggle debug mode, allows displaying additional data for development and debugging purposes. |
|
||||||
|
|
||||||
|
### 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. |
|
||||||
|
|
||||||
|
All `form*` events recieve an instance of the `FormModel` as event argument for ease of development:
|
||||||
|
|
||||||
|
**MyView.component.html**
|
||||||
|
```html
|
||||||
|
<activiti-form
|
||||||
|
[taskId]="selectedTask?.id"
|
||||||
|
formSaved="onFormSaved($event)">
|
||||||
|
</activiti-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
**MyView.component.ts**
|
||||||
|
```ts
|
||||||
|
onFormSaved(form: FormModel) {
|
||||||
|
console.log(form);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build from sources
|
## Build from sources
|
||||||
|
@@ -16,11 +16,551 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { it, describe, expect } from '@angular/core/testing';
|
import { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import { SimpleChange } from '@angular/core';
|
||||||
|
import { ActivitiForm } from './activiti-form.component';
|
||||||
|
import { FormModel, FormOutcomeModel } from './widgets/index';
|
||||||
|
import { FormService } from './../services/form.service';
|
||||||
|
|
||||||
describe('ActivitiForm', () => {
|
describe('ActivitiForm', () => {
|
||||||
|
|
||||||
it('test placeholder', () => {
|
let componentHandler: any;
|
||||||
expect(true).toBeTruthy();
|
let formService: FormService;
|
||||||
|
let formComponent: ActivitiForm;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
componentHandler = jasmine.createSpyObj('componentHandler', [
|
||||||
|
'upgradeAllRegistered'
|
||||||
|
]);
|
||||||
|
window['componentHandler'] = componentHandler;
|
||||||
|
|
||||||
|
formService = new FormService(null, null, null);
|
||||||
|
formComponent = new ActivitiForm(formService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should upgrade MDL content on view checked', () => {
|
||||||
|
formComponent.ngAfterViewChecked();
|
||||||
|
expect(componentHandler.upgradeAllRegistered).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup MDL content only if component handler available', () => {
|
||||||
|
expect(formComponent.setupMaterialComponents()).toBeTruthy();
|
||||||
|
|
||||||
|
window['componentHandler'] = null;
|
||||||
|
expect(formComponent.setupMaterialComponents()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should start loading form on init', () => {
|
||||||
|
spyOn(formComponent, 'loadForm').and.stub();
|
||||||
|
formComponent.ngOnInit();
|
||||||
|
expect(formComponent.loadForm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check form', () => {
|
||||||
|
expect(formComponent.hasForm()).toBeFalsy();
|
||||||
|
formComponent.form = new FormModel();
|
||||||
|
expect(formComponent.hasForm()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow title if task name available', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
formComponent.form = formModel;
|
||||||
|
|
||||||
|
expect(formComponent.showTitle).toBeTruthy();
|
||||||
|
expect(formModel.taskName).toBe(FormModel.UNSET_TASK_NAME);
|
||||||
|
expect(formComponent.isTitleEnabled()).toBeTruthy();
|
||||||
|
|
||||||
|
// override property as it's the readonly one
|
||||||
|
Object.defineProperty(formModel, 'taskName', {
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
writable: false,
|
||||||
|
value: null
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(formComponent.isTitleEnabled()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow title', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
formComponent.showTitle = false;
|
||||||
|
|
||||||
|
expect(formModel.taskName).toBe(FormModel.UNSET_TASK_NAME);
|
||||||
|
expect(formComponent.isTitleEnabled()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not enable outcome button when model missing', () => {
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(null)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable custom outcome buttons', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, { id: 'action1', name: 'Action 1' });
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should allow controlling [complete] button visibility', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
|
||||||
|
|
||||||
|
formComponent.showSaveButton = true;
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||||
|
|
||||||
|
formComponent.showSaveButton = false;
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow controlling [save] button visibility', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.COMPLETE_ACTION });
|
||||||
|
|
||||||
|
formComponent.showCompleteButton = true;
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||||
|
|
||||||
|
formComponent.showCompleteButton = false;
|
||||||
|
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load form on refresh', () => {
|
||||||
|
spyOn(formComponent, 'loadForm').and.stub();
|
||||||
|
|
||||||
|
formComponent.onRefreshClicked();
|
||||||
|
expect(formComponent.loadForm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form by task id on load', () => {
|
||||||
|
spyOn(formComponent, 'getFormByTaskId').and.stub();
|
||||||
|
const taskId = '123';
|
||||||
|
|
||||||
|
formComponent.taskId = taskId;
|
||||||
|
formComponent.loadForm();
|
||||||
|
|
||||||
|
expect(formComponent.getFormByTaskId).toHaveBeenCalledWith(taskId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form definition by form id on load', () => {
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormId').and.stub();
|
||||||
|
const formId = '123';
|
||||||
|
|
||||||
|
formComponent.formId = formId;
|
||||||
|
formComponent.loadForm();
|
||||||
|
|
||||||
|
expect(formComponent.getFormDefinitionByFormId).toHaveBeenCalledWith(formId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form definition by form name on load', () => {
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormName').and.stub();
|
||||||
|
const formName = '<form>';
|
||||||
|
|
||||||
|
formComponent.formName = formName;
|
||||||
|
formComponent.loadForm();
|
||||||
|
|
||||||
|
expect(formComponent.getFormDefinitionByFormName).toHaveBeenCalledWith(formName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload form by task id on binding changes', () => {
|
||||||
|
spyOn(formComponent, 'getFormByTaskId').and.stub();
|
||||||
|
const taskId = '<task id>';
|
||||||
|
|
||||||
|
let change = new SimpleChange(null, taskId);
|
||||||
|
formComponent.ngOnChanges({ 'taskId': change });
|
||||||
|
|
||||||
|
expect(formComponent.getFormByTaskId).toHaveBeenCalledWith(taskId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload form definition by form id on binding changes', () => {
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormId').and.stub();
|
||||||
|
const formId = '123';
|
||||||
|
|
||||||
|
let change = new SimpleChange(null, formId);
|
||||||
|
formComponent.ngOnChanges({ 'formId': change });
|
||||||
|
|
||||||
|
expect(formComponent.getFormDefinitionByFormId).toHaveBeenCalledWith(formId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload form definition by name on binding changes', () => {
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormName').and.stub();
|
||||||
|
const formName = '<form>';
|
||||||
|
|
||||||
|
let change = new SimpleChange(null, formName);
|
||||||
|
formComponent.ngOnChanges({ 'formName': change });
|
||||||
|
|
||||||
|
expect(formComponent.getFormDefinitionByFormName).toHaveBeenCalledWith(formName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not get form on load', () => {
|
||||||
|
spyOn(formComponent, 'getFormByTaskId').and.stub();
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormId').and.stub();
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormName').and.stub();
|
||||||
|
|
||||||
|
formComponent.taskId = null;
|
||||||
|
formComponent.formId = null;
|
||||||
|
formComponent.formName = null;
|
||||||
|
formComponent.loadForm();
|
||||||
|
|
||||||
|
expect(formComponent.getFormByTaskId).not.toHaveBeenCalled();
|
||||||
|
expect(formComponent.getFormDefinitionByFormId).not.toHaveBeenCalled();
|
||||||
|
expect(formComponent.getFormDefinitionByFormName).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not reload form on binding changes', () => {
|
||||||
|
spyOn(formComponent, 'getFormByTaskId').and.stub();
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormId').and.stub();
|
||||||
|
spyOn(formComponent, 'getFormDefinitionByFormName').and.stub();
|
||||||
|
|
||||||
|
formComponent.ngOnChanges({ 'tag': new SimpleChange(null, 'hello world')});
|
||||||
|
|
||||||
|
expect(formComponent.getFormByTaskId).not.toHaveBeenCalled();
|
||||||
|
expect(formComponent.getFormDefinitionByFormId).not.toHaveBeenCalled();
|
||||||
|
expect(formComponent.getFormDefinitionByFormName).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 saved = false;
|
||||||
|
formComponent.form = formModel;
|
||||||
|
formComponent.formSaved.subscribe(v => saved = true);
|
||||||
|
spyOn(formComponent, 'completeTaskForm').and.stub();
|
||||||
|
|
||||||
|
let result = formComponent.onOutcomeClicked(outcome);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(saved).toBeTruthy();
|
||||||
|
expect(formComponent.completeTaskForm).toHaveBeenCalledWith(outcomeName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save form on [save] outcome click', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, {
|
||||||
|
id: ActivitiForm.SAVE_OUTCOME_ID,
|
||||||
|
name: 'Save',
|
||||||
|
isSystem: true
|
||||||
|
});
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
spyOn(formComponent, 'saveTaskForm').and.stub();
|
||||||
|
|
||||||
|
let result = formComponent.onOutcomeClicked(outcome);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(formComponent.saveTaskForm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete form on [complete] outcome click', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, {
|
||||||
|
id: ActivitiForm.COMPLETE_OUTCOME_ID,
|
||||||
|
name: 'Complete',
|
||||||
|
isSystem: true
|
||||||
|
});
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
spyOn(formComponent, 'completeTaskForm').and.stub();
|
||||||
|
|
||||||
|
let result = formComponent.onOutcomeClicked(outcome);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(formComponent.completeTaskForm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit form saved event on custom outcome click', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, {
|
||||||
|
id: ActivitiForm.CUSTOM_OUTCOME_ID,
|
||||||
|
name: 'Custom',
|
||||||
|
isSystem: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let saved = false;
|
||||||
|
formComponent.form = formModel;
|
||||||
|
formComponent.formSaved.subscribe(v => saved = true);
|
||||||
|
|
||||||
|
let result = formComponent.onOutcomeClicked(outcome);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(saved).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 });
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
spyOn(formComponent, 'completeTaskForm').and.stub();
|
||||||
|
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeTruthy();
|
||||||
|
formComponent.readOnly = true;
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require outcome model when clicking outcome', () => {
|
||||||
|
formComponent.form = new FormModel();
|
||||||
|
formComponent.readOnly = false;
|
||||||
|
expect(formComponent.onOutcomeClicked(null)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 });
|
||||||
|
|
||||||
|
formComponent.readOnly = false;
|
||||||
|
formComponent.form = null;
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not execute unknown system outcome', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, { id: 'unknown', name: 'Unknown', isSystem: true });
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require custom action name to complete form', () => {
|
||||||
|
let formModel = new FormModel();
|
||||||
|
let outcome = new FormOutcomeModel(formModel, { id: 'custom' });
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeFalsy();
|
||||||
|
|
||||||
|
outcome = new FormOutcomeModel(formModel, { id: 'custom', name: 'Custom' });
|
||||||
|
spyOn(formComponent, 'completeTaskForm').and.stub();
|
||||||
|
expect(formComponent.onOutcomeClicked(outcome)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse form by task id', () => {
|
||||||
|
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next({ taskId: taskId });
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const taskId = '456';
|
||||||
|
let loaded = false;
|
||||||
|
formComponent.formLoaded.subscribe(() => loaded = true);
|
||||||
|
|
||||||
|
expect(formComponent.form).toBeUndefined();
|
||||||
|
formComponent.getFormByTaskId(taskId);
|
||||||
|
|
||||||
|
expect(loaded).toBeTruthy();
|
||||||
|
expect(formService.getTaskForm).toHaveBeenCalledWith(taskId);
|
||||||
|
expect(formComponent.form).toBeDefined();
|
||||||
|
expect(formComponent.form.taskId).toBe(taskId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error when getting form by task id', () => {
|
||||||
|
const error = 'Some error';
|
||||||
|
|
||||||
|
spyOn(formComponent, 'handleError').and.stub();
|
||||||
|
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
|
||||||
|
return Observable.throw(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
formComponent.getFormByTaskId('123');
|
||||||
|
expect(formComponent.handleError).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply readonly state when getting form by task id', () => {
|
||||||
|
spyOn(formService, 'getTaskForm').and.callFake((taskId) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next({ taskId: taskId });
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
formComponent.readOnly = true;
|
||||||
|
formComponent.getFormByTaskId('123');
|
||||||
|
|
||||||
|
expect(formComponent.form).toBeDefined();
|
||||||
|
expect(formComponent.form.readOnly).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse form definition by id', () => {
|
||||||
|
spyOn(formService, 'getFormDefinitionById').and.callFake((formId) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next({ id: formId });
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const formId = '456';
|
||||||
|
let loaded = false;
|
||||||
|
formComponent.formLoaded.subscribe(() => loaded = true);
|
||||||
|
|
||||||
|
expect(formComponent.form).toBeUndefined();
|
||||||
|
formComponent.getFormDefinitionByFormId(formId);
|
||||||
|
|
||||||
|
expect(loaded).toBeTruthy();
|
||||||
|
expect(formService.getFormDefinitionById).toHaveBeenCalledWith(formId);
|
||||||
|
expect(formComponent.form).toBeDefined();
|
||||||
|
expect(formComponent.form.id).toBe(formId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error when getting form by definition id', () => {
|
||||||
|
const error = 'Some error';
|
||||||
|
|
||||||
|
spyOn(formComponent, 'handleError').and.stub();
|
||||||
|
spyOn(formService, 'getFormDefinitionById').and.callFake(() => Observable.throw(error));
|
||||||
|
|
||||||
|
formComponent.getFormDefinitionByFormId('123');
|
||||||
|
expect(formService.getFormDefinitionById).toHaveBeenCalledWith('123');
|
||||||
|
expect(formComponent.handleError).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse form definition by form name', () => {
|
||||||
|
spyOn(formService, 'getFormDefinitionByName').and.callFake((formName) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next(formName);
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(formService, 'getFormDefinitionById').and.callFake((formName) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next({ name: formName });
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const formName = '<form>';
|
||||||
|
let loaded = false;
|
||||||
|
formComponent.formLoaded.subscribe(() => loaded = true);
|
||||||
|
|
||||||
|
expect(formComponent.form).toBeUndefined();
|
||||||
|
formComponent.getFormDefinitionByFormName(formName);
|
||||||
|
|
||||||
|
expect(loaded).toBeTruthy();
|
||||||
|
expect(formService.getFormDefinitionByName).toHaveBeenCalledWith(formName);
|
||||||
|
expect(formComponent.form).toBeDefined();
|
||||||
|
expect(formComponent.form.name).toBe(formName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save task form and raise corresponding event', () => {
|
||||||
|
spyOn(formService, 'saveTaskForm').and.callFake(() => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next();
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let saved = false;
|
||||||
|
let savedForm = null;
|
||||||
|
formComponent.formSaved.subscribe(form => {
|
||||||
|
saved = true;
|
||||||
|
savedForm = form;
|
||||||
|
});
|
||||||
|
|
||||||
|
let formModel = new FormModel({
|
||||||
|
taskId: '123',
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1' },
|
||||||
|
{ id: 'field2' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
formComponent.form = formModel;
|
||||||
|
formComponent.saveTaskForm();
|
||||||
|
|
||||||
|
expect(formService.saveTaskForm).toHaveBeenCalledWith(formModel.taskId, formModel.values);
|
||||||
|
expect(saved).toBeTruthy();
|
||||||
|
expect(savedForm).toEqual(formModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error during form save', () => {
|
||||||
|
const error = 'Error';
|
||||||
|
spyOn(formService, 'saveTaskForm').and.callFake(() => Observable.throw(error));
|
||||||
|
spyOn(formComponent, 'handleError').and.stub();
|
||||||
|
|
||||||
|
formComponent.form = new FormModel({ taskId: '123' });
|
||||||
|
formComponent.saveTaskForm();
|
||||||
|
|
||||||
|
expect(formComponent.handleError).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require form with task id to save', () => {
|
||||||
|
spyOn(formService, 'saveTaskForm').and.stub();
|
||||||
|
|
||||||
|
formComponent.form = null;
|
||||||
|
formComponent.saveTaskForm();
|
||||||
|
|
||||||
|
formComponent.form = new FormModel();
|
||||||
|
formComponent.saveTaskForm();
|
||||||
|
|
||||||
|
expect(formService.saveTaskForm).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require form with task id to complete', () => {
|
||||||
|
spyOn(formService, 'completeTaskForm').and.stub();
|
||||||
|
|
||||||
|
formComponent.form = null;
|
||||||
|
formComponent.completeTaskForm('save');
|
||||||
|
|
||||||
|
formComponent.form = new FormModel();
|
||||||
|
formComponent.completeTaskForm('complete');
|
||||||
|
|
||||||
|
expect(formService.completeTaskForm).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log error to console by default', () => {
|
||||||
|
const error = 'Error';
|
||||||
|
spyOn(console, 'log').and.stub();
|
||||||
|
formComponent.handleError(error);
|
||||||
|
expect(console.log).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete form form and raise corresponding event', () => {
|
||||||
|
spyOn(formService, 'completeTaskForm').and.callFake(() => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
observer.next();
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const outcome = 'complete';
|
||||||
|
let completed = false;
|
||||||
|
formComponent.formCompleted.subscribe(() => completed = true);
|
||||||
|
|
||||||
|
let formModel = new FormModel({
|
||||||
|
taskId: '123',
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1' },
|
||||||
|
{ id: 'field2' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
formComponent.form = formModel;
|
||||||
|
formComponent.completeTaskForm(outcome);
|
||||||
|
|
||||||
|
expect(formService.completeTaskForm).toHaveBeenCalledWith(formModel.taskId, formModel.values, outcome);
|
||||||
|
expect(completed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require json to parse form', () => {
|
||||||
|
expect(formComponent.parseForm(null)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse form from json', () => {
|
||||||
|
let form = formComponent.parseForm({
|
||||||
|
id: '<id>',
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(form).toBeDefined();
|
||||||
|
expect(form.id).toBe('<id>');
|
||||||
|
expect(form.fields.length).toBe(1);
|
||||||
|
expect(form.fields[0].id).toBe('field1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should provide outcomes for form definition', () => {
|
||||||
|
spyOn(formComponent, 'getFormDefinitionOutcomes').and.callThrough();
|
||||||
|
|
||||||
|
let form = formComponent.parseForm({ id: '<id>' });
|
||||||
|
expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
OnInit, AfterViewChecked, OnChanges,
|
OnInit, AfterViewChecked, OnChanges,
|
||||||
SimpleChange,
|
SimpleChanges,
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
EventEmitter
|
EventEmitter
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
import { FormService } from './../services/form.service';
|
import { FormService } from './../services/form.service';
|
||||||
import { FormModel, FormOutcomeModel } from './widgets/widget.model';
|
import { FormModel, FormOutcomeModel, FormValues } from './widgets/core/index';
|
||||||
|
|
||||||
import { TabsWidget } from './widgets/tabs/tabs.widget';
|
import { TabsWidget } from './widgets/tabs/tabs.widget';
|
||||||
import { ContainerWidget } from './widgets/container/container.widget';
|
import { ContainerWidget } from './widgets/container/container.widget';
|
||||||
@@ -39,12 +39,12 @@ declare var componentHandler;
|
|||||||
* ActivitiForm can show 3 forms searching by 3 type of params:
|
* ActivitiForm can show 3 forms searching by 3 type of params:
|
||||||
* 1) Form attached to a task passing the {taskId}.
|
* 1) Form attached to a task passing the {taskId}.
|
||||||
* 2) Form that are only defined with the {formId} (in this case you receive only the form definition and the form will not be
|
* 2) Form that are only defined with the {formId} (in this case you receive only the form definition and the form will not be
|
||||||
* attached to any process, usefull in case you want to use Activitiform as form designer), in this case you can pass also other 2
|
* attached to any process, useful in case you want to use ActivitiForm as form designer), in this case you can pass also other 2
|
||||||
* parameters:
|
* parameters:
|
||||||
* - {saveOption} as parameter to tell what is the function to call on the save action.
|
* - {saveOption} as parameter to tell what is the function to call on the save action.
|
||||||
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
|
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
|
||||||
* 3) Form that are only defined with the {formName} (in this case you receive only the form definition and the form will not be
|
* 3) Form that are only defined with the {formName} (in this case you receive only the form definition and the form will not be
|
||||||
* attached to any process, usefull in case you want to use Activitiform as form designer),
|
* attached to any process, useful in case you want to use ActivitiForm as form designer),
|
||||||
* in this case you can pass also other 2 parameters:
|
* in this case you can pass also other 2 parameters:
|
||||||
* - {saveOption} as parameter to tell what is the function to call on the save action.
|
* - {saveOption} as parameter to tell what is the function to call on the save action.
|
||||||
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
|
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
|
||||||
@@ -74,6 +74,10 @@ declare var componentHandler;
|
|||||||
})
|
})
|
||||||
export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
||||||
|
|
||||||
|
static SAVE_OUTCOME_ID: string = '$save';
|
||||||
|
static COMPLETE_OUTCOME_ID: string = '$complete';
|
||||||
|
static CUSTOM_OUTCOME_ID: string = '$custom';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
taskId: string;
|
taskId: string;
|
||||||
|
|
||||||
@@ -84,7 +88,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
|||||||
formName: string;
|
formName: string;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
data: any;
|
data: FormValues;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showTitle: boolean = true;
|
showTitle: boolean = true;
|
||||||
@@ -102,13 +106,13 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
|||||||
showRefreshButton: boolean = true;
|
showRefreshButton: boolean = true;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
formSaved = new EventEmitter();
|
formSaved: EventEmitter<FormModel> = new EventEmitter<FormModel>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
formCompleted = new EventEmitter();
|
formCompleted: EventEmitter<FormModel> = new EventEmitter<FormModel>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
formLoaded = new EventEmitter();
|
formLoaded: EventEmitter<FormModel> = new EventEmitter<FormModel>();
|
||||||
|
|
||||||
form: FormModel;
|
form: FormModel;
|
||||||
|
|
||||||
@@ -122,155 +126,212 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isTitleEnabled(): boolean {
|
isTitleEnabled(): boolean {
|
||||||
return this.form.taskName && this.showTitle;
|
if (this.showTitle) {
|
||||||
|
if (this.form && this.form.taskName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isOutcomeButtonEnabled(outcome: any): boolean {
|
isOutcomeButtonEnabled(outcome: FormOutcomeModel): boolean {
|
||||||
if (outcome.name === 'Complete') {
|
if (outcome && outcome.name) {
|
||||||
|
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
|
||||||
return this.showCompleteButton;
|
return this.showCompleteButton;
|
||||||
}
|
}
|
||||||
if (outcome.name === 'Save') {
|
if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
|
||||||
return this.showSaveButton;
|
return this.showSaveButton;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.taskId) {
|
this.loadForm();
|
||||||
this.loadForm(this.taskId);
|
|
||||||
}
|
|
||||||
if (this.formId) {
|
|
||||||
this.getFormDefinitionById();
|
|
||||||
}
|
|
||||||
if (this.formName) {
|
|
||||||
this.getFormDefinitionByName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewChecked() {
|
ngAfterViewChecked() {
|
||||||
// workaround for MDL issues with dynamic components
|
this.setupMaterialComponents();
|
||||||
if (componentHandler) {
|
|
||||||
componentHandler.upgradeAllRegistered();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
let taskId = changes['taskId'];
|
let taskId = changes['taskId'];
|
||||||
if (taskId && taskId.currentValue) {
|
if (taskId && taskId.currentValue) {
|
||||||
this.loadForm(taskId.currentValue);
|
this.getFormByTaskId(taskId.currentValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let formId = changes['formId'];
|
let formId = changes['formId'];
|
||||||
if (formId && formId.currentValue) {
|
if (formId && formId.currentValue) {
|
||||||
this.getFormDefinitionById();
|
this.getFormDefinitionByFormId(formId.currentValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let formName = changes['formName'];
|
let formName = changes['formName'];
|
||||||
if (formName && formName.currentValue) {
|
if (formName && formName.currentValue) {
|
||||||
this.getFormDefinitionByName();
|
this.getFormDefinitionByFormName(formName.currentValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
onOutcomeClicked(outcome: FormOutcomeModel, event?: Event) {
|
* Invoked when user clicks outcome button.
|
||||||
if (!this.readOnly && outcome) {
|
* @param outcome Form outcome model
|
||||||
|
* @returns {boolean} True if outcome action was executed, otherwise false.
|
||||||
|
*/
|
||||||
|
onOutcomeClicked(outcome: FormOutcomeModel): boolean {
|
||||||
|
if (!this.readOnly && outcome && this.form) {
|
||||||
if (outcome.isSystem) {
|
if (outcome.isSystem) {
|
||||||
if (outcome.id === '$save') {
|
if (outcome.id === ActivitiForm.SAVE_OUTCOME_ID) {
|
||||||
return this.saveTaskForm();
|
this.saveTaskForm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcome.id === '$complete') {
|
if (outcome.id === ActivitiForm.COMPLETE_OUTCOME_ID) {
|
||||||
return this.completeTaskForm();
|
this.completeTaskForm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcome.id === '$custom') {
|
if (outcome.id === ActivitiForm.CUSTOM_OUTCOME_ID) {
|
||||||
this.formSaved.emit(this.form.values);
|
this.formSaved.emit(this.form);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Note: Activiti is using NAME field rather than ID for outcomes
|
// Note: Activiti is using NAME field rather than ID for outcomes
|
||||||
if (outcome.name) {
|
if (outcome.name) {
|
||||||
this.formSaved.emit(this.form.values);
|
this.formSaved.emit(this.form);
|
||||||
return this.completeTaskForm(outcome.name);
|
this.completeTaskForm(outcome.name);
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when user clicks form refresh button.
|
||||||
|
*/
|
||||||
onRefreshClicked() {
|
onRefreshClicked() {
|
||||||
|
this.loadForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadForm() {
|
||||||
if (this.taskId) {
|
if (this.taskId) {
|
||||||
this.loadForm(this.taskId);
|
this.getFormByTaskId(this.taskId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.formId) {
|
if (this.formId) {
|
||||||
this.getFormDefinitionById();
|
this.getFormDefinitionByFormId(this.formId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.formName) {
|
if (this.formName) {
|
||||||
this.getFormDefinitionByName();
|
this.getFormDefinitionByFormName(this.formName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadForm(taskId: string) {
|
setupMaterialComponents(): boolean {
|
||||||
|
// workaround for MDL issues with dynamic components
|
||||||
|
if (componentHandler) {
|
||||||
|
componentHandler.upgradeAllRegistered();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormByTaskId(taskId: string) {
|
||||||
let data = this.data;
|
let data = this.data;
|
||||||
this.formService
|
this.formService
|
||||||
.getTaskForm(taskId)
|
.getTaskForm(taskId)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
form => {
|
form => {
|
||||||
this.form = new FormModel(form, data, null, this.readOnly);
|
this.form = new FormModel(form, data, this.readOnly);
|
||||||
this.formLoaded.emit(this.form.values);
|
this.formLoaded.emit(this.form);
|
||||||
},
|
},
|
||||||
err => console.log(err)
|
this.handleError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFormDefinitionById() {
|
getFormDefinitionByFormId(formId: string) {
|
||||||
this.formService
|
this.formService
|
||||||
.getFormDefinitionById(this.formId)
|
.getFormDefinitionById(formId)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
form => {
|
form => {
|
||||||
console.log('Get Form By definition Id', form);
|
// console.log('Get Form By definition Id', form);
|
||||||
this.form = new FormModel(form, this.data, this.formSaved, this.readOnly);
|
this.form = this.parseForm(form);
|
||||||
this.formLoaded.emit(this.form.values);
|
this.formLoaded.emit(this.form);
|
||||||
},
|
},
|
||||||
err => console.log(err)
|
this.handleError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFormDefinitionByName() {
|
getFormDefinitionByFormName(formName: string) {
|
||||||
this.formService
|
this.formService
|
||||||
.getFormDefinitionByName(this.formName)
|
.getFormDefinitionByName(formName)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
id => {
|
id => {
|
||||||
this.formService.getFormDefinitionById(id).subscribe(
|
this.formService.getFormDefinitionById(id).subscribe(
|
||||||
form => {
|
form => {
|
||||||
console.log('Get Form By Form definition Name', form);
|
// console.log('Get Form By Form definition Name', form);
|
||||||
this.form = new FormModel(form, this.data, this.formSaved, this.readOnly);
|
this.form = this.parseForm(form);
|
||||||
this.formLoaded.emit(this.form.values);
|
this.formLoaded.emit(this.form);
|
||||||
},
|
},
|
||||||
err => console.log(err)
|
this.handleError
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
err => console.log(err)
|
this.handleError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveTaskForm() {
|
saveTaskForm() {
|
||||||
this.formService.saveTaskForm(this.form.taskId, this.form.values).subscribe(
|
if (this.form && this.form.taskId) {
|
||||||
(response) => {
|
this.formService
|
||||||
console.log('Saved task', response);
|
.saveTaskForm(this.form.taskId, this.form.values)
|
||||||
this.formSaved.emit(this.form.values);
|
.subscribe(
|
||||||
},
|
() => this.formSaved.emit(this.form),
|
||||||
(err) => console.log(err)
|
this.handleError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private completeTaskForm(outcome?: string) {
|
completeTaskForm(outcome?: string) {
|
||||||
|
if (this.form && this.form.taskId) {
|
||||||
this.formService
|
this.formService
|
||||||
.completeTaskForm(this.form.taskId, this.form.values, outcome)
|
.completeTaskForm(this.form.taskId, this.form.values, outcome)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(response) => {
|
() => this.formCompleted.emit(this.form),
|
||||||
console.log('Completed task', response);
|
this.handleError
|
||||||
this.formCompleted.emit(this.form.values);
|
|
||||||
},
|
|
||||||
(err) => console.log(err)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleError(err: any) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseForm(json: any): FormModel {
|
||||||
|
if (json) {
|
||||||
|
let form = new FormModel(json, this.data, this.readOnly);
|
||||||
|
if (!json.fields) {
|
||||||
|
form.outcomes = this.getFormDefinitionOutcomes(form);
|
||||||
|
}
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get custom set of outcomes for a Form Definition.
|
||||||
|
* @param form Form definition model.
|
||||||
|
* @returns {FormOutcomeModel[]} Outcomes for a given form definition.
|
||||||
|
*/
|
||||||
|
getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] {
|
||||||
|
return [
|
||||||
|
new FormOutcomeModel(form, { id: '$custom', name: FormOutcomeModel.SAVE_ACTION, isSystem: true })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="field.id">
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="field.id">
|
||||||
<input type="checkbox" [attr.id]="field.id" class="mdl-checkbox__input" [(ngModel)]="field.value">
|
<input type="checkbox"
|
||||||
|
[attr.id]="field.id"
|
||||||
|
class="mdl-checkbox__input"
|
||||||
|
[(ngModel)]="field.value"
|
||||||
|
[disabled]="field.readOnly">
|
||||||
<span class="mdl-checkbox__label">{{field.name}}</span>
|
<span class="mdl-checkbox__label">{{field.name}}</span>
|
||||||
</label>
|
</label>
|
||||||
|
@@ -0,0 +1,97 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { ContainerWidget } from './container.widget';
|
||||||
|
import { FormModel } from './../core/form.model';
|
||||||
|
import { ContainerModel } from './../core/container.model';
|
||||||
|
import { FormFieldTypes } from './../core/form-field-types';
|
||||||
|
|
||||||
|
describe('ContainerWidget', () => {
|
||||||
|
|
||||||
|
let componentHandler;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
componentHandler = jasmine.createSpyObj('componentHandler', [
|
||||||
|
'upgradeAllRegistered'
|
||||||
|
]);
|
||||||
|
|
||||||
|
window['componentHandler'] = componentHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade MDL content on view init', () => {
|
||||||
|
let container = new ContainerWidget();
|
||||||
|
container.ngAfterViewInit();
|
||||||
|
expect(componentHandler.upgradeAllRegistered).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup MDL content only if component handler available', () => {
|
||||||
|
let container = new ContainerWidget();
|
||||||
|
expect(container.setupMaterialComponents()).toBeTruthy();
|
||||||
|
|
||||||
|
window['componentHandler'] = null;
|
||||||
|
expect(container.setupMaterialComponents()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle underlying group container', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.GROUP,
|
||||||
|
params: {
|
||||||
|
allowCollapse: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let widget = new ContainerWidget();
|
||||||
|
widget.content = container;
|
||||||
|
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
widget.onExpanderClicked();
|
||||||
|
expect(container.isExpanded).toBeFalsy();
|
||||||
|
widget.onExpanderClicked();
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle only collapsible container', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.GROUP
|
||||||
|
});
|
||||||
|
|
||||||
|
let widget = new ContainerWidget();
|
||||||
|
widget.content = container;
|
||||||
|
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
widget.onExpanderClicked();
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle only group container', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.CONTAINER,
|
||||||
|
params: {
|
||||||
|
allowCollapse: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let widget = new ContainerWidget();
|
||||||
|
widget.content = container;
|
||||||
|
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
widget.onExpanderClicked();
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input, AfterViewInit } from '@angular/core';
|
import { Component, Input, AfterViewInit } from '@angular/core';
|
||||||
import { ContainerModel } from './../widget.model';
|
import { ContainerModel } from './../core/index';
|
||||||
|
|
||||||
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
||||||
import { PRIMITIVE_WIDGET_DIRECTIVES } from './../index';
|
import { PRIMITIVE_WIDGET_DIRECTIVES } from './../index';
|
||||||
@@ -46,10 +46,16 @@ export class ContainerWidget implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
this.setupMaterialComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMaterialComponents(): boolean {
|
||||||
// workaround for MDL issues with dynamic components
|
// workaround for MDL issues with dynamic components
|
||||||
if (componentHandler) {
|
if (componentHandler) {
|
||||||
componentHandler.upgradeAllRegistered();
|
componentHandler.upgradeAllRegistered();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { ContainerColumnModel } from './container-column.model';
|
||||||
|
import { FormFieldModel } from './form-field.model';
|
||||||
|
|
||||||
|
describe('ContainerColumnModel', () => {
|
||||||
|
|
||||||
|
it('should have max size by default', () => {
|
||||||
|
let column = new ContainerColumnModel();
|
||||||
|
expect(column.size).toBe(12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check fields', () => {
|
||||||
|
let column = new ContainerColumnModel();
|
||||||
|
|
||||||
|
column.fields = null;
|
||||||
|
expect(column.hasFields()).toBeFalsy();
|
||||||
|
|
||||||
|
column.fields = [];
|
||||||
|
expect(column.hasFields()).toBeFalsy();
|
||||||
|
|
||||||
|
column.fields = [new FormFieldModel(null, null)];
|
||||||
|
expect(column.hasFields()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,28 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormFieldModel } from './form-field.model';
|
||||||
|
|
||||||
|
export class ContainerColumnModel {
|
||||||
|
|
||||||
|
size: number = 12;
|
||||||
|
fields: FormFieldModel[] = [];
|
||||||
|
|
||||||
|
hasFields(): boolean {
|
||||||
|
return this.fields && this.fields.length > 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,140 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { ContainerModel } from './container.model';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { FormFieldTypes } from './form-field-types';
|
||||||
|
|
||||||
|
describe('ContainerModel', () => {
|
||||||
|
|
||||||
|
it('should store the form reference', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let model = new ContainerModel(form);
|
||||||
|
expect(model.form).toBe(form);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let model = new ContainerModel(null, json);
|
||||||
|
expect(model.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have 1 column layout by default', () => {
|
||||||
|
let container = new ContainerModel(null, null);
|
||||||
|
expect(container.numberOfColumns).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be expanded by default', () => {
|
||||||
|
let container = new ContainerModel(null, null);
|
||||||
|
expect(container.isExpanded).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup with json config', () => {
|
||||||
|
let json = {
|
||||||
|
fieldType: '<type>',
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>',
|
||||||
|
type: '<type>',
|
||||||
|
tab: '<tab>',
|
||||||
|
numberOfColumns: 2,
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
let container = new ContainerModel(null, json);
|
||||||
|
Object.keys(json).forEach(key => {
|
||||||
|
expect(container[key]).toEqual(json[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should wrap fields into columns on setup', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let json = {
|
||||||
|
fieldType: '<type>',
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>',
|
||||||
|
type: '<type>',
|
||||||
|
tab: '<tab>',
|
||||||
|
numberOfColumns: 3,
|
||||||
|
params: {},
|
||||||
|
fields: {
|
||||||
|
'1': [
|
||||||
|
{ id: 'field-1' },
|
||||||
|
{ id: 'field-3' }
|
||||||
|
],
|
||||||
|
'2': [
|
||||||
|
{ id: 'field-2' }
|
||||||
|
],
|
||||||
|
'3': null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let container = new ContainerModel(form, json);
|
||||||
|
expect(container.columns.length).toBe(3);
|
||||||
|
|
||||||
|
let col1 = container.columns[0];
|
||||||
|
expect(col1.fields.length).toBe(2);
|
||||||
|
expect(col1.fields[0].id).toBe('field-1');
|
||||||
|
expect(col1.fields[1].id).toBe('field-3');
|
||||||
|
|
||||||
|
let col2 = container.columns[1];
|
||||||
|
expect(col2.fields.length).toBe(1);
|
||||||
|
expect(col2.fields[0].id).toBe('field-2');
|
||||||
|
|
||||||
|
let col3 = container.columns[2];
|
||||||
|
expect(col3.fields.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow collapsing only when of a group type', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.CONTAINER,
|
||||||
|
params: {
|
||||||
|
allowCollapse: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(container.isCollapsible()).toBeFalsy();
|
||||||
|
container.type = FormFieldTypes.GROUP;
|
||||||
|
expect(container.isCollapsible()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow collapsing only when explicitly defined in params', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.GROUP,
|
||||||
|
params: {}
|
||||||
|
});
|
||||||
|
expect(container.isCollapsible()).toBeFalsy();
|
||||||
|
|
||||||
|
container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.GROUP,
|
||||||
|
params: {
|
||||||
|
allowCollapse: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(container.isCollapsible()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be collapsed by default', () => {
|
||||||
|
let container = new ContainerModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.GROUP,
|
||||||
|
params: {
|
||||||
|
allowCollapse: true,
|
||||||
|
collapseByDefault: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(container.isCollapsedByDefault()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,97 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormWidgetModel } from './form-widget.model';
|
||||||
|
import { FormFieldMetadata } from './form-field-metadata';
|
||||||
|
import { ContainerColumnModel } from './container-column.model';
|
||||||
|
import { FormFieldTypes } from './form-field-types';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { FormFieldModel } from './form-field.model';
|
||||||
|
|
||||||
|
// TODO: inherit FormFieldModel
|
||||||
|
export class ContainerModel extends FormWidgetModel {
|
||||||
|
|
||||||
|
fieldType: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
tab: string;
|
||||||
|
numberOfColumns: number = 1;
|
||||||
|
params: FormFieldMetadata = {};
|
||||||
|
|
||||||
|
columns: ContainerColumnModel[] = [];
|
||||||
|
isExpanded: boolean = true;
|
||||||
|
|
||||||
|
isGroup(): boolean {
|
||||||
|
return this.type === FormFieldTypes.GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCollapsible(): boolean {
|
||||||
|
let allowCollapse = false;
|
||||||
|
|
||||||
|
if (this.isGroup() && this.params['allowCollapse']) {
|
||||||
|
allowCollapse = <boolean> this.params['allowCollapse'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowCollapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCollapsedByDefault(): boolean {
|
||||||
|
let collapseByDefault = false;
|
||||||
|
|
||||||
|
if (this.isCollapsible() && this.params['collapseByDefault']) {
|
||||||
|
collapseByDefault = <boolean> this.params['collapseByDefault'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return collapseByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(form: FormModel, json?: any) {
|
||||||
|
super(form, json);
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
this.fieldType = json.fieldType;
|
||||||
|
this.id = json.id;
|
||||||
|
this.name = json.name;
|
||||||
|
this.type = json.type;
|
||||||
|
this.tab = json.tab;
|
||||||
|
this.numberOfColumns = <number> json.numberOfColumns;
|
||||||
|
this.params = <FormFieldMetadata> json.params || {};
|
||||||
|
|
||||||
|
let columnSize: number = 12;
|
||||||
|
if (this.numberOfColumns > 1) {
|
||||||
|
columnSize = 12 / this.numberOfColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.numberOfColumns; i++) {
|
||||||
|
let col = new ContainerColumnModel();
|
||||||
|
col.size = columnSize;
|
||||||
|
this.columns.push(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.fields) {
|
||||||
|
Object.keys(json.fields).map(key => {
|
||||||
|
let fields = (json.fields[key] || []).map(f => new FormFieldModel(form, f));
|
||||||
|
let col = this.columns[parseInt(key, 10) - 1];
|
||||||
|
col.fields = fields;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isExpanded = !this.isCollapsedByDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
/*!
|
||||||
|
* @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 interface FormFieldMetadata {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* @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 interface FormFieldOption {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @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 class FormFieldTypes {
|
||||||
|
static CONTAINER: string = 'container';
|
||||||
|
static GROUP: string = 'group';
|
||||||
|
static DROPDOWN: string = 'dropdown';
|
||||||
|
static HYPERLINK: string = 'hyperlink';
|
||||||
|
static RADIO_BUTTONS: string = 'radio-buttons';
|
||||||
|
static DISPLAY_VALUE: string = 'readonly';
|
||||||
|
static READONLY_TEXT: string = 'readonly-text';
|
||||||
|
|
||||||
|
static READONLY_TYPES: string[] = [
|
||||||
|
FormFieldTypes.HYPERLINK,
|
||||||
|
FormFieldTypes.DISPLAY_VALUE,
|
||||||
|
FormFieldTypes.READONLY_TEXT
|
||||||
|
];
|
||||||
|
|
||||||
|
static isReadOnlyType(type: string) {
|
||||||
|
return FormFieldTypes.READONLY_TYPES.indexOf(type) > -1;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,243 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { FormFieldModel } from './form-field.model';
|
||||||
|
import { FormFieldTypes } from './form-field-types';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
|
||||||
|
|
||||||
|
describe('FormFieldModel', () => {
|
||||||
|
|
||||||
|
it('should store the form reference', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let model = new FormFieldModel(form);
|
||||||
|
expect(model.form).toBe(form);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let model = new FormFieldModel(new FormModel(), json);
|
||||||
|
expect(model.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup with json config', () => {
|
||||||
|
let json = {
|
||||||
|
fieldType: '<fieldType>',
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>',
|
||||||
|
type: '<type>',
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
overrideId: true,
|
||||||
|
tab: '<tab>',
|
||||||
|
restUrl: '<rest-url>',
|
||||||
|
restResponsePath: '<rest-path>',
|
||||||
|
restIdProperty: '<rest-id>',
|
||||||
|
restLabelProperty: '<rest-label>',
|
||||||
|
colspan: 1,
|
||||||
|
options: [],
|
||||||
|
hasEmptyValue: true,
|
||||||
|
className: '<class>',
|
||||||
|
optionType: '<type>',
|
||||||
|
params: {},
|
||||||
|
hyperlinkUrl: '<url>',
|
||||||
|
displayText: '<text>',
|
||||||
|
value: '<value>'
|
||||||
|
};
|
||||||
|
let field = new FormFieldModel(new FormModel(), json);
|
||||||
|
Object.keys(json).forEach(key => {
|
||||||
|
expect(field[key]).toBe(json[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup empty options collection', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), null);
|
||||||
|
expect(field.options).toBeDefined();
|
||||||
|
expect(field.options.length).toBe(0);
|
||||||
|
|
||||||
|
field = new FormFieldModel(new FormModel(), { options: null });
|
||||||
|
expect(field.options).toBeDefined();
|
||||||
|
expect(field.options.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup empty params', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), null);
|
||||||
|
expect(field.params).toEqual({});
|
||||||
|
|
||||||
|
field = new FormFieldModel(new FormModel(), { params: null });
|
||||||
|
expect(field.params).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form on every value change', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, { id: 'field1' });
|
||||||
|
let value = 10;
|
||||||
|
|
||||||
|
spyOn(field, 'updateForm').and.callThrough();
|
||||||
|
field.value = value;
|
||||||
|
|
||||||
|
expect(field.value).toBe(value);
|
||||||
|
expect(field.updateForm).toHaveBeenCalled();
|
||||||
|
expect(form.values['field1']).toBe(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form readonly state', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, null);
|
||||||
|
|
||||||
|
expect(field.readOnly).toBeFalsy();
|
||||||
|
form.readOnly = true;
|
||||||
|
expect(field.readOnly).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should take own readonly state if form is writable', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, { readOnly: true });
|
||||||
|
|
||||||
|
expect(form.readOnly).toBeFalsy();
|
||||||
|
expect(field.readOnly).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse and convert empty dropdown value', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.DROPDOWN,
|
||||||
|
value: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(field.value).toBe('empty');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse and leave dropdown value as is', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.DROPDOWN,
|
||||||
|
options: [],
|
||||||
|
value: 'deferred'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(field.value).toBe('deferred');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse and resolve radio button value', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.RADIO_BUTTONS,
|
||||||
|
options: [
|
||||||
|
{ id: 'opt1', value: 'Option 1' },
|
||||||
|
{ id: 'opt2', value: 'Option 2' }
|
||||||
|
],
|
||||||
|
value: 'opt2'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(field.value).toBe('opt2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse and fall back to first radio button value', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.RADIO_BUTTONS,
|
||||||
|
options: [
|
||||||
|
{ id: 'opt1', value: 'Option 1' },
|
||||||
|
{ id: 'opt2', value: 'Option 2' }
|
||||||
|
],
|
||||||
|
value: 'opt3'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(field.value).toBe('opt1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse and leave radio button value as is', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
type: FormFieldTypes.RADIO_BUTTONS,
|
||||||
|
options: [],
|
||||||
|
value: 'deferred-radio'
|
||||||
|
});
|
||||||
|
expect(field.value).toBe('deferred-radio');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form with empty dropdown value', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, {
|
||||||
|
id: 'dropdown-1',
|
||||||
|
type: FormFieldTypes.DROPDOWN
|
||||||
|
});
|
||||||
|
|
||||||
|
field.value = 'empty';
|
||||||
|
expect(form.values['dropdown-1']).toEqual({});
|
||||||
|
|
||||||
|
field.value = '';
|
||||||
|
expect(form.values['dropdown-1']).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form with dropdown value', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, {
|
||||||
|
id: 'dropdown-2',
|
||||||
|
type: FormFieldTypes.DROPDOWN,
|
||||||
|
options: [
|
||||||
|
{ id: 'opt1', value: 'Option 1' },
|
||||||
|
{ id: 'opt2', value: 'Option 2' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
field.value = 'opt2';
|
||||||
|
expect(form.values['dropdown-2']).toEqual(field.options[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form with radio button value', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, {
|
||||||
|
id: 'radio-1',
|
||||||
|
type: FormFieldTypes.RADIO_BUTTONS,
|
||||||
|
options: [
|
||||||
|
{ id: 'opt1', value: 'Option 1' },
|
||||||
|
{ id: 'opt2', value: 'Option 2' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
field.value = 'opt2';
|
||||||
|
expect(form.values['radio-1']).toEqual(field.options[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form with the first radio button value', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let field = new FormFieldModel(form, {
|
||||||
|
id: 'radio-2',
|
||||||
|
type: FormFieldTypes.RADIO_BUTTONS,
|
||||||
|
options: [
|
||||||
|
{ id: 'opt1', value: 'Option 1' },
|
||||||
|
{ id: 'opt2', value: 'Option 2' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
field.value = 'missing';
|
||||||
|
expect(form.values['radio-2']).toEqual(field.options[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update form with display-only field value', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
|
||||||
|
FormFieldTypes.READONLY_TYPES.forEach(typeName => {
|
||||||
|
let field = new FormFieldModel(form, {
|
||||||
|
id: typeName,
|
||||||
|
type: typeName
|
||||||
|
});
|
||||||
|
|
||||||
|
field.value = '<some value>';
|
||||||
|
expect(form.values[field.id]).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,158 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormWidgetModel } from './form-widget.model';
|
||||||
|
import { FormFieldOption } from './form-field-option';
|
||||||
|
import { FormFieldTypes } from './form-field-types';
|
||||||
|
import { FormFieldMetadata } from './form-field-metadata';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
|
||||||
|
export class FormFieldModel extends FormWidgetModel {
|
||||||
|
|
||||||
|
private _value: string;
|
||||||
|
private _readOnly: boolean = false;
|
||||||
|
|
||||||
|
fieldType: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
required: boolean;
|
||||||
|
overrideId: boolean;
|
||||||
|
tab: string;
|
||||||
|
colspan: number = 1;
|
||||||
|
options: FormFieldOption[] = [];
|
||||||
|
restUrl: string;
|
||||||
|
restResponsePath: string;
|
||||||
|
restIdProperty: string;
|
||||||
|
restLabelProperty: string;
|
||||||
|
hasEmptyValue: boolean;
|
||||||
|
className: string;
|
||||||
|
optionType: string;
|
||||||
|
params: FormFieldMetadata = {};
|
||||||
|
hyperlinkUrl: string;
|
||||||
|
displayText: string;
|
||||||
|
|
||||||
|
get value(): any {
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(v: any) {
|
||||||
|
this._value = v;
|
||||||
|
this.updateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
get readOnly(): boolean {
|
||||||
|
if (this.form && this.form.readOnly) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this._readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(form: FormModel, json?: any) {
|
||||||
|
super(form, json);
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
this.fieldType = json.fieldType;
|
||||||
|
this.id = json.id;
|
||||||
|
this.name = json.name;
|
||||||
|
this.type = json.type;
|
||||||
|
this.required = <boolean> json.required;
|
||||||
|
this._readOnly = <boolean> json.readOnly;
|
||||||
|
this.overrideId = <boolean> json.overrideId;
|
||||||
|
this.tab = json.tab;
|
||||||
|
this.restUrl = json.restUrl;
|
||||||
|
this.restResponsePath = json.restResponsePath;
|
||||||
|
this.restIdProperty = json.restIdProperty;
|
||||||
|
this.restLabelProperty = json.restLabelProperty;
|
||||||
|
this.colspan = <number> json.colspan;
|
||||||
|
this.options = <FormFieldOption[]> json.options || [];
|
||||||
|
this.hasEmptyValue = <boolean> json.hasEmptyValue;
|
||||||
|
this.className = json.className;
|
||||||
|
this.optionType = json.optionType;
|
||||||
|
this.params = <FormFieldMetadata> json.params || {};
|
||||||
|
this.hyperlinkUrl = json.hyperlinkUrl;
|
||||||
|
this.displayText = json.displayText;
|
||||||
|
|
||||||
|
this._value = this.parseValue(json);
|
||||||
|
this.updateForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseValue(json: any): any {
|
||||||
|
let value = json.value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is needed due to Activiti issue related to reading dropdown values as value string
|
||||||
|
but saving back as object: { id: <id>, name: <name> }
|
||||||
|
*/
|
||||||
|
// TODO: needs review
|
||||||
|
if (json.type === FormFieldTypes.DROPDOWN) {
|
||||||
|
if (value === '') {
|
||||||
|
value = 'empty';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is needed due to Activiti issue related to reading radio button values as value string
|
||||||
|
but saving back as object: { id: <id>, name: <name> }
|
||||||
|
*/
|
||||||
|
if (json.type === FormFieldTypes.RADIO_BUTTONS) {
|
||||||
|
// Activiti has a bug with default radio button value,
|
||||||
|
// so try resolving current one with a fallback to first entry
|
||||||
|
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === value);
|
||||||
|
if (entry.length > 0) {
|
||||||
|
value = entry[0].id;
|
||||||
|
} else if (this.options.length > 0) {
|
||||||
|
value = this.options[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateForm() {
|
||||||
|
if (this.type === FormFieldTypes.DROPDOWN) {
|
||||||
|
/*
|
||||||
|
This is needed due to Activiti reading dropdown values as string
|
||||||
|
but saving back as object: { id: <id>, name: <name> }
|
||||||
|
*/
|
||||||
|
if (this.value === 'empty' || this.value === '') {
|
||||||
|
this.form.values[this.id] = {};
|
||||||
|
} else {
|
||||||
|
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
|
||||||
|
if (entry.length > 0) {
|
||||||
|
this.form.values[this.id] = entry[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.type === FormFieldTypes.RADIO_BUTTONS) {
|
||||||
|
/*
|
||||||
|
This is needed due to Activiti issue related to reading radio button values as value string
|
||||||
|
but saving back as object: { id: <id>, name: <name> }
|
||||||
|
*/
|
||||||
|
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
|
||||||
|
if (entry.length > 0) {
|
||||||
|
this.form.values[this.id] = entry[0];
|
||||||
|
} else if (this.options.length > 0) {
|
||||||
|
this.form.values[this.id] = this.options[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!FormFieldTypes.isReadOnlyType(this.type)) {
|
||||||
|
this.form.values[this.id] = this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { FormOutcomeModel } from './form-outcome.model';
|
||||||
|
|
||||||
|
describe('FormOutcomeModel', () => {
|
||||||
|
|
||||||
|
it('should setup with json config', () => {
|
||||||
|
let json = {
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>'
|
||||||
|
};
|
||||||
|
let model = new FormOutcomeModel(null, json);
|
||||||
|
expect(model.id).toBe(json.id);
|
||||||
|
expect(model.name).toBe(json.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store the form reference', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let model = new FormOutcomeModel(form);
|
||||||
|
expect(model.form).toBe(form);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let model = new FormOutcomeModel(null, json);
|
||||||
|
expect(model.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormWidgetModel } from './form-widget.model';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
|
||||||
|
export class FormOutcomeModel extends FormWidgetModel {
|
||||||
|
|
||||||
|
static SAVE_ACTION: string = 'Save'; // Activiti 'Save' action name
|
||||||
|
static COMPLETE_ACTION: string = 'Complete'; // Activiti 'Complete' action name
|
||||||
|
|
||||||
|
private _id: string;
|
||||||
|
private _name: string;
|
||||||
|
|
||||||
|
isSystem: boolean = false;
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(form: FormModel, json?: any) {
|
||||||
|
super(form, json);
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
this._id = json.id;
|
||||||
|
this._name = json.name;
|
||||||
|
this.isSystem = json.isSystem ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormFieldMetadata } from './form-field-metadata';
|
||||||
|
|
||||||
|
export interface FormValues extends FormFieldMetadata {
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { FormWidgetModel } from './form-widget.model';
|
||||||
|
|
||||||
|
describe('FormWidgetModel', () => {
|
||||||
|
|
||||||
|
it('should store the form reference', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let model = new FormWidgetModel(form, null);
|
||||||
|
expect(model.form).toBe(form);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let model = new FormWidgetModel(null, json);
|
||||||
|
expect(model.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormModel } from './form.model';
|
||||||
|
|
||||||
|
export class FormWidgetModel {
|
||||||
|
|
||||||
|
private _form: FormModel;
|
||||||
|
private _json: any;
|
||||||
|
|
||||||
|
get form(): FormModel {
|
||||||
|
return this._form;
|
||||||
|
}
|
||||||
|
|
||||||
|
get json(): any {
|
||||||
|
return this._json;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(form: FormModel, json: any) {
|
||||||
|
this._form = form;
|
||||||
|
this._json = json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormWidgetModelCache<T extends FormWidgetModel> {
|
||||||
|
[key: string]: T;
|
||||||
|
}
|
@@ -0,0 +1,295 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { TabModel } from './tab.model';
|
||||||
|
import { ContainerModel } from './container.model';
|
||||||
|
import { FormOutcomeModel } from './form-outcome.model';
|
||||||
|
import { FormValues } from './form-values';
|
||||||
|
|
||||||
|
describe('FormModel', () => {
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup properties with json', () => {
|
||||||
|
let json = {
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>',
|
||||||
|
taskId: '<task-id>',
|
||||||
|
taskName: '<task-name>'
|
||||||
|
};
|
||||||
|
let form = new FormModel(json);
|
||||||
|
|
||||||
|
Object.keys(json).forEach(key => {
|
||||||
|
expect(form[key]).toEqual(form[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should take form name when task name is missing', () => {
|
||||||
|
let json = {
|
||||||
|
id: '<id>',
|
||||||
|
name: '<name>'
|
||||||
|
};
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.taskName).toBe(json.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use fallback value for task name', () => {
|
||||||
|
let form = new FormModel({});
|
||||||
|
expect(form.taskName).toBe(FormModel.UNSET_TASK_NAME);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set readonly state from params', () => {
|
||||||
|
let form = new FormModel({}, null, true);
|
||||||
|
expect(form.readOnly).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check tabs', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
|
||||||
|
form.tabs = null;
|
||||||
|
expect(form.hasTabs()).toBeFalsy();
|
||||||
|
|
||||||
|
form.tabs = [];
|
||||||
|
expect(form.hasTabs()).toBeFalsy();
|
||||||
|
|
||||||
|
form.tabs = [new TabModel(null)];
|
||||||
|
expect(form.hasTabs()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check fields', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
|
||||||
|
form.fields = null;
|
||||||
|
expect(form.hasFields()).toBeFalsy();
|
||||||
|
|
||||||
|
form.fields = [];
|
||||||
|
expect(form.hasFields()).toBeFalsy();
|
||||||
|
|
||||||
|
form.fields = [new ContainerModel(null)];
|
||||||
|
expect(form.hasFields()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check outcomes', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
|
||||||
|
form.outcomes = null;
|
||||||
|
expect(form.hasOutcomes()).toBeFalsy();
|
||||||
|
|
||||||
|
form.outcomes = [];
|
||||||
|
expect(form.hasOutcomes()).toBeFalsy();
|
||||||
|
|
||||||
|
form.outcomes = [new FormOutcomeModel(null)];
|
||||||
|
expect(form.hasOutcomes()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse tabs', () => {
|
||||||
|
let json = {
|
||||||
|
tabs: [
|
||||||
|
{ id: 'tab1' },
|
||||||
|
{ id: 'tab2' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.tabs.length).toBe(2);
|
||||||
|
expect(form.tabs[0].id).toBe('tab1');
|
||||||
|
expect(form.tabs[1].id).toBe('tab2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse fields', () => {
|
||||||
|
let json = {
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1' },
|
||||||
|
{ id: 'field2' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.fields.length).toBe(2);
|
||||||
|
expect(form.fields[0].id).toBe('field1');
|
||||||
|
expect(form.fields[1].id).toBe('field2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse fields from the definition', () => {
|
||||||
|
let json = {
|
||||||
|
fields: null,
|
||||||
|
formDefinition: {
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1' },
|
||||||
|
{ id: 'field2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.fields.length).toBe(2);
|
||||||
|
expect(form.fields[0].id).toBe('field1');
|
||||||
|
expect(form.fields[1].id).toBe('field2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert missing fields to empty collection', () => {
|
||||||
|
let json = {
|
||||||
|
fields: null
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.fields).toBeDefined();
|
||||||
|
expect(form.fields.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should put fields into corresponding tabs', () => {
|
||||||
|
let json = {
|
||||||
|
tabs: [
|
||||||
|
{ id: 'tab1' },
|
||||||
|
{ id: 'tab2' }
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{ id: 'field1', tab: 'tab1' },
|
||||||
|
{ id: 'field2', tab: 'tab2' },
|
||||||
|
{ id: 'field3', tab: 'tab1' },
|
||||||
|
{ id: 'field4', tab: 'missing-tab' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.tabs.length).toBe(2);
|
||||||
|
expect(form.fields.length).toBe(4);
|
||||||
|
|
||||||
|
let tab1 = form.tabs[0];
|
||||||
|
expect(tab1.fields.length).toBe(2);
|
||||||
|
expect(tab1.fields[0].id).toBe('field1');
|
||||||
|
expect(tab1.fields[1].id).toBe('field3');
|
||||||
|
|
||||||
|
let tab2 = form.tabs[1];
|
||||||
|
expect(tab2.fields.length).toBe(1);
|
||||||
|
expect(tab2.fields[0].id).toBe('field2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply external data', () => {
|
||||||
|
let data: FormValues = {
|
||||||
|
field1: 'one',
|
||||||
|
field2: 'two'
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldType: 'ContainerRepresentation',
|
||||||
|
id: 'container1',
|
||||||
|
type: 'container',
|
||||||
|
numberOfColumns: 2,
|
||||||
|
fields: {
|
||||||
|
'1': [
|
||||||
|
{
|
||||||
|
fieldType: 'FormFieldRepresentation',
|
||||||
|
type: 'text',
|
||||||
|
id: 'field1'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
fieldType: 'FormFieldRepresentation',
|
||||||
|
type: 'text',
|
||||||
|
id: 'field2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldType: 'FormFieldRepresentation',
|
||||||
|
type: 'text',
|
||||||
|
id: 'field3',
|
||||||
|
value: 'original-value'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json, data);
|
||||||
|
expect(form.fields.length).toBe(1);
|
||||||
|
|
||||||
|
let container = form.fields[0];
|
||||||
|
expect(container.columns.length).toBe(2);
|
||||||
|
|
||||||
|
let column1 = container.columns[0];
|
||||||
|
let column2 = container.columns[1];
|
||||||
|
expect(column1.fields.length).toBe(1);
|
||||||
|
expect(column2.fields.length).toBe(2);
|
||||||
|
|
||||||
|
let field1 = column1.fields[0];
|
||||||
|
expect(field1.id).toBe('field1');
|
||||||
|
expect(field1.value).toBe('one');
|
||||||
|
|
||||||
|
let field2 = column2.fields[0];
|
||||||
|
expect(field2.id).toBe('field2');
|
||||||
|
expect(field2.value).toBe('two');
|
||||||
|
|
||||||
|
let field3 = column2.fields[1];
|
||||||
|
expect(field3.id).toBe('field3');
|
||||||
|
expect(field3.value).toBe('original-value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create standard form outcomes', () => {
|
||||||
|
let json = {
|
||||||
|
fields: [
|
||||||
|
{ id: 'container1' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.outcomes.length).toBe(2);
|
||||||
|
|
||||||
|
expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME);
|
||||||
|
expect(form.outcomes[0].isSystem).toBeTruthy();
|
||||||
|
|
||||||
|
expect(form.outcomes[1].id).toBe(FormModel.COMPLETE_OUTCOME);
|
||||||
|
expect(form.outcomes[1].isSystem).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create outcomes only when fields available', () => {
|
||||||
|
let json = {
|
||||||
|
fields: null
|
||||||
|
};
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.outcomes.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom form outcomes', () => {
|
||||||
|
let json = {
|
||||||
|
fields: [
|
||||||
|
{ id: 'container1' }
|
||||||
|
],
|
||||||
|
outcomes: [
|
||||||
|
{ id: 'custom-1', name: 'custom 1' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = new FormModel(json);
|
||||||
|
expect(form.outcomes.length).toBe(2);
|
||||||
|
|
||||||
|
expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME);
|
||||||
|
expect(form.outcomes[0].isSystem).toBeTruthy();
|
||||||
|
|
||||||
|
expect(form.outcomes[1].id).toBe('custom-1');
|
||||||
|
expect(form.outcomes[1].isSystem).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,151 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormWidgetModelCache } from './form-widget.model';
|
||||||
|
import { FormValues } from './form-values';
|
||||||
|
import { ContainerModel } from './container.model';
|
||||||
|
import { TabModel } from './tab.model';
|
||||||
|
import { FormOutcomeModel } from './form-outcome.model';
|
||||||
|
|
||||||
|
export class FormModel {
|
||||||
|
|
||||||
|
static UNSET_TASK_NAME: string = 'Nameless task';
|
||||||
|
static SAVE_OUTCOME: string = '$save';
|
||||||
|
static COMPLETE_OUTCOME: string = '$complete';
|
||||||
|
|
||||||
|
private _id: string;
|
||||||
|
private _name: string;
|
||||||
|
private _taskId: string;
|
||||||
|
private _taskName: string = FormModel.UNSET_TASK_NAME;
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get taskId(): string {
|
||||||
|
return this._taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
get taskName(): string {
|
||||||
|
return this._taskName;
|
||||||
|
}
|
||||||
|
|
||||||
|
readOnly: boolean = false;
|
||||||
|
tabs: TabModel[] = [];
|
||||||
|
fields: ContainerModel[] = [];
|
||||||
|
outcomes: FormOutcomeModel[] = [];
|
||||||
|
|
||||||
|
values: FormValues = {};
|
||||||
|
|
||||||
|
private _json: any;
|
||||||
|
|
||||||
|
get json() {
|
||||||
|
return this._json;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTabs(): boolean {
|
||||||
|
return this.tabs && this.tabs.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFields(): boolean {
|
||||||
|
return this.fields && this.fields.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasOutcomes(): boolean {
|
||||||
|
return this.outcomes && this.outcomes.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(json?: any, data?: FormValues, readOnly: boolean = false) {
|
||||||
|
this.readOnly = readOnly;
|
||||||
|
if (json) {
|
||||||
|
this._json = json;
|
||||||
|
|
||||||
|
this._id = json.id;
|
||||||
|
this._name = json.name;
|
||||||
|
this._taskId = json.taskId;
|
||||||
|
this._taskName = json.taskName || json.name || FormModel.UNSET_TASK_NAME;
|
||||||
|
|
||||||
|
let tabCache: FormWidgetModelCache<TabModel> = {};
|
||||||
|
|
||||||
|
this.tabs = (json.tabs || []).map(t => {
|
||||||
|
let model = new TabModel(this, t);
|
||||||
|
tabCache[model.id] = model;
|
||||||
|
return model;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fields = this.parseContainerFields(json);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
this.loadData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.fields.length; i++) {
|
||||||
|
let field = this.fields[i];
|
||||||
|
if (field.tab) {
|
||||||
|
let tab = tabCache[field.tab];
|
||||||
|
if (tab) {
|
||||||
|
tab.fields.push(new ContainerModel(this, field.json));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
|
||||||
|
|
||||||
|
this.outcomes = [saveOutcome].concat(
|
||||||
|
customOutcomes.length > 0 ? customOutcomes : [completeOutcome]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseContainerFields(json: any): ContainerModel[] {
|
||||||
|
let fields = [];
|
||||||
|
|
||||||
|
if (json.fields) {
|
||||||
|
fields = json.fields;
|
||||||
|
} else if (json.formDefinition && json.formDefinition.fields) {
|
||||||
|
fields = json.formDefinition.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields.map(obj => new ContainerModel(this, obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads external data and overrides field values
|
||||||
|
// Typically used when form definition and form data coming from different sources
|
||||||
|
private loadData(data: FormValues) {
|
||||||
|
for (let i = 0; i < this.fields.length; i++) {
|
||||||
|
let container = this.fields[i];
|
||||||
|
for (let i = 0; i < container.columns.length; i++) {
|
||||||
|
let column = container.columns[i];
|
||||||
|
for (let i = 0; i < column.fields.length; i++) {
|
||||||
|
let field = column.fields[i];
|
||||||
|
if (data[field.id]) {
|
||||||
|
field.json.value = data[field.id];
|
||||||
|
field.value = data[field.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
/*!
|
||||||
|
* @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 * from './form-field-metadata';
|
||||||
|
export * from './form-values';
|
||||||
|
export * from './form-field-types';
|
||||||
|
export * from './form-field-option';
|
||||||
|
export * from './form-widget.model';
|
||||||
|
export * from './form-field.model';
|
||||||
|
export * from './form.model';
|
||||||
|
export * from './container-column.model';
|
||||||
|
export * from './container.model';
|
||||||
|
export * from './tab.model';
|
||||||
|
export * from './form-outcome.model';
|
@@ -0,0 +1,70 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect } from '@angular/core/testing';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
import { TabModel } from './tab.model';
|
||||||
|
import { ContainerModel } from './container.model';
|
||||||
|
|
||||||
|
describe('TabModel', () => {
|
||||||
|
|
||||||
|
it('should setup with json config', () => {
|
||||||
|
let json = {
|
||||||
|
id: '<id>',
|
||||||
|
title: '<title>',
|
||||||
|
visibilityCondition: '<condition>'
|
||||||
|
};
|
||||||
|
|
||||||
|
let model = new TabModel(null, json);
|
||||||
|
expect(model.id).toBe(json.id);
|
||||||
|
expect(model.title).toBe(json.title);
|
||||||
|
expect(model.visibilityCondition).toBe(json.visibilityCondition);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not setup with json config', () => {
|
||||||
|
let model = new TabModel(null, null);
|
||||||
|
expect(model.id).toBeUndefined();
|
||||||
|
expect(model.title).toBeUndefined();
|
||||||
|
expect(model.visibilityCondition).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate content based on fields', () => {
|
||||||
|
let model = new TabModel(null, null);
|
||||||
|
|
||||||
|
model.fields = null;
|
||||||
|
expect(model.hasContent()).toBeFalsy();
|
||||||
|
|
||||||
|
model.fields = [];
|
||||||
|
expect(model.hasContent()).toBeFalsy();
|
||||||
|
|
||||||
|
model.fields = [new ContainerModel(null, null)];
|
||||||
|
expect(model.hasContent()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store the form reference', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
let model = new TabModel(form);
|
||||||
|
expect(model.form).toBe(form);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store original json', () => {
|
||||||
|
let json = {};
|
||||||
|
let model = new TabModel(null, json);
|
||||||
|
expect(model.json).toBe(json);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { FormWidgetModel } from './form-widget.model';
|
||||||
|
import { ContainerModel } from './container.model';
|
||||||
|
import { FormModel } from './form.model';
|
||||||
|
|
||||||
|
export class TabModel extends FormWidgetModel {
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
visibilityCondition: any;
|
||||||
|
|
||||||
|
fields: ContainerModel[] = [];
|
||||||
|
|
||||||
|
hasContent(): boolean {
|
||||||
|
return this.fields && this.fields.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(form: FormModel, json?: any) {
|
||||||
|
super(form, json);
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
this.id = json.id;
|
||||||
|
this.title = json.title;
|
||||||
|
this.visibilityCondition = json.visibilityCondition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,212 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { Http, RequestOptionsArgs, Response, ResponseOptions } from '@angular/http';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import { DropdownWidget } from './dropdown.widget';
|
||||||
|
import { FormModel } from './../core/form.model';
|
||||||
|
import { FormFieldModel } from './../core/form-field.model';
|
||||||
|
|
||||||
|
describe('DropdownWidget', () => {
|
||||||
|
|
||||||
|
let http: Http;
|
||||||
|
let widget: DropdownWidget;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
http = <Http> {
|
||||||
|
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
widget = new DropdownWidget(http);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse REST data on init', () => {
|
||||||
|
|
||||||
|
let data = [
|
||||||
|
{ uid: '1', text: 'One' },
|
||||||
|
{ uid: '2', text: 'Two' }
|
||||||
|
];
|
||||||
|
|
||||||
|
spyOn(http, 'get').and.callFake((url) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
let options = new ResponseOptions({
|
||||||
|
body: data,
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
let response = new Response(options);
|
||||||
|
observer.next(response);
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
optionType: 'rest',
|
||||||
|
restUrl: 'http://<address>',
|
||||||
|
restIdProperty: 'uid',
|
||||||
|
restLabelProperty: 'text'
|
||||||
|
});
|
||||||
|
|
||||||
|
widget.field = field;
|
||||||
|
widget.ngOnInit();
|
||||||
|
|
||||||
|
|
||||||
|
expect((<any>http.get).calls.argsFor(0)).toEqual([field.restUrl]);
|
||||||
|
expect(field.options.length).toBe(2);
|
||||||
|
|
||||||
|
expect(field.options[0].id).toBe(data[0].uid);
|
||||||
|
expect(field.options[0].name).toBe(data[0].text);
|
||||||
|
|
||||||
|
expect(field.options[1].id).toBe(data[1].uid);
|
||||||
|
expect(field.options[1].name).toBe(data[1].text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require REST settings to fetch data', () => {
|
||||||
|
let form = new FormModel();
|
||||||
|
spyOn(http, 'get').and.stub();
|
||||||
|
|
||||||
|
// 1) Null field
|
||||||
|
widget.field = null;
|
||||||
|
widget.ngOnInit();
|
||||||
|
expect(http.get).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 2) Missing [optionType]
|
||||||
|
widget.field = new FormFieldModel(form, {
|
||||||
|
optionType: null,
|
||||||
|
restUrl: 'http://<address>',
|
||||||
|
restIdProperty: 'uid',
|
||||||
|
restLabelProperty: 'text'
|
||||||
|
});
|
||||||
|
widget.ngOnInit();
|
||||||
|
expect(http.get).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 3) Missing [restUrl]
|
||||||
|
widget.field = new FormFieldModel(form, {
|
||||||
|
optionType: 'rest',
|
||||||
|
restUrl: null,
|
||||||
|
restIdProperty: 'uid',
|
||||||
|
restLabelProperty: 'text'
|
||||||
|
});
|
||||||
|
widget.ngOnInit();
|
||||||
|
expect(http.get).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 4) Missing [restIdProperty]
|
||||||
|
widget.field = new FormFieldModel(form, {
|
||||||
|
optionType: 'rest',
|
||||||
|
restUrl: 'http://<address>',
|
||||||
|
restIdProperty: null,
|
||||||
|
restLabelProperty: 'text'
|
||||||
|
});
|
||||||
|
widget.ngOnInit();
|
||||||
|
expect(http.get).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 4) Missing [restLabelProperty]
|
||||||
|
widget.field = new FormFieldModel(form, {
|
||||||
|
optionType: 'rest',
|
||||||
|
restUrl: 'http://<address>',
|
||||||
|
restIdProperty: null,
|
||||||
|
restLabelProperty: null
|
||||||
|
});
|
||||||
|
widget.ngOnInit();
|
||||||
|
expect(http.get).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse only array response', () => {
|
||||||
|
expect(widget.loadFromJson([])).toBeFalsy();
|
||||||
|
|
||||||
|
widget.field = new FormFieldModel(new FormModel());
|
||||||
|
expect(widget.loadFromJson([])).toBeTruthy();
|
||||||
|
|
||||||
|
expect(widget.loadFromJson(null)).toBeFalsy();
|
||||||
|
expect(widget.loadFromJson({})).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should bind to nested properties', () => {
|
||||||
|
let data = [
|
||||||
|
{ uid: { value: 1 }, name: { fullName: 'John Doe' } }
|
||||||
|
];
|
||||||
|
|
||||||
|
spyOn(http, 'get').and.callFake((url) => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
let options = new ResponseOptions({
|
||||||
|
body: data,
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
let response = new Response(options);
|
||||||
|
observer.next(response);
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let field = new FormFieldModel(new FormModel(), {
|
||||||
|
optionType: 'rest',
|
||||||
|
restUrl: 'http://<address>',
|
||||||
|
restIdProperty: 'uid.value',
|
||||||
|
restLabelProperty: 'name.fullName'
|
||||||
|
});
|
||||||
|
|
||||||
|
widget.field = field;
|
||||||
|
widget.ngOnInit();
|
||||||
|
|
||||||
|
expect(field.options.length).toBe(1);
|
||||||
|
expect(field.options[0].id).toBe(data[0].uid.value.toString());
|
||||||
|
expect(field.options[0].name).toBe(data[0].name.fullName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update form upon loading REST data', () => {
|
||||||
|
let field = new FormFieldModel(new FormModel());
|
||||||
|
widget.field = field;
|
||||||
|
|
||||||
|
spyOn(field, 'updateForm').and.stub();
|
||||||
|
|
||||||
|
expect(widget.loadFromJson([])).toBeTruthy();
|
||||||
|
expect(field.updateForm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with generic message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
|
||||||
|
widget.handleError(null);
|
||||||
|
expect(console.error).toHaveBeenCalledWith(DropdownWidget.UNKNOWN_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with error message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
|
||||||
|
const message = '<error>';
|
||||||
|
widget.handleError({ message: message });
|
||||||
|
|
||||||
|
expect(console.error).toHaveBeenCalledWith(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with detailed message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
widget.handleError({
|
||||||
|
status: '400',
|
||||||
|
statusText: 'Bad request'
|
||||||
|
});
|
||||||
|
expect(console.error).toHaveBeenCalledWith('400 - Bad request');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with generic message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
widget.handleError({});
|
||||||
|
expect(console.error).toHaveBeenCalledWith(DropdownWidget.GENERIC_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -16,7 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Rx';
|
|
||||||
import { Http } from '@angular/http';
|
import { Http } from '@angular/http';
|
||||||
import { ObjectUtils } from 'ng2-alfresco-core';
|
import { ObjectUtils } from 'ng2-alfresco-core';
|
||||||
import { WidgetComponent } from './../widget.component';
|
import { WidgetComponent } from './../widget.component';
|
||||||
@@ -32,6 +31,9 @@ declare var componentHandler;
|
|||||||
})
|
})
|
||||||
export class DropdownWidget extends WidgetComponent implements OnInit {
|
export class DropdownWidget extends WidgetComponent implements OnInit {
|
||||||
|
|
||||||
|
static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error';
|
||||||
|
static GENERIC_ERROR_MESSAGE: string = 'Server error';
|
||||||
|
|
||||||
constructor(private http: Http) {
|
constructor(private http: Http) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -51,13 +53,12 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
this.handleError
|
this.handleError
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support 'restResponsePath'
|
// TODO: support 'restResponsePath'
|
||||||
private loadFromJson(json: any) {
|
loadFromJson(json: any): boolean {
|
||||||
if (json instanceof Array) {
|
if (this.field && json && json instanceof Array) {
|
||||||
let options = json.map(obj => {
|
let options = json.map(obj => {
|
||||||
return {
|
return {
|
||||||
id: ObjectUtils.getValue(obj, this.field.restIdProperty).toString(),
|
id: ObjectUtils.getValue(obj, this.field.restIdProperty).toString(),
|
||||||
@@ -66,16 +67,19 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
this.field.options = options;
|
this.field.options = options;
|
||||||
this.field.updateForm();
|
this.field.updateForm();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError (error: any) {
|
|
||||||
// In a real world app, we might use a remote logging infrastructure
|
handleError(error: any) {
|
||||||
// We'd also dig deeper into the error to get a better message
|
let errMsg = DropdownWidget.UNKNOWN_ERROR_MESSAGE;
|
||||||
let errMsg = (error.message) ? error.message :
|
if (error) {
|
||||||
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
|
errMsg = (error.message) ? error.message :
|
||||||
console.error(errMsg); // log to console instead
|
error.status ? `${error.status} - ${error.statusText}` : DropdownWidget.GENERIC_ERROR_MESSAGE;
|
||||||
return Observable.throw(errMsg);
|
}
|
||||||
|
console.error(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,97 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { HyperlinkWidget } from './hyperlink.widget';
|
||||||
|
import { FormModel } from './../core/form.model';
|
||||||
|
import { FormFieldModel } from './../core/form-field.model';
|
||||||
|
|
||||||
|
describe('HyperlinkWidget', () => {
|
||||||
|
|
||||||
|
let widget: HyperlinkWidget;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
widget = new HyperlinkWidget();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get link text from field display text', () => {
|
||||||
|
const text = 'hello world';
|
||||||
|
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
displayText: text
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkText).toBe(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get link text from field url', () => {
|
||||||
|
const url = 'http://<address>';
|
||||||
|
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
displayText: null,
|
||||||
|
hyperlinkUrl: url
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkText).toBe(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require field to get link text', () => {
|
||||||
|
widget.field = null;
|
||||||
|
expect(widget.linkText).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return link text', () => {
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
displayText: null,
|
||||||
|
hyperlinkUrl: null
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkText).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return default url for missing field', () => {
|
||||||
|
widget.field = null;
|
||||||
|
expect(widget.linkUrl).toBe(HyperlinkWidget.DEFAULT_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return default url for missing field property', () => {
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
hyperlinkUrl: null
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkUrl).toBe(HyperlinkWidget.DEFAULT_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend url with scheme', () => {
|
||||||
|
const url = 'www.alfresco.com';
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
hyperlinkUrl: url
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkUrl).toBe(`${HyperlinkWidget.DEFAULT_URL_SCHEME}${url}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend url with scheme', () => {
|
||||||
|
const url = 'https://<secure/address>';
|
||||||
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
|
hyperlinkUrl: url
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(widget.linkUrl).toBe(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -29,21 +29,27 @@ declare var componentHandler;
|
|||||||
})
|
})
|
||||||
export class HyperlinkWidget extends WidgetComponent {
|
export class HyperlinkWidget extends WidgetComponent {
|
||||||
|
|
||||||
|
static DEFAULT_URL: string = '#';
|
||||||
|
static DEFAULT_URL_SCHEME: string = 'http://';
|
||||||
|
|
||||||
get linkUrl(): string {
|
get linkUrl(): string {
|
||||||
let url = '#';
|
let url = HyperlinkWidget.DEFAULT_URL;
|
||||||
|
|
||||||
if (this.field && this.field.hyperlinkUrl) {
|
if (this.field && this.field.hyperlinkUrl) {
|
||||||
url = this.field.hyperlinkUrl;
|
url = this.field.hyperlinkUrl;
|
||||||
if (!/^https?:\/\//i.test(url)) {
|
if (!/^https?:\/\//i.test(url)) {
|
||||||
url = 'http://' + url;
|
url = HyperlinkWidget.DEFAULT_URL_SCHEME + url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
get linkText() {
|
get linkText(): string {
|
||||||
|
if (this.field) {
|
||||||
return this.field.displayText || this.field.hyperlinkUrl;
|
return this.field.displayText || this.field.hyperlinkUrl;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,9 +30,7 @@ import { DisplayTextWidget } from './display-text/display-text.widget';
|
|||||||
|
|
||||||
// core
|
// core
|
||||||
export * from './widget.component';
|
export * from './widget.component';
|
||||||
|
export * from './core/index';
|
||||||
// model
|
|
||||||
export * from './widget.model';
|
|
||||||
|
|
||||||
// containers
|
// containers
|
||||||
export * from './tabs/tabs.widget';
|
export * from './tabs/tabs.widget';
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label multiline-text-widget">
|
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label multiline-text-widget">
|
||||||
<textarea class="mdl-textfield__input" type="text" rows= "3"
|
<textarea class="mdl-textfield__input"
|
||||||
[attr.id]="field.id" [(ngModel)]="field.value">
|
type="text"
|
||||||
|
rows= "3"
|
||||||
|
[attr.id]="field.id"
|
||||||
|
[(ngModel)]="field.value"
|
||||||
|
[disabled]="field.readOnly">
|
||||||
</textarea>
|
</textarea>
|
||||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget">
|
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget">
|
||||||
<input class="mdl-textfield__input" type="text" pattern="-?[0-9]*(\.[0-9]+)?"
|
<input class="mdl-textfield__input"
|
||||||
[attr.id]="field.id" [(ngModel)]="field.value">
|
type="text"
|
||||||
|
pattern="-?[0-9]*(\.[0-9]+)?"
|
||||||
|
[attr.id]="field.id"
|
||||||
|
[(ngModel)]="field.value"
|
||||||
|
[disabled]="field.readOnly">
|
||||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||||
<span class="mdl-textfield__error">Input is not a number!</span>
|
<span class="mdl-textfield__error">Input is not a number!</span>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2,11 +2,12 @@
|
|||||||
<div *ngFor="let opt of field.options">
|
<div *ngFor="let opt of field.options">
|
||||||
<label [attr.for]="opt.id" class="mdl-radio mdl-js-radio">
|
<label [attr.for]="opt.id" class="mdl-radio mdl-js-radio">
|
||||||
<input type="radio"
|
<input type="radio"
|
||||||
|
class="mdl-radio__button"
|
||||||
[checked]="field.value === opt.id"
|
[checked]="field.value === opt.id"
|
||||||
[attr.id]="opt.id"
|
[attr.id]="opt.id"
|
||||||
[attr.name]="field.id"
|
[attr.name]="field.id"
|
||||||
[attr.value]="opt.id"
|
[attr.value]="opt.id"
|
||||||
class="mdl-radio__button"
|
[disabled]="field.readOnly"
|
||||||
(click)="field.value = opt.id">
|
(click)="field.value = opt.id">
|
||||||
<span class="mdl-radio__label">{{opt.name}}</span>
|
<span class="mdl-radio__label">{{opt.name}}</span>
|
||||||
</label>
|
</label>
|
||||||
|
@@ -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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { TabsWidget } from './tabs.widget';
|
||||||
|
import { TabModel } from './../core/tab.model';
|
||||||
|
|
||||||
|
describe('TabsWidget', () => {
|
||||||
|
|
||||||
|
let componentHandler;
|
||||||
|
let widget: TabsWidget;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
widget = new TabsWidget();
|
||||||
|
|
||||||
|
componentHandler = jasmine.createSpyObj('componentHandler', [
|
||||||
|
'upgradeAllRegistered'
|
||||||
|
]);
|
||||||
|
|
||||||
|
window['componentHandler'] = componentHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check tabs', () => {
|
||||||
|
widget.tabs = null;
|
||||||
|
expect(widget.hasTabs()).toBeFalsy();
|
||||||
|
|
||||||
|
widget.tabs = [];
|
||||||
|
expect(widget.hasTabs()).toBeFalsy();
|
||||||
|
|
||||||
|
widget.tabs = [new TabModel(null)];
|
||||||
|
expect(widget.hasTabs()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade MDL content on view init', () => {
|
||||||
|
widget.ngAfterViewInit();
|
||||||
|
expect(componentHandler.upgradeAllRegistered).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup MDL content only if component handler available', () => {
|
||||||
|
expect(widget.setupMaterialComponents()).toBeTruthy();
|
||||||
|
|
||||||
|
window['componentHandler'] = null;
|
||||||
|
expect(widget.setupMaterialComponents()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Component, Input, AfterViewInit } from '@angular/core';
|
import { Component, Input, AfterViewInit } from '@angular/core';
|
||||||
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
|
||||||
import { TabModel } from './../widget.model';
|
import { TabModel } from './../core/index';
|
||||||
import { ContainerWidget } from './../container/container.widget';
|
import { ContainerWidget } from './../container/container.widget';
|
||||||
|
|
||||||
declare let __moduleName: string;
|
declare let __moduleName: string;
|
||||||
@@ -39,9 +39,15 @@ export class TabsWidget implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
this.setupMaterialComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMaterialComponents(): boolean {
|
||||||
// workaround for MDL issues with dynamic components
|
// workaround for MDL issues with dynamic components
|
||||||
if (componentHandler) {
|
if (componentHandler) {
|
||||||
componentHandler.upgradeAllRegistered();
|
componentHandler.upgradeAllRegistered();
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label text-widget">
|
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label text-widget">
|
||||||
<input class="mdl-textfield__input" type="text" [attr.id]="field.id" [(ngModel)]="field.value">
|
<input class="mdl-textfield__input"
|
||||||
|
type="text"
|
||||||
|
[attr.id]="field.id"
|
||||||
|
[(ngModel)]="field.value"
|
||||||
|
[disabled]="field.readOnly">
|
||||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { WidgetComponent } from './widget.component';
|
||||||
|
import { FormFieldModel } from './core/form-field.model';
|
||||||
|
|
||||||
|
describe('WidgetComponent', () => {
|
||||||
|
|
||||||
|
let componentHandler;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
componentHandler = jasmine.createSpyObj('componentHandler', [
|
||||||
|
'upgradeAllRegistered'
|
||||||
|
]);
|
||||||
|
|
||||||
|
window['componentHandler'] = componentHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should upgrade MDL content on view init', () => {
|
||||||
|
let component = new WidgetComponent();
|
||||||
|
component.ngAfterViewInit();
|
||||||
|
expect(componentHandler.upgradeAllRegistered).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup MDL content only if component handler available', () => {
|
||||||
|
let component = new WidgetComponent();
|
||||||
|
expect(component.setupMaterialComponents()).toBeTruthy();
|
||||||
|
|
||||||
|
window['componentHandler'] = null;
|
||||||
|
expect(component.setupMaterialComponents()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check field', () => {
|
||||||
|
let component = new WidgetComponent();
|
||||||
|
|
||||||
|
expect(component.hasField()).toBeFalsy();
|
||||||
|
component.field = new FormFieldModel(null);
|
||||||
|
expect(component.hasField()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Input, AfterViewInit } from '@angular/core';
|
import { Input, AfterViewInit } from '@angular/core';
|
||||||
import { FormFieldModel } from './widget.model';
|
import { FormFieldModel } from './core/index';
|
||||||
|
|
||||||
declare let __moduleName: string;
|
declare let __moduleName: string;
|
||||||
declare var componentHandler;
|
declare var componentHandler;
|
||||||
@@ -34,10 +34,16 @@ export class WidgetComponent implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
this.setupMaterialComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMaterialComponents(): boolean {
|
||||||
// workaround for MDL issues with dynamic components
|
// workaround for MDL issues with dynamic components
|
||||||
if (componentHandler) {
|
if (componentHandler) {
|
||||||
componentHandler.upgradeAllRegistered();
|
componentHandler.upgradeAllRegistered();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,463 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @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 interface FormFieldMetadata {
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FormValues extends FormFieldMetadata {
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FormFieldTypes {
|
|
||||||
static CONTAINER: string = 'container';
|
|
||||||
static GROUP: string = 'group';
|
|
||||||
static DROPDOWN: string = 'dropdown';
|
|
||||||
static HYPERLINK: string = 'hyperlink';
|
|
||||||
static RADIO_BUTTONS: string = 'radio-buttons';
|
|
||||||
static DISPLAY_VALUE: string = 'readonly';
|
|
||||||
static READONLY_TEXT: string = 'readonly-text';
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FormWidgetModel {
|
|
||||||
|
|
||||||
private _form: FormModel;
|
|
||||||
private _json: any;
|
|
||||||
|
|
||||||
get form(): FormModel {
|
|
||||||
return this._form;
|
|
||||||
}
|
|
||||||
|
|
||||||
get json(): any {
|
|
||||||
return this._json;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(form: FormModel, json: any) {
|
|
||||||
this._form = form;
|
|
||||||
this._json = json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FormFieldOption {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FormFieldModel extends FormWidgetModel {
|
|
||||||
|
|
||||||
private _value: string;
|
|
||||||
|
|
||||||
fieldType: string;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
required: boolean;
|
|
||||||
readOnly: boolean;
|
|
||||||
overrideId: boolean;
|
|
||||||
tab: string;
|
|
||||||
colspan: number = 1;
|
|
||||||
options: FormFieldOption[] = [];
|
|
||||||
restUrl: string;
|
|
||||||
restResponsePath: string;
|
|
||||||
restIdProperty: string;
|
|
||||||
restLabelProperty: string;
|
|
||||||
hasEmptyValue: boolean;
|
|
||||||
className: string;
|
|
||||||
optionType: string;
|
|
||||||
params: FormFieldMetadata = {};
|
|
||||||
hyperlinkUrl: string;
|
|
||||||
displayText: string;
|
|
||||||
|
|
||||||
get value(): any {
|
|
||||||
return this._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(v: any) {
|
|
||||||
this._value = v;
|
|
||||||
this.updateForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(form: FormModel, json?: any) {
|
|
||||||
super(form, json);
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
this.fieldType = json.fieldType;
|
|
||||||
this.id = json.id;
|
|
||||||
this.name = json.name;
|
|
||||||
this.type = json.type;
|
|
||||||
this.required = <boolean> json.required;
|
|
||||||
this.readOnly = <boolean> json.readOnly;
|
|
||||||
this.overrideId = <boolean> json.overrideId;
|
|
||||||
this.tab = json.tab;
|
|
||||||
this.restUrl = json.restUrl;
|
|
||||||
this.restResponsePath = json.restResponsePath;
|
|
||||||
this.restIdProperty = json.restIdProperty;
|
|
||||||
this.restLabelProperty = json.restLabelProperty;
|
|
||||||
this.colspan = <number> json.colspan;
|
|
||||||
this.options = <FormFieldOption[]> json.options || [];
|
|
||||||
this.hasEmptyValue = <boolean> json.hasEmptyValue;
|
|
||||||
this.className = json.className;
|
|
||||||
this.optionType = json.optionType;
|
|
||||||
this.params = <FormFieldMetadata> json.params || {};
|
|
||||||
this.hyperlinkUrl = json.hyperlinkUrl;
|
|
||||||
this.displayText = json.displayText;
|
|
||||||
|
|
||||||
this._value = this.parseValue(json);
|
|
||||||
this.updateForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseValue(json: any): any {
|
|
||||||
let value = json.value;
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is needed due to Activiti issue related to reading dropdown values as value string
|
|
||||||
but saving back as object: { id: <id>, name: <name> }
|
|
||||||
*/
|
|
||||||
// TODO: needs review
|
|
||||||
if (json.type === FormFieldTypes.DROPDOWN) {
|
|
||||||
if (value === '') {
|
|
||||||
value = 'empty';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is needed due to Activiti issue related to reading radio button values as value string
|
|
||||||
but saving back as object: { id: <id>, name: <name> }
|
|
||||||
*/
|
|
||||||
if (json.type === FormFieldTypes.RADIO_BUTTONS) {
|
|
||||||
// Activiti has a bug with default radio button value,
|
|
||||||
// so try resolving current one with a fallback to first entry
|
|
||||||
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === value);
|
|
||||||
if (entry.length > 0) {
|
|
||||||
value = entry[0].id;
|
|
||||||
} else if (this.options.length > 0) {
|
|
||||||
value = this.options[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateForm() {
|
|
||||||
if (this.type === FormFieldTypes.DROPDOWN) {
|
|
||||||
/*
|
|
||||||
This is needed due to Activiti reading dropdown values as string
|
|
||||||
but saving back as object: { id: <id>, name: <name> }
|
|
||||||
*/
|
|
||||||
if (this.value === 'empty' || this.value === '') {
|
|
||||||
this.form.values[this.id] = {};
|
|
||||||
} else {
|
|
||||||
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
|
|
||||||
if (entry.length > 0) {
|
|
||||||
this.form.values[this.id] = entry[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.type === FormFieldTypes.RADIO_BUTTONS) {
|
|
||||||
/*
|
|
||||||
This is needed due to Activiti issue related to reading radio button values as value string
|
|
||||||
but saving back as object: { id: <id>, name: <name> }
|
|
||||||
*/
|
|
||||||
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === this.value);
|
|
||||||
if (entry.length > 0) {
|
|
||||||
this.form.values[this.id] = entry[0];
|
|
||||||
} else if (this.options.length > 0) {
|
|
||||||
this.form.values[this.id] = this.options[0].id;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this.isIngonreType()) {
|
|
||||||
this.form.values[this.id] = this.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private isIngonreType(): boolean {
|
|
||||||
if (this.type === FormFieldTypes.READONLY_TEXT) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ContainerColumnModel {
|
|
||||||
|
|
||||||
size: number = 12;
|
|
||||||
fields: FormFieldModel[] = [];
|
|
||||||
|
|
||||||
hasFields(): boolean {
|
|
||||||
return this.fields && this.fields.length > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: inherit FormFieldModel
|
|
||||||
export class ContainerModel extends FormWidgetModel {
|
|
||||||
|
|
||||||
fieldType: string;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
tab: string;
|
|
||||||
numberOfColumns: number = 1;
|
|
||||||
params: FormFieldMetadata = {};
|
|
||||||
|
|
||||||
columns: ContainerColumnModel[] = [];
|
|
||||||
isExpanded: boolean = true;
|
|
||||||
|
|
||||||
isGroup(): boolean {
|
|
||||||
return this.type === FormFieldTypes.GROUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
isCollapsible(): boolean {
|
|
||||||
let allowCollapse = false;
|
|
||||||
|
|
||||||
if (this.isGroup() && this.params['allowCollapse']) {
|
|
||||||
allowCollapse = <boolean> this.params['allowCollapse'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowCollapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
isCollapsedByDefault(): boolean {
|
|
||||||
let collapseByDefault = false;
|
|
||||||
|
|
||||||
if (this.isCollapsible() && this.params['collapseByDefault']) {
|
|
||||||
collapseByDefault = <boolean> this.params['collapseByDefault'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return collapseByDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(form: FormModel, json?: any) {
|
|
||||||
super(form, json);
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
this.fieldType = json.fieldType;
|
|
||||||
this.id = json.id;
|
|
||||||
this.name = json.name;
|
|
||||||
this.type = json.type;
|
|
||||||
this.tab = json.tab;
|
|
||||||
this.numberOfColumns = <number> json.numberOfColumns;
|
|
||||||
this.params = <FormFieldMetadata> json.params || {};
|
|
||||||
|
|
||||||
let columnSize: number = 12;
|
|
||||||
if (this.numberOfColumns > 1) {
|
|
||||||
columnSize = 12 / this.numberOfColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this.numberOfColumns; i++) {
|
|
||||||
let col = new ContainerColumnModel();
|
|
||||||
col.size = columnSize;
|
|
||||||
this.columns.push(col);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(json.fields).map(key => {
|
|
||||||
let fields = (json.fields[key] || []).map(f => new FormFieldModel(form, f));
|
|
||||||
let col = this.columns[parseInt(key, 10) - 1];
|
|
||||||
col.fields = fields;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.isExpanded = !this.isCollapsedByDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TabModel extends FormWidgetModel {
|
|
||||||
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
visibilityCondition: any;
|
|
||||||
|
|
||||||
fields: ContainerModel[] = [];
|
|
||||||
|
|
||||||
hasContent(): boolean {
|
|
||||||
return this.fields && this.fields.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(form: FormModel, json?: any) {
|
|
||||||
super(form, json);
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
this.id = json.id;
|
|
||||||
this.title = json.title;
|
|
||||||
this.visibilityCondition = json.visibilityCondition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WidgetModelCache<T extends FormWidgetModel> {
|
|
||||||
[key: string]: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FormOutcomeModel extends FormWidgetModel {
|
|
||||||
|
|
||||||
private _id: string;
|
|
||||||
private _name: string;
|
|
||||||
|
|
||||||
isSystem: boolean = false;
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return this._id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(form: FormModel, json?: any) {
|
|
||||||
super(form, json);
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
this._id = json.id;
|
|
||||||
this._name = json.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FormModel {
|
|
||||||
|
|
||||||
private UNSET_TASK_NAME: string = 'Nameless task';
|
|
||||||
|
|
||||||
private _id: string;
|
|
||||||
private _name: string;
|
|
||||||
private _taskId: string;
|
|
||||||
private _taskName: string = this.UNSET_TASK_NAME;
|
|
||||||
|
|
||||||
get id(): string {
|
|
||||||
return this._id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name(): string {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get taskId(): string {
|
|
||||||
return this._taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
get taskName(): string {
|
|
||||||
return this._taskName;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabs: TabModel[] = [];
|
|
||||||
fields: ContainerModel[] = [];
|
|
||||||
outcomes: FormOutcomeModel[] = [];
|
|
||||||
|
|
||||||
values: FormValues = {};
|
|
||||||
|
|
||||||
private _json: any;
|
|
||||||
|
|
||||||
get json() {
|
|
||||||
return this._json;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTabs(): boolean {
|
|
||||||
return this.tabs && this.tabs.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasFields(): boolean {
|
|
||||||
return this.fields && this.fields.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasOutcomes(): boolean {
|
|
||||||
return this.outcomes && this.outcomes.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(json?: any, data?: any, saveOption?: any, readOnly: boolean = false) {
|
|
||||||
if (json) {
|
|
||||||
this._json = json;
|
|
||||||
|
|
||||||
this._id = json.id;
|
|
||||||
this._name = json.name;
|
|
||||||
this._taskId = json.taskId;
|
|
||||||
this._taskName = json.taskName || json.name || this.UNSET_TASK_NAME;
|
|
||||||
|
|
||||||
let tabCache: WidgetModelCache<TabModel> = {};
|
|
||||||
|
|
||||||
// this.tabs = (json.tabs || []).map(t => new TabModel(this, t));
|
|
||||||
this.tabs = (json.tabs || []).map(t => {
|
|
||||||
let model = new TabModel(this, t);
|
|
||||||
tabCache[model.id] = model;
|
|
||||||
return model;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.fields = (json.fields || json.formDefinition.fields || []).map(obj => new ContainerModel(this, obj));
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
this.updateFormValueWithProvaidedDataModel(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this.fields.length; i++) {
|
|
||||||
let field = this.fields[i];
|
|
||||||
if (field.tab) {
|
|
||||||
let tab = tabCache[field.tab];
|
|
||||||
if (tab) {
|
|
||||||
tab.fields.push(new ContainerModel(this, field.json));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isATaskForm()) {
|
|
||||||
let saveOutcome = new FormOutcomeModel(this, {id: '$save', name: 'Save'});
|
|
||||||
saveOutcome.isSystem = true;
|
|
||||||
|
|
||||||
let completeOutcome = new FormOutcomeModel(this, {id: '$complete', name: 'Complete'});
|
|
||||||
completeOutcome.isSystem = true;
|
|
||||||
|
|
||||||
let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
|
|
||||||
|
|
||||||
this.outcomes = [saveOutcome].concat(
|
|
||||||
customOutcomes.length > 0 ? customOutcomes : [completeOutcome]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (saveOption && saveOption.observers.length > 0) {
|
|
||||||
let saveOutcome = new FormOutcomeModel(this, {id: '$custom', name: 'Save'});
|
|
||||||
saveOutcome.isSystem = true;
|
|
||||||
|
|
||||||
this.outcomes = [saveOutcome];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateFormValueWithProvaidedDataModel(data: any) {
|
|
||||||
for (let i = 0; i < this.fields.length; i++) {
|
|
||||||
let containerModel = this.fields[i];
|
|
||||||
if (containerModel) {
|
|
||||||
for (let i = 0; i < containerModel.columns.length; i++) {
|
|
||||||
let containerModelColumn = containerModel.columns[i];
|
|
||||||
if (containerModelColumn) {
|
|
||||||
for (let i = 0; i < containerModelColumn.fields.length; i++) {
|
|
||||||
let formField = containerModelColumn.fields[i];
|
|
||||||
if (data[formField.id]) {
|
|
||||||
formField.value = data[formField.id];
|
|
||||||
formField.json.value = data[formField.id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the form is associated to a task or if is only the form definition
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
private isATaskForm(): boolean {
|
|
||||||
return this._json.fields ? true : false;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,320 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||||
|
import { Http, RequestOptionsArgs, Response, ResponseOptions } from '@angular/http';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
|
import { FormService } from './form.service';
|
||||||
|
import { FormValues } from './../components/widgets/core/index';
|
||||||
|
|
||||||
|
describe('FormService', () => {
|
||||||
|
|
||||||
|
let http: Http;
|
||||||
|
let responseBody: any;
|
||||||
|
let formService: FormService;
|
||||||
|
let authService: AlfrescoAuthenticationService;
|
||||||
|
let settingsService: AlfrescoSettingsService;
|
||||||
|
|
||||||
|
let createResponse = (url, body): Observable<Response> => {
|
||||||
|
return Observable.create(observer => {
|
||||||
|
let response = new Response(new ResponseOptions({
|
||||||
|
url: url,
|
||||||
|
body: body
|
||||||
|
}));
|
||||||
|
observer.next(response);
|
||||||
|
observer.complete();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
|
||||||
|
http = <Http> {
|
||||||
|
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||||
|
return createResponse(url, responseBody);
|
||||||
|
},
|
||||||
|
post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
|
||||||
|
return createResponse(url, responseBody);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
settingsService = new AlfrescoSettingsService();
|
||||||
|
settingsService.setProviders([]);
|
||||||
|
|
||||||
|
authService = new AlfrescoAuthenticationService(settingsService, null);
|
||||||
|
formService = new FormService(http, authService, settingsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve host address via settings service', () => {
|
||||||
|
const url = '<url>';
|
||||||
|
settingsService.bpmHost = url;
|
||||||
|
expect(formService.getHostAddress()).toBe(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse process definitions', (done) => {
|
||||||
|
spyOn(http, 'get').and.callThrough();
|
||||||
|
|
||||||
|
responseBody = {
|
||||||
|
data: [
|
||||||
|
{ id: '1' },
|
||||||
|
{ id: '2' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.getProcessDefinitions().subscribe(result => {
|
||||||
|
expect(http.get).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).get.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/process-definitions')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(responseBody.data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse tasks', (done) => {
|
||||||
|
spyOn(http, 'post').and.callThrough();
|
||||||
|
|
||||||
|
responseBody = {
|
||||||
|
data: [
|
||||||
|
{ id: '1' },
|
||||||
|
{ id: '2' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.getTasks().subscribe(result => {
|
||||||
|
expect(http.post).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).post.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/tasks/query')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(responseBody.data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and parse the task by id', (done) => {
|
||||||
|
spyOn(http, 'get').and.callThrough();
|
||||||
|
|
||||||
|
responseBody = {
|
||||||
|
id: '1'
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.getTask('1').subscribe(result => {
|
||||||
|
expect(http.get).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).get.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/tasks/1')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(responseBody);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save task form', (done) => {
|
||||||
|
spyOn(http, 'post').and.callThrough();
|
||||||
|
|
||||||
|
let values = <FormValues> {
|
||||||
|
field1: 'one',
|
||||||
|
field2: 'two'
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.saveTaskForm('1', values).subscribe(() => {
|
||||||
|
expect(http.post).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).post.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/task-forms/1/save-form')).toBeTruthy();
|
||||||
|
expect(args[1]).toEqual(JSON.stringify({ values: values }));
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete task form', (done) => {
|
||||||
|
spyOn(http, 'post').and.callThrough();
|
||||||
|
|
||||||
|
let values = <FormValues> {
|
||||||
|
field1: 'one',
|
||||||
|
field2: 'two'
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.completeTaskForm('1', values).subscribe(() => {
|
||||||
|
expect(http.post).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).post.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/task-forms/1')).toBeTruthy();
|
||||||
|
expect(args[1]).toEqual(JSON.stringify({ values: values }));
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete task form with a specific outcome', (done) => {
|
||||||
|
spyOn(http, 'post').and.callThrough();
|
||||||
|
|
||||||
|
let values = <FormValues> {
|
||||||
|
field1: 'one',
|
||||||
|
field2: 'two'
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.completeTaskForm('1', values, 'custom').subscribe(() => {
|
||||||
|
expect(http.post).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).post.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/task-forms/1')).toBeTruthy();
|
||||||
|
expect(args[1]).toEqual(JSON.stringify({ values: values, outcome: 'custom' }));
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get task form by id', (done) => {
|
||||||
|
spyOn(http, 'get').and.callThrough();
|
||||||
|
|
||||||
|
responseBody = { id: '1' };
|
||||||
|
|
||||||
|
formService.getTaskForm('1').subscribe(result => {
|
||||||
|
expect(http.get).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).get.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/task-forms/1')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(responseBody);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form definition by id', (done) => {
|
||||||
|
spyOn(http, 'get').and.callThrough();
|
||||||
|
|
||||||
|
responseBody = { id: '1' };
|
||||||
|
|
||||||
|
formService.getFormDefinitionById('1').subscribe(result => {
|
||||||
|
expect(http.get).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).get.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith('/form-models/1')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(responseBody);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get form definition id by name', (done) => {
|
||||||
|
spyOn(http, 'get').and.callThrough();
|
||||||
|
|
||||||
|
const formName = 'form1';
|
||||||
|
const formId = 1;
|
||||||
|
responseBody = {
|
||||||
|
data: [
|
||||||
|
{ id: formId }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
formService.getFormDefinitionByName(formName).subscribe(result => {
|
||||||
|
expect(http.get).toHaveBeenCalled();
|
||||||
|
|
||||||
|
let args: any[] = (<any>http).get.calls.argsFor(0);
|
||||||
|
expect(args[0].endsWith(`models?filter=myReusableForms&filterText=${formName}&modelType=2`)).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result).toEqual(formId);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not get form id from response', () => {
|
||||||
|
let response = new Response(new ResponseOptions({ body: null }));
|
||||||
|
expect(formService.getFormId(response)).toBeNull();
|
||||||
|
|
||||||
|
response = new Response(new ResponseOptions({ body: {} }));
|
||||||
|
expect(formService.getFormId(response)).toBeNull();
|
||||||
|
|
||||||
|
response = new Response(new ResponseOptions({ body: { data: null } }));
|
||||||
|
expect(formService.getFormId(response)).toBeNull();
|
||||||
|
|
||||||
|
response = new Response(new ResponseOptions({ body: { data: [] } }));
|
||||||
|
expect(formService.getFormId(response)).toBeNull();
|
||||||
|
|
||||||
|
expect(formService.getFormId(null)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert response to json object', () => {
|
||||||
|
let data = { id: 1 };
|
||||||
|
let response = new Response(new ResponseOptions({ body: data }));
|
||||||
|
expect(formService.toJson(response)).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to empty json object', () => {
|
||||||
|
let response = new Response(new ResponseOptions({ body: null }));
|
||||||
|
expect(formService.toJson(response)).toEqual({});
|
||||||
|
|
||||||
|
expect(formService.toJson(null)).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert response to json array', () => {
|
||||||
|
let payload = {
|
||||||
|
data: [
|
||||||
|
{ id: 1 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = new Response(new ResponseOptions({ body: JSON.stringify(payload) }));
|
||||||
|
expect(formService.toJsonArray(response)).toEqual(payload.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to empty json array', () => {
|
||||||
|
expect(formService.toJsonArray(null)).toEqual([]);
|
||||||
|
|
||||||
|
let response = new Response(new ResponseOptions({ body: {} }));
|
||||||
|
expect(formService.toJsonArray(response)).toEqual([]);
|
||||||
|
|
||||||
|
response = new Response(new ResponseOptions({ body: { data: null } }));
|
||||||
|
expect(formService.toJsonArray(response)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with generic message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
|
||||||
|
formService.handleError(null);
|
||||||
|
expect(console.error).toHaveBeenCalledWith(FormService.UNKNOWN_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with error message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
|
||||||
|
const message = '<error>';
|
||||||
|
formService.handleError({ message: message });
|
||||||
|
|
||||||
|
expect(console.error).toHaveBeenCalledWith(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with detailed message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
formService.handleError({
|
||||||
|
status: '400',
|
||||||
|
statusText: 'Bad request'
|
||||||
|
});
|
||||||
|
expect(console.error).toHaveBeenCalledWith('400 - Bad request');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error with generic message', () => {
|
||||||
|
spyOn(console, 'error').and.stub();
|
||||||
|
formService.handleError({});
|
||||||
|
expect(console.error).toHaveBeenCalledWith(FormService.GENERIC_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
});
|
@@ -19,19 +19,26 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Response, Http, Headers, RequestOptions } from '@angular/http';
|
import { Response, Http, Headers, RequestOptions } from '@angular/http';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
||||||
import { FormValues } from './../components/widgets/widget.model';
|
import { FormValues } from './../components/widgets/core/index';
|
||||||
import { AlfrescoSettingsService } from 'ng2-alfresco-core';
|
import { AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FormService {
|
export class FormService {
|
||||||
|
|
||||||
|
static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error';
|
||||||
|
static GENERIC_ERROR_MESSAGE: string = 'Server error';
|
||||||
|
|
||||||
constructor(private http: Http,
|
constructor(private http: Http,
|
||||||
private authService: AlfrescoAuthenticationService,
|
private authService: AlfrescoAuthenticationService,
|
||||||
private alfrescoSettingsService: AlfrescoSettingsService) {
|
private alfrescoSettingsService: AlfrescoSettingsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHostAddress(): string {
|
||||||
|
return this.alfrescoSettingsService.bpmHost;
|
||||||
|
}
|
||||||
|
|
||||||
getProcessDefinitions(): Observable<any> {
|
getProcessDefinitions(): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/process-definitions`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/process-definitions`;
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
return this.http
|
return this.http
|
||||||
.get(url, options)
|
.get(url, options)
|
||||||
@@ -40,7 +47,7 @@ export class FormService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTasks(): Observable<any> {
|
getTasks(): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/tasks/query`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/tasks/query`;
|
||||||
let body = JSON.stringify({});
|
let body = JSON.stringify({});
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
@@ -51,7 +58,7 @@ export class FormService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTask(id: string): Observable<any> {
|
getTask(id: string): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/tasks/${id}`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/tasks/${id}`;
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
@@ -61,10 +68,8 @@ export class FormService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveTaskForm(id: string, formValues: FormValues): Observable<Response> {
|
saveTaskForm(id: string, formValues: FormValues): Observable<Response> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/task-forms/${id}/save-form`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/task-forms/${id}/save-form`;
|
||||||
let body = JSON.stringify({
|
let body = JSON.stringify({ values: formValues });
|
||||||
values: formValues
|
|
||||||
});
|
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
@@ -72,8 +77,15 @@ export class FormService {
|
|||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete Task Form
|
||||||
|
* @param id Task Id
|
||||||
|
* @param formValues Form Values
|
||||||
|
* @param outcome Form Outcome
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
completeTaskForm(id: string, formValues: FormValues, outcome?: string): Observable<Response> {
|
completeTaskForm(id: string, formValues: FormValues, outcome?: string): Observable<Response> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/task-forms/${id}`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/task-forms/${id}`;
|
||||||
let data: any = { values: formValues };
|
let data: any = { values: formValues };
|
||||||
if (outcome) {
|
if (outcome) {
|
||||||
data.outcome = outcome;
|
data.outcome = outcome;
|
||||||
@@ -87,7 +99,7 @@ export class FormService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTaskForm(id: string): Observable<any> {
|
getTaskForm(id: string): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/task-forms/${id}`;
|
let url = `${this.getHostAddress()}/activiti-app/api/enterprise/task-forms/${id}`;
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
@@ -97,7 +109,7 @@ export class FormService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFormDefinitionById(id: string): Observable<any> {
|
getFormDefinitionById(id: string): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/app/rest/form-models/${id}`;
|
let url = `${this.getHostAddress()}/activiti-app/app/rest/form-models/${id}`;
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
@@ -106,9 +118,13 @@ export class FormService {
|
|||||||
.catch(this.handleError);
|
.catch(this.handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns form definition ID by a given name.
|
||||||
|
* @param name
|
||||||
|
* @returns {Promise<T>|Promise<ErrorObservable>}
|
||||||
|
*/
|
||||||
getFormDefinitionByName(name: string): Observable<any> {
|
getFormDefinitionByName(name: string): Observable<any> {
|
||||||
let url = `${this.alfrescoSettingsService.bpmHost}` +
|
let url = `${this.getHostAddress()}/activiti-app/app/rest/models?filter=myReusableForms&filterText=${name}&modelType=2`;
|
||||||
`/activiti-app/app/rest/models?filter=myReusableForms&filterText=${name}&modelType=2`;
|
|
||||||
let options = this.getRequestOptions();
|
let options = this.getRequestOptions();
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
@@ -130,27 +146,42 @@ export class FormService {
|
|||||||
return new RequestOptions({headers: headers});
|
return new RequestOptions({headers: headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFormId(res: Response) {
|
getFormId(res: Response) {
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
if (res) {
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body.data[0].id || {};
|
if (body && body.data && body.data.length > 0) {
|
||||||
|
result = body.data[0].id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private toJson(res: Response) {
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson(res: Response) {
|
||||||
|
if (res) {
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body || {};
|
return body || {};
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
private toJsonArray(res: Response) {
|
toJsonArray(res: Response) {
|
||||||
|
if (res) {
|
||||||
let body = res.json();
|
let body = res.json();
|
||||||
return body.data || [];
|
return body.data || [];
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
private handleError(error: any) {
|
handleError(error: any): Observable<any> {
|
||||||
// In a real world app, we might use a remote logging infrastructure
|
let errMsg = FormService.UNKNOWN_ERROR_MESSAGE;
|
||||||
// We'd also dig deeper into the error to get a better message
|
if (error) {
|
||||||
let errMsg = (error.message) ? error.message :
|
errMsg = (error.message) ? error.message :
|
||||||
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
|
error.status ? `${error.status} - ${error.statusText}` : FormService.GENERIC_ERROR_MESSAGE;
|
||||||
console.error(errMsg); // log to console instead
|
}
|
||||||
|
console.error(errMsg);
|
||||||
return Observable.throw(errMsg);
|
return Observable.throw(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,13 +35,15 @@ export class AlfrescoAuthenticationService extends AlfrescoAuthenticationBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param alfrescoSetting
|
* @param settingsService
|
||||||
* @param http
|
* @param http
|
||||||
*/
|
*/
|
||||||
constructor(alfrescoSetting: AlfrescoSettingsService,
|
constructor(settingsService: AlfrescoSettingsService,
|
||||||
http: Http) {
|
http: Http) {
|
||||||
super(alfrescoSetting, http);
|
super(settingsService, http);
|
||||||
this.createProviderInstance(alfrescoSetting.getProviders());
|
if (settingsService) {
|
||||||
|
this.createProviderInstance(settingsService.getProviders());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user