basic form events hooks and readme updates (#1528)

* basic form events hooks

FormService now exposes the following events:

- formLoaded (formRendered alternative)
- formFieldValueChanged
- taskCompleted
- taskCompletedError
- taskSaved
- taskSavedError

* readme updates
This commit is contained in:
Denys Vuika
2017-01-25 10:44:41 +00:00
committed by Mario Romano
parent dd753ba890
commit bd485c730f
10 changed files with 263 additions and 38 deletions

View File

@@ -40,7 +40,7 @@ import {
DataSorting
} from 'ng2-alfresco-datatable';
import { AlfrescoApiService } from 'ng2-alfresco-core';
import { FormRenderingService } from 'ng2-activiti-form';
import { FormService, FormRenderingService, FormEvent, FormFieldEvent } from 'ng2-activiti-form';
import { /*CustomEditorComponent*/ CustomStencil01 } from './custom-editor/custom-editor.component';
declare var componentHandler;
@@ -99,7 +99,8 @@ export class ActivitiDemoComponent implements AfterViewInit {
constructor(private elementRef: ElementRef,
private route: ActivatedRoute,
private apiService: AlfrescoApiService,
private formRenderingService: FormRenderingService) {
private formRenderingService: FormRenderingService,
private formService: FormService) {
this.dataTasks = new ObjectDataTableAdapter(
[],
[
@@ -123,6 +124,14 @@ export class ActivitiDemoComponent implements AfterViewInit {
// Uncomment this line to map 'custom_stencil_01' to local editor component
formRenderingService.setComponentTypeResolver('custom_stencil_01', () => CustomStencil01, true);
formService.formLoaded.subscribe((e: FormEvent) => {
console.log(`Form loaded: ${e.form.id}`);
});
formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => {
console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`);
});
}
ngOnInit() {

View File

@@ -31,6 +31,22 @@
</a>
</p>
## Library Contents
### Components
- [ActivitiForm](#activitiform-component)
- ActivitiStartForm
### Services
- [FormService](#formservice)
- ActivitiAlfrescoContentService
- EcmModelService
- FormRenderingService
- NodeService
- WidgetVisibilityService
## Prerequisites
Before you start using this development framework, make sure you have installed all required software and done all the
@@ -101,7 +117,9 @@ Follow the 3 steps below:
Please refer to the following example file: [systemjs.config.js](demo/systemjs
.config.js) .
## Basic usage examples
## ActivitiForm Component
### Basic usage
The component shows a Form from Activiti
@@ -112,6 +130,7 @@ The component shows a Form from Activiti
Usage example of this component :
**main.ts**
```ts
import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@@ -223,9 +242,9 @@ and store the form field as metadata. The param nameNode is optional.
</activiti-form>
```
## Configuration
### Configuration
### Properties
#### Properties
The recommended set of properties can be found in the following table:
@@ -246,7 +265,7 @@ The recommended set of properties can be found in the following table:
| `path` | string | | Path of the folder where to store the metadata. |
| `nameNode` (optional) | string | true | Name to assign to the new node where the metadata are stored. |
#### Advanced properties
##### Advanced properties
The following properties are for complex customisation purposes:
@@ -255,7 +274,7 @@ The recommended set of properties can be found in the following table:
| `form` | FormModel | | Underlying form model instance. |
| `debugMode` | boolean | false | Toggle debug mode, allows displaying additional data for development and debugging purposes. |
### Events
#### Events
| Name | Description |
| --- | --- |
@@ -332,6 +351,70 @@ There are two additional functions that can be of a great value when controlling
**Please note that if `event.preventDefault()` is not called then default outcome behaviour
will also be executed after your custom code.**
## FormService
```ts
import { Component } from '@angular/core';
import { FormService, FormEvent, FormFieldEvent } from 'ng2-activiti-form';
@Component(...)
class MyComponent {
constructor(private formService: FormService) {
formService.formLoaded.subscribe((e: FormEvent) => {
console.log(`Form loaded: ${e.form.id}`);
});
formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => {
console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`);
});
}
}
```
### Events
| Name | Args Type | Description |
| --- | --- | --- |
| formLoaded | FormEvent | Raised when form has been loaded or reloaded |
| formFieldValueChanged | FormFieldEvent | Raised when input values change |
| taskCompleted | FormEvent | Raised when a task is completed successfully |
| taskCompletedError | FormErrorEvent | Raised when a task is completed unsuccessfully |
| taskSaved | FormEvent | Raised when a task is saved successfully |
| taskSavedError | FormErrorEvent | Raised when a task is saved unsuccessfully |
### Methods
| Name | Params | Returns | Description |
| --- | --- | --- | --- |
| createFormFromANode | (formName: string) | Observable<any\> | Create a Form with a fields for each metadata properties |
| createForm | (formName: string) | Observable<any\> | Create a Form |
| addFieldsToAForm | (formId: string, formModel: FormDefinitionModel) | Observable<any\> | Add Fileds to A form |
| searchFrom | (name: string) | Observable<any\> | Search For A Form by name |
| getForms | n/a | Observable<any\> | Get All the forms |
| getProcessDefinitions | n/a | Observable<any\> | Get Process Definitions |
| getTasks | n/a | Observable<any\> | Get All the Tasks |
| getTask | (taskId: string) | Observable<any\> | Get Task |
| saveTaskForm | (taskId: string, formValues: FormValues) | Observable<any\> | Save Task Form |
| completeTaskForm | (taskId: string, formValues: FormValues, outcome?: string) | Observable<any\> | Complete Task Form |
| getTaskForm | (taskId: string) | Observable<any\> | Get Form related to a taskId |
| getFormDefinitionById | (formId: string) | Observable<any\> | Get Form Definition |
| getFormDefinitionByName | (name: string) | Observable<any\> | Returns form definition by a given name. |
| getStartFormInstance | (processId: string) | Observable<any\> | Get start form instance for a given processId |
| getStartFormDefinition | (processId: string) | Observable<any\> | Get start form definition for a given process |
| createTemporaryRawRelatedContent | (file: any) | Observable<any\> | Save File |
| getRestFieldValues | (taskId: string, field: string) | Observable<any\> | |
| getRestFieldValuesByProcessId | (processDefinitionId: string, field: string) | Observable<any\> | |
| getRestFieldValuesColumnByProcessId | (processDefinitionId: string, field: string, column?: string) | Observable<any\> | |
| getRestFieldValuesColumn | (taskId: string, field: string, column?: string) | Observable<any\> | |
| getWorkflowGroups\* | (filter: string, groupId?: string) | Observable<GroupModel[]\> | |
| getWorkflowUsers\* | (filter: string, groupId?: string) | Observable<GroupUserModel[]\> | |
\* _Uses private Activiti WebApp api_
## See also
- [Form Stencils with Angular 2](docs/stencils.md)

View File

@@ -37,6 +37,7 @@ export * from './src/components/widgets/index';
export * from './src/services/ecm-model.service';
export * from './src/services/node.service';
export * from './src/services/form-rendering.service';
export * from './src/events/index';
export const ACTIVITI_FORM_DIRECTIVES: any[] = [
ActivitiForm,

View File

@@ -15,21 +15,13 @@
* limitations under the License.
*/
import {
Component,
OnInit,
AfterViewChecked,
OnChanges,
SimpleChanges,
Input,
Output,
EventEmitter
} from '@angular/core';
import { Component, OnInit, AfterViewChecked, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { EcmModelService } from './../services/ecm-model.service';
import { FormService } from './../services/form.service';
import { NodeService } from './../services/node.service';
import { FormModel, FormOutcomeModel, FormValues, FormFieldModel, FormOutcomeEvent } from './widgets/core/index';
import { FormEvent, FormErrorEvent } from './../events/index';
import { WidgetVisibilityService } from './../services/widget-visibility.service';
@@ -272,14 +264,14 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
}
if (outcome.id === ActivitiForm.CUSTOM_OUTCOME_ID) {
this.formSaved.emit(this.form);
this.onTaskSaved(this.form);
this.storeFormAsMetadata();
return true;
}
} else {
// Note: Activiti is using NAME field rather than ID for outcomes
if (outcome.name) {
this.formSaved.emit(this.form);
this.onTaskSaved(this.form);
this.completeTaskForm(outcome.name);
return true;
}
@@ -345,8 +337,8 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.getTaskForm(taskId)
.subscribe(
form => {
this.form = new FormModel(form, data, this.readOnly);
this.formLoaded.emit(this.form);
this.form = new FormModel(form, data, this.readOnly, this.formService);
this.onFormLoaded(this.form);
},
(error) => {
this.handleError(error);
@@ -361,7 +353,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
form => {
this.formName = form.name;
this.form = this.parseForm(form);
this.formLoaded.emit(this.form);
this.onFormLoaded(this.form);
},
(error) => {
this.handleError(error);
@@ -377,7 +369,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
this.formService.getFormDefinitionById(id).subscribe(
form => {
this.form = this.parseForm(form);
this.formLoaded.emit(this.form);
this.onFormLoaded(this.form);
},
(error) => {
this.handleError(error);
@@ -396,12 +388,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.saveTaskForm(this.form.taskId, this.form.values)
.subscribe(
() => {
this.formSaved.emit(this.form);
this.onTaskSaved(this.form);
this.storeFormAsMetadata();
},
(error) => {
this.handleError(error);
}
error => this.onTaskSavedError(this.form, error)
);
}
}
@@ -412,12 +402,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.completeTaskForm(this.form.taskId, this.form.values, outcome)
.subscribe(
() => {
this.formCompleted.emit(this.form);
this.onTaskCompleted(this.form);
this.storeFormAsMetadata();
},
(error) => {
this.handleError(error);
}
error => this.onTaskCompletedError(this.form, error)
);
}
}
@@ -429,7 +417,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
parseForm(json: any): FormModel {
if (json) {
let form = new FormModel(json, this.data, this.readOnly);
let form = new FormModel(json, this.data, this.readOnly, this.formService);
if (!json.fields) {
form.outcomes = this.getFormDefinitionOutcomes(form);
}
@@ -496,4 +484,29 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
);
}
}
protected onFormLoaded(form: FormModel) {
this.formLoaded.emit(form);
this.formService.formLoaded.next(new FormEvent(form));
}
protected onTaskSaved(form: FormModel) {
this.formSaved.emit(form);
this.formService.taskSaved.next(new FormEvent(form));
}
protected onTaskSavedError(form: FormModel, error: any) {
this.handleError(error);
this.formService.taskSavedError.next(new FormErrorEvent(form, error));
}
protected onTaskCompleted(form: FormModel) {
this.formCompleted.emit(form);
this.formService.taskCompleted.next(new FormEvent(form));
}
protected onTaskCompletedError(form: FormModel, error: any) {
this.handleError(error);
this.formService.taskCompletedError.next(new FormErrorEvent(form, error));
}
}

View File

@@ -23,6 +23,8 @@ import { FormOutcomeModel } from './form-outcome.model';
import { FormFieldModel } from './form-field.model';
import { FormFieldTypes } from './form-field-types';
import { FormFieldTemplates } from './form-field-templates';
import { FormService } from './../../../services/form.service';
import { FormFieldEvent } from './../../../events/index';
export class FormModel {
@@ -66,7 +68,7 @@ export class FormModel {
return this.outcomes && this.outcomes.length > 0;
}
constructor(json?: any, data?: FormValues, readOnly: boolean = false) {
constructor(json?: any, data?: FormValues, readOnly: boolean = false, protected formService?: FormService) {
this.readOnly = readOnly;
if (json) {
@@ -120,6 +122,9 @@ export class FormModel {
onFormFieldChanged(field: FormFieldModel) {
this.validateField(field);
if (this.formService) {
this.formService.formFieldValueChanged.next(new FormFieldEvent(this, field));
}
}
// TODO: consider evaluating and caching once the form is loaded

View File

@@ -0,0 +1,30 @@
/*!
* @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 { FormEvent } from './form.event';
import { FormModel } from './../components/widgets/core/index';
export class FormErrorEvent extends FormEvent {
readonly error: any;
constructor(form: FormModel, error: any) {
super(form);
this.error = error;
}
}

View File

@@ -0,0 +1,30 @@
/*!
* @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 { FormEvent } from './form.event';
import { FormModel, FormFieldModel } from './../components/widgets/core/index';
export class FormFieldEvent extends FormEvent {
readonly field: FormFieldModel;
constructor(form: FormModel, field: FormFieldModel) {
super(form);
this.field = field;
}
}

View File

@@ -0,0 +1,27 @@
/*!
* @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 './../components/widgets/core/index';
export class FormEvent {
readonly form: FormModel;
constructor(form: FormModel) {
this.form = form;
}
}

View File

@@ -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 * from './form.event';
export * from './form-error.event';
export * from './form-field.event';

View File

@@ -16,13 +16,14 @@
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Observable, Subject } from 'rxjs/Rx';
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
import { FormValues } from './../components/widgets/core/index';
import { FormDefinitionModel } from '../models/form-definition.model';
import { EcmModelService } from './ecm-model.service';
import { GroupModel } from './../components/widgets/core/group.model';
import { GroupUserModel } from './../components/widgets/core/group-user.model';
import { FormEvent, FormErrorEvent, FormFieldEvent } from './../events/index';
@Injectable()
export class FormService {
@@ -30,6 +31,13 @@ export class FormService {
static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error';
static GENERIC_ERROR_MESSAGE: string = 'Server error';
formLoaded: Subject<FormEvent> = new Subject<FormEvent>();
formFieldValueChanged: Subject<FormFieldEvent> = new Subject<FormFieldEvent>();
taskCompleted: Subject<FormEvent> = new Subject<FormEvent>();
taskCompletedError: Subject<FormErrorEvent> = new Subject<FormErrorEvent>();
taskSaved: Subject<FormEvent> = new Subject<FormEvent>();
taskSavedError: Subject<FormErrorEvent> = new Subject<FormErrorEvent>();
constructor(private ecmModelService: EcmModelService,
private apiService: AlfrescoApiService,
private logService: LogService) {
@@ -111,7 +119,7 @@ export class FormService {
}
/**
* Get Process Definition
* Get Process Definitions
* @returns {Observable<any>}
*/
getProcessDefinitions(): Observable<any> {
@@ -122,7 +130,6 @@ export class FormService {
/**
* Get All the Tasks
* @param taskId Task Id
* @returns {Observable<any>}
*/
getTasks(): Observable<any> {
@@ -196,7 +203,7 @@ export class FormService {
}
/**
* Returns form definition ID by a given name.
* Returns form definition by a given name.
* @param name
* @returns {Promise<T>|Promise<ErrorObservable>}
*/
@@ -328,7 +335,7 @@ export class FormService {
});
}
getFormId(res: any) {
getFormId(res: any): string {
let result = null;
if (res && res.data && res.data.length > 0) {