diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts
index 8b05745aca..0fee1a1127 100644
--- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts
+++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts
@@ -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() {
diff --git a/ng2-components/ng2-activiti-form/README.md b/ng2-components/ng2-activiti-form/README.md
index 894a5d102d..4e246cde16 100644
--- a/ng2-components/ng2-activiti-form/README.md
+++ b/ng2-components/ng2-activiti-form/README.md
@@ -31,6 +31,22 @@
+## 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.
```
-## 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 | Create a Form with a fields for each metadata properties |
+| createForm | (formName: string) | Observable | Create a Form |
+| addFieldsToAForm | (formId: string, formModel: FormDefinitionModel) | Observable | Add Fileds to A form |
+| searchFrom | (name: string) | Observable | Search For A Form by name |
+| getForms | n/a | Observable | Get All the forms |
+| getProcessDefinitions | n/a | Observable | Get Process Definitions |
+| getTasks | n/a | Observable | Get All the Tasks |
+| getTask | (taskId: string) | Observable | Get Task |
+| saveTaskForm | (taskId: string, formValues: FormValues) | Observable | Save Task Form |
+| completeTaskForm | (taskId: string, formValues: FormValues, outcome?: string) | Observable | Complete Task Form |
+| getTaskForm | (taskId: string) | Observable | Get Form related to a taskId |
+| getFormDefinitionById | (formId: string) | Observable | Get Form Definition |
+| getFormDefinitionByName | (name: string) | Observable | Returns form definition by a given name. |
+| getStartFormInstance | (processId: string) | Observable | Get start form instance for a given processId |
+| getStartFormDefinition | (processId: string) | Observable | Get start form definition for a given process |
+| createTemporaryRawRelatedContent | (file: any) | Observable | Save File |
+| getRestFieldValues | (taskId: string, field: string) | Observable | |
+| getRestFieldValuesByProcessId | (processDefinitionId: string, field: string) | Observable | |
+| getRestFieldValuesColumnByProcessId | (processDefinitionId: string, field: string, column?: string) | Observable | |
+| getRestFieldValuesColumn | (taskId: string, field: string, column?: string) | Observable | |
+| getWorkflowGroups\* | (filter: string, groupId?: string) | Observable | |
+| getWorkflowUsers\* | (filter: string, groupId?: string) | Observable | |
+
+\* _Uses private Activiti WebApp api_
+
## See also
- [Form Stencils with Angular 2](docs/stencils.md)
diff --git a/ng2-components/ng2-activiti-form/index.ts b/ng2-components/ng2-activiti-form/index.ts
index 98e9d2970c..110d843dfc 100644
--- a/ng2-components/ng2-activiti-form/index.ts
+++ b/ng2-components/ng2-activiti-form/index.ts
@@ -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,
diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts
index 396f30b5d0..45a3837244 100644
--- a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts
+++ b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts
@@ -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));
+ }
}
diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts
index cc31cf1101..8741224b54 100644
--- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts
+++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts
@@ -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
diff --git a/ng2-components/ng2-activiti-form/src/events/form-error.event.ts b/ng2-components/ng2-activiti-form/src/events/form-error.event.ts
new file mode 100644
index 0000000000..1e4ad50c31
--- /dev/null
+++ b/ng2-components/ng2-activiti-form/src/events/form-error.event.ts
@@ -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;
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-form/src/events/form-field.event.ts b/ng2-components/ng2-activiti-form/src/events/form-field.event.ts
new file mode 100644
index 0000000000..1189e33a64
--- /dev/null
+++ b/ng2-components/ng2-activiti-form/src/events/form-field.event.ts
@@ -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;
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-form/src/events/form.event.ts b/ng2-components/ng2-activiti-form/src/events/form.event.ts
new file mode 100644
index 0000000000..841a12dd66
--- /dev/null
+++ b/ng2-components/ng2-activiti-form/src/events/form.event.ts
@@ -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;
+ }
+}
diff --git a/ng2-components/ng2-activiti-form/src/events/index.ts b/ng2-components/ng2-activiti-form/src/events/index.ts
new file mode 100644
index 0000000000..39a1671687
--- /dev/null
+++ b/ng2-components/ng2-activiti-form/src/events/index.ts
@@ -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';
diff --git a/ng2-components/ng2-activiti-form/src/services/form.service.ts b/ng2-components/ng2-activiti-form/src/services/form.service.ts
index 85f77dc42a..0ffc0156d3 100644
--- a/ng2-components/ng2-activiti-form/src/services/form.service.ts
+++ b/ng2-components/ng2-activiti-form/src/services/form.service.ts
@@ -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 = new Subject();
+ formFieldValueChanged: Subject = new Subject();
+ taskCompleted: Subject = new Subject();
+ taskCompletedError: Subject = new Subject();
+ taskSaved: Subject = new Subject();
+ taskSavedError: Subject = new Subject();
+
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}
*/
getProcessDefinitions(): Observable {
@@ -122,7 +130,6 @@ export class FormService {
/**
* Get All the Tasks
- * @param taskId Task Id
* @returns {Observable}
*/
getTasks(): Observable {
@@ -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|Promise}
*/
@@ -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) {