diff --git a/ng2-components/ng2-activiti-form/README.md b/ng2-components/ng2-activiti-form/README.md index 1538053c0b..bfbc45143d 100644 --- a/ng2-components/ng2-activiti-form/README.md +++ b/ng2-components/ng2-activiti-form/README.md @@ -66,7 +66,7 @@ necessary configuration, see this [page](https://github.com/Alfresco/alfresco-ng npm install ng2-activiti-form ``` -## ActivitiForm Component +## Form Component The component shows a Form from Activiti @@ -165,6 +165,26 @@ The recommended set of properties can be found in the following table: | saveMetadata | boolean | false | Store the value of the form as metadata. | | path | string | | Path of the folder where to store the metadata. | | nameNode | string | true | Name to assign to the new node where the metadata are stored. | +| fieldValidators | FormFieldValidator[] | *see below* | Contains a list of form field validator instances. | + +#### Form Field Validators + +The Form component provides you with access to all Form Field validators. By default the following instances are created automatically: + +- RequiredFieldValidator +- NumberFieldValidator +- MinLengthFieldValidator +- MaxLengthFieldValidator +- MinValueFieldValidator +- MaxValueFieldValidator +- RegExFieldValidator +- DateFieldValidator +- MinDateFieldValidator +- MaxDateFieldValidator + +If needed, you can completely redefine the set of validators used by the form. + +All changes to `fieldValidators` collection are automatically applied to all the further validation cycles. ### Advanced properties @@ -338,7 +358,9 @@ class MyComponent { | taskSaved | FormEvent | Raised when a task is saved successfully | | taskSavedError | FormErrorEvent | Raised when a task is saved unsuccessfully | | executeOutcome | FormOutcomeEvent | Raised when a form outcome is executed | -| formEvents | Event | You can subscribe to this event to listen : ( click, blur, change, focus, focusin, focusout, input, invalid, select) of any elements in the form , see doc below| +| formEvents | Event | You can subscribe to this event to listen : ( click, blur, change, focus, focusin, focusout, input, invalid, select) of any elements in the form , see doc below | +| validateForm | ValidateFormEvent | Raised each time a form is validated. You can use it to provide custom validation or prevent default behaviour. | +| validateFormField | ValidateFormFieldEvent | Raised each time a form field is validated. You can use it to provide custom validation or prevent default behaviour.| ### Methods diff --git a/ng2-components/ng2-activiti-form/src/components/start-form.component.spec.ts b/ng2-components/ng2-activiti-form/src/components/start-form.component.spec.ts index 0de3ca4ea3..738a575125 100644 --- a/ng2-components/ng2-activiti-form/src/components/start-form.component.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/start-form.component.spec.ts @@ -27,8 +27,8 @@ import { EcmModelService } from './../services/ecm-model.service'; import { FormService } from './../services/form.service'; import { WidgetVisibilityService } from './../services/widget-visibility.service'; import { ActivitiContentComponent } from './activiti-content.component'; -import { StartFormComponent } from './start-form.component'; import { FormFieldComponent } from './form-field/form-field.component'; +import { StartFormComponent } from './start-form.component'; import { MASK_DIRECTIVE } from './widgets/index'; import { WIDGET_DIRECTIVES } from './widgets/index'; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.spec.ts index e3a2738128..7a2fa2672a 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/attach/attach.widget.spec.ts @@ -25,6 +25,7 @@ import { FormFieldTypes } from '../core/form-field-types'; import { EcmModelService } from './../../../services/ecm-model.service'; import { FormService } from './../../../services/form.service'; import { FormFieldModel } from './../core/form-field.model'; +import { FormModel } from './../core/form.model'; import { AttachWidgetComponent } from './attach.widget'; describe('AttachWidgetComponent', () => { @@ -146,7 +147,7 @@ describe('AttachWidgetComponent', () => { }); it('should reset', () => { - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.UPLOAD, value: [{name: 'filename'}] }); diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts index 6dbd8f8037..2ec0d67743 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts @@ -23,19 +23,7 @@ import { ContainerColumnModel } from './container-column.model'; import { FormFieldMetadata } from './form-field-metadata'; import { FormFieldOption } from './form-field-option'; import { FormFieldTypes } from './form-field-types'; -import { - DateFieldValidator, - FormFieldValidator, - MaxDateFieldValidator, - MaxLengthFieldValidator, - MaxValueFieldValidator, - MinDateFieldValidator, - MinLengthFieldValidator, - MinValueFieldValidator, - NumberFieldValidator, - RegExFieldValidator, - RequiredFieldValidator -} from './form-field-validator'; +import { NumberFieldValidator } from './form-field-validator'; import { FormWidgetModel } from './form-widget.model'; import { FormModel } from './form.model'; @@ -88,7 +76,6 @@ export class FormFieldModel extends FormWidgetModel { // util members emptyOption: FormFieldOption; validationSummary: string; - validators: FormFieldValidator[] = []; get value(): any { return this._value; @@ -119,13 +106,11 @@ export class FormFieldModel extends FormWidgetModel { validate(): boolean { this.validationSummary = null; - // TODO: consider doing that on value setter and caching result - if (this.validators && this.validators.length > 0) { - for (let i = 0; i < this.validators.length; i++) { - if (!this.validators[i].validate(this)) { - this._isValid = false; - return this._isValid; - } + let validators = this.form.fieldValidators || []; + for (let validator of validators) { + if (!validator.validate(this)) { + this._isValid = false; + return this._isValid; } } @@ -201,19 +186,6 @@ export class FormFieldModel extends FormWidgetModel { this.emptyOption = this.options[0]; } - this.validators = [ - new RequiredFieldValidator(), - new NumberFieldValidator(), - new MinLengthFieldValidator(), - new MaxLengthFieldValidator(), - new MinValueFieldValidator(), - new MaxValueFieldValidator(), - new RegExFieldValidator(), - new DateFieldValidator(), - new MinDateFieldValidator(), - new MaxDateFieldValidator() - ]; - this.updateForm(); } diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.spec.ts index 47b8124ff3..68b7e10f0f 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.spec.ts @@ -15,8 +15,10 @@ * limitations under the License. */ +import { ValidateFormFieldEvent } from './../../../events/validate-form-field.event'; +import { ValidateFormEvent } from './../../../events/validate-form.event'; +import { FormService } from './../../../services/form.service'; import { ContainerModel } from './container.model'; -// import { FormValues } from './form-values'; import { FormFieldTypes } from './form-field-types'; import { FormOutcomeModel } from './form-outcome.model'; import { FormModel } from './form.model'; @@ -24,6 +26,12 @@ import { TabModel } from './tab.model'; describe('FormModel', () => { + let formService: FormService; + + beforeEach(() => { + formService = new FormService(null, null, null); + }); + it('should store original json', () => { let json = {}; let form = new FormModel(json); @@ -197,71 +205,6 @@ describe('FormModel', () => { 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: [ @@ -309,4 +252,133 @@ describe('FormModel', () => { expect(form.outcomes[1].id).toBe('custom-1'); expect(form.outcomes[1].isSystem).toBeFalsy(); }); + + it('should raise validation event when validating form', (done) => { + const form = new FormModel({}, null, false, formService); + + formService.validateForm.subscribe(() => done()); + form.validateForm(); + }); + + it('should raise validation event when validating field', (done) => { + const form = new FormModel({}, null, false, formService); + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + + formService.validateFormField.subscribe(() => done()); + form.validateField(field); + }); + + it('should skip form validation when default behaviour prevented', () => { + const form = new FormModel({}, null, false, formService); + + let prevented = false; + + formService.validateForm.subscribe((event: ValidateFormEvent) => { + event.isValid = false; + event.preventDefault(); + prevented = true; + }); + + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + spyOn(form, 'getFormFields').and.returnValue([field]); + + form.validateForm(); + + expect(prevented).toBeTruthy(); + expect(form.isValid).toBeFalsy(); + expect(field.validate).not.toHaveBeenCalled(); + }); + + it('should skip field validation when default behaviour prevented', () => { + const form = new FormModel({}, null, false, formService); + + let prevented = false; + + formService.validateFormField.subscribe((event: ValidateFormFieldEvent) => { + event.isValid = false; + event.preventDefault(); + prevented = true; + }); + + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + form.validateField(field); + + expect(prevented).toBeTruthy(); + expect(form.isValid).toBeFalsy(); + expect(field.validate).not.toHaveBeenCalled(); + }); + + it('should validate fields when form validation not prevented', () => { + const form = new FormModel({}, null, false, formService); + + let validated = false; + + formService.validateForm.subscribe((event: ValidateFormEvent) => { + validated = true; + }); + + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + spyOn(form, 'getFormFields').and.returnValue([field]); + + form.validateForm(); + + expect(validated).toBeTruthy(); + expect(field.validate).toHaveBeenCalled(); + }); + + it('should validate field when field validation not prevented', () => { + const form = new FormModel({}, null, false, formService); + + let validated = false; + + formService.validateFormField.subscribe((event: ValidateFormFieldEvent) => { + validated = true; + }); + + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + form.validateField(field); + + expect(validated).toBeTruthy(); + expect(field.validate).toHaveBeenCalled(); + }); + + it('should validate form when field validation not prevented', () => { + const form = new FormModel({}, null, false, formService); + spyOn(form, 'validateForm').and.stub(); + + let validated = false; + + formService.validateFormField.subscribe((event: ValidateFormFieldEvent) => { + validated = true; + }); + + const field: any = { + validate() { + return true; + } + }; + form.validateField(field); + + expect(validated).toBeTruthy(); + expect(form.validateForm).toHaveBeenCalled(); + }); + + it('should not validate form when field validation prevented', () => { + const form = new FormModel({}, null, false, formService); + spyOn(form, 'validateForm').and.stub(); + + let prevented = false; + + formService.validateFormField.subscribe((event: ValidateFormFieldEvent) => { + event.preventDefault(); + prevented = true; + }); + + const field = jasmine.createSpyObj('FormFieldModel', ['validate']); + form.validateField(field); + + expect(prevented).toBeTruthy(); + expect(field.validate).not.toHaveBeenCalled(); + expect(form.validateForm).not.toHaveBeenCalled(); + }); }); 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 996d8063df..344f3e7ee2 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 @@ -17,7 +17,7 @@ /* tslint:disable:component-selector */ -import { FormFieldEvent } from './../../../events/index'; +import { FormFieldEvent, ValidateFormEvent, ValidateFormFieldEvent } from './../../../events/index'; import { FormService } from './../../../services/form.service'; import { ContainerModel } from './container.model'; import { FormFieldTemplates } from './form-field-templates'; @@ -28,6 +28,20 @@ import { FormValues } from './form-values'; import { FormWidgetModel, FormWidgetModelCache } from './form-widget.model'; import { TabModel } from './tab.model'; +import { + DateFieldValidator, + FormFieldValidator, + MaxDateFieldValidator, + MaxLengthFieldValidator, + MaxValueFieldValidator, + MinDateFieldValidator, + MinLengthFieldValidator, + MinValueFieldValidator, + NumberFieldValidator, + RegExFieldValidator, + RequiredFieldValidator +} from './form-field-validator'; + export class FormModel { static UNSET_TASK_NAME: string = 'Nameless task'; @@ -53,6 +67,7 @@ export class FormModel { fields: FormWidgetModel[] = []; outcomes: FormOutcomeModel[] = []; customFieldTemplates: FormFieldTemplates = {}; + fieldValidators: FormFieldValidator[] = []; readonly selectedOutcome: string; values: FormValues = {}; @@ -121,6 +136,20 @@ export class FormModel { ); } } + + this.fieldValidators = [ + new RequiredFieldValidator(), + new NumberFieldValidator(), + new MinLengthFieldValidator(), + new MaxLengthFieldValidator(), + new MinValueFieldValidator(), + new MaxValueFieldValidator(), + new RegExFieldValidator(), + new DateFieldValidator(), + new MinDateFieldValidator(), + new MaxDateFieldValidator() + ]; + this.validateForm(); } @@ -148,21 +177,63 @@ export class FormModel { return result; } - private validateForm() { - this._isValid = true; - let fields = this.getFormFields(); - for (let i = 0; i < fields.length; i++) { - if (!fields[i].validate()) { - this._isValid = false; - return; + /** + * Validates entire form and all form fields. + * + * @returns {void} + * @memberof FormModel + */ + validateForm(): void { + const validateFormEvent = new ValidateFormEvent(this); + + if (this.formService) { + this.formService.validateForm.next(validateFormEvent); + } + + this._isValid = validateFormEvent.isValid; + + if (validateFormEvent.defaultPrevented) { + return; + } + + if (validateFormEvent.isValid) { + let fields = this.getFormFields(); + for (let i = 0; i < fields.length; i++) { + if (!fields[i].validate()) { + this._isValid = false; + return; + } } } } - private validateField(field: FormFieldModel) { + /** + * Validates a specific form field, triggers form validation. + * + * @param {FormFieldModel} field Form field to validate. + * @returns {void} + * @memberof FormModel + */ + validateField(field: FormFieldModel): void { if (!field) { return; } + + const validateFieldEvent = new ValidateFormFieldEvent(this, field); + + if (this.formService) { + this.formService.validateFormField.next(validateFieldEvent); + } + + if (!validateFieldEvent.isValid) { + this._isValid = false; + return; + } + + if (validateFieldEvent.defaultPrevented) { + return; + } + if (!field.validate()) { this._isValid = false; return; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.spec.ts index 90c373080a..34e22d391a 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.spec.ts @@ -131,7 +131,7 @@ describe('DateWidgetComponent', () => { }); it('should update picker value on input date changed', () => { - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(new FormModel(), { type: 'date', value: '13-03-1982' }); @@ -145,7 +145,7 @@ describe('DateWidgetComponent', () => { it('should update field value on date selected', () => { widget.elementRef = new ElementRef(nativeElement); - widget.field = new FormFieldModel(null, {type: 'date'}); + widget.field = new FormFieldModel(new FormModel(), {type: 'date'}); widget.ngOnInit(); let date = '13-3-1982'; @@ -157,7 +157,7 @@ describe('DateWidgetComponent', () => { it('should update material textfield on date selected', () => { spyOn(widget, 'setupMaterialTextField').and.callThrough(); - widget.field = new FormFieldModel(null, {type: 'date'}); + widget.field = new FormFieldModel(new FormModel(), {type: 'date'}); widget.ngOnInit(); widget.datePicker.time = moment(); @@ -169,7 +169,7 @@ describe('DateWidgetComponent', () => { widget.elementRef = undefined; spyOn(widget, 'setupMaterialTextField').and.callThrough(); - widget.field = new FormFieldModel(null, {type: 'date'}); + widget.field = new FormFieldModel(new FormModel(), {type: 'date'}); widget.ngOnInit(); widget.datePicker.time = moment(); @@ -179,7 +179,7 @@ describe('DateWidgetComponent', () => { it('should send field change event when a new date is picked from data picker', (done) => { spyOn(widget, 'setupMaterialTextField').and.callThrough(); - widget.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'}); + widget.field = new FormFieldModel(new FormModel(), {value: '9-9-9999', type: 'date'}); widget.ngOnInit(); widget.datePicker.time = moment('9-9-9999', widget.field.dateDisplayFormat); widget.fieldChanged.subscribe((field) => { diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/radio-buttons/radio-buttons.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/radio-buttons/radio-buttons.widget.spec.ts index 8b76a2cb6d..34403bb418 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/radio-buttons/radio-buttons.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/radio-buttons/radio-buttons.widget.spec.ts @@ -203,7 +203,7 @@ describe('RadioButtonsWidgetComponent', () => { it('should evaluate visibility on option click', async(() => { spyOn(stubVisibilityService, 'evaluateVisibility').and.returnValue(false); - let option: HTMLElement = element.querySelector('#opt-1'); + let option: HTMLElement = element.querySelector('#opt-1'); expect(element.querySelector('#radio-id')).not.toBeNull(); expect(option).not.toBeNull(); option.click(); diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/text/text.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/text/text.widget.spec.ts index 12b5441ebf..b72609e815 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/text/text.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/text/text.widget.spec.ts @@ -76,7 +76,7 @@ describe('TextWidgetComponent', () => { }); fixture.detectChanges(); - inputElement = element.querySelector('#text-id'); + inputElement = element.querySelector('#text-id'); }); it('should raise ngModelChange event', async(() => { @@ -118,7 +118,7 @@ describe('TextWidgetComponent', () => { }); fixture.detectChanges(); - inputElement = element.querySelector('#text-id'); + inputElement = element.querySelector('#text-id'); }); it('should show text widget', () => { @@ -138,7 +138,7 @@ describe('TextWidgetComponent', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - inputElement = element.querySelector('#text-id'); + inputElement = element.querySelector('#text-id'); expect(inputElement.value).toBe(''); }); })); @@ -153,7 +153,7 @@ describe('TextWidgetComponent', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - inputElement = element.querySelector('#text-id'); + inputElement = element.querySelector('#text-id'); expect(inputElement.value).toBe(''); }); })); @@ -169,7 +169,7 @@ describe('TextWidgetComponent', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - let textEle: HTMLInputElement = element.querySelector('#text-id'); + let textEle: HTMLInputElement = element.querySelector('#text-id'); expect(textEle.value).toBe('1'); }); })); @@ -185,7 +185,7 @@ describe('TextWidgetComponent', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - let textEle: HTMLInputElement = element.querySelector('#text-id'); + let textEle: HTMLInputElement = element.querySelector('#text-id'); expect(textEle.value).toBe('12-345,67%'); }); })); @@ -206,7 +206,7 @@ describe('TextWidgetComponent', () => { }); fixture.detectChanges(); - inputElement = element.querySelector('#text-id'); + inputElement = element.querySelector('#text-id'); }); afterEach(() => { @@ -225,7 +225,7 @@ describe('TextWidgetComponent', () => { fixture.whenStable().then(() => { fixture.detectChanges(); - let textEle: HTMLInputElement = element.querySelector('#text-id'); + let textEle: HTMLInputElement = element.querySelector('#text-id'); expect(textEle.value).toBe('12,34%'); }); })); diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/upload/upload.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/upload/upload.widget.spec.ts index 39f5f7b8f6..159fe46b92 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/upload/upload.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/upload/upload.widget.spec.ts @@ -73,7 +73,7 @@ describe('UploadWidgetComponent', () => { }); it('should reset field value', () => { - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.UPLOAD, value: [ { name: 'filename' } @@ -125,7 +125,7 @@ describe('UploadWidgetComponent', () => { it('should be disabled on readonly forms', async(() => { uploadWidgetComponent.field.form.readOnly = true; fixture.detectChanges(); - inputElement = element.querySelector('#upload-id'); + inputElement = element.querySelector('#upload-id'); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -138,7 +138,7 @@ describe('UploadWidgetComponent', () => { it('should have the multiple attribute when is selected in parameters', async(() => { uploadWidgetComponent.field.params.multiple = true; fixture.detectChanges(); - inputElement = element.querySelector('#upload-id'); + inputElement = element.querySelector('#upload-id'); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -151,7 +151,7 @@ describe('UploadWidgetComponent', () => { it('should not have the multiple attribute if multiple is false', async(() => { uploadWidgetComponent.field.params.multiple = false; fixture.detectChanges(); - inputElement = element.querySelector('#upload-id'); + inputElement = element.querySelector('#upload-id'); fixture.whenStable().then(() => { fixture.detectChanges(); diff --git a/ng2-components/ng2-activiti-form/src/events/form.event.ts b/ng2-components/ng2-activiti-form/src/events/form.event.ts index 841a12dd66..2fec253b78 100644 --- a/ng2-components/ng2-activiti-form/src/events/form.event.ts +++ b/ng2-components/ng2-activiti-form/src/events/form.event.ts @@ -19,9 +19,19 @@ import { FormModel } from './../components/widgets/core/index'; export class FormEvent { + private isDefaultPrevented: boolean = false; + readonly form: FormModel; constructor(form: FormModel) { this.form = form; } + + get defaultPrevented() { + return this.isDefaultPrevented; + } + + preventDefault() { + this.isDefaultPrevented = true; + } } diff --git a/ng2-components/ng2-activiti-form/src/events/index.ts b/ng2-components/ng2-activiti-form/src/events/index.ts index 39a1671687..d36fa51615 100644 --- a/ng2-components/ng2-activiti-form/src/events/index.ts +++ b/ng2-components/ng2-activiti-form/src/events/index.ts @@ -15,6 +15,8 @@ * limitations under the License. */ -export * from './form.event'; -export * from './form-error.event'; -export * from './form-field.event'; +export { FormEvent } from './form.event'; +export { FormErrorEvent } from './form-error.event'; +export { FormFieldEvent } from './form-field.event'; +export { ValidateFormFieldEvent } from './validate-form-field.event'; +export { ValidateFormEvent } from './validate-form.event'; diff --git a/ng2-components/ng2-activiti-form/src/events/validate-form-field.event.ts b/ng2-components/ng2-activiti-form/src/events/validate-form-field.event.ts new file mode 100644 index 0000000000..c7562eafe2 --- /dev/null +++ b/ng2-components/ng2-activiti-form/src/events/validate-form-field.event.ts @@ -0,0 +1,29 @@ +/*! + * @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, FormModel } from './../components/widgets/core/index'; +import { FormFieldEvent } from './form-field.event'; + +export class ValidateFormFieldEvent extends FormFieldEvent { + + isValid = true; + + constructor(form: FormModel, field: FormFieldModel) { + super(form, field); + } + +} diff --git a/ng2-components/ng2-activiti-form/src/events/validate-form.event.ts b/ng2-components/ng2-activiti-form/src/events/validate-form.event.ts new file mode 100644 index 0000000000..8de2f5109f --- /dev/null +++ b/ng2-components/ng2-activiti-form/src/events/validate-form.event.ts @@ -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, FormModel } from './../components/widgets/core/index'; +import { FormEvent } from './form.event'; + +export class ValidateFormEvent extends FormEvent { + + isValid = true; + + constructor(form: FormModel) { + super(form); + } +} 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 e850e77c85..06b61297c2 100644 --- a/ng2-components/ng2-activiti-form/src/services/form.service.ts +++ b/ng2-components/ng2-activiti-form/src/services/form.service.ts @@ -23,7 +23,7 @@ import { ContentLinkModel } from './../components/widgets/core/content-link.mode import { GroupUserModel } from './../components/widgets/core/group-user.model'; import { GroupModel } from './../components/widgets/core/group.model'; import { FormModel, FormOutcomeEvent, FormOutcomeModel, FormValues } from './../components/widgets/core/index'; -import { FormErrorEvent, FormEvent, FormFieldEvent } from './../events/index'; +import { FormErrorEvent, FormEvent, FormFieldEvent, ValidateFormEvent, ValidateFormFieldEvent } from './../events/index'; import { EcmModelService } from './ecm-model.service'; @Injectable() @@ -32,23 +32,54 @@ export class FormService { static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; - formLoaded: Subject = new Subject(); - formDataRefreshed: Subject = new Subject(); - formFieldValueChanged: Subject = new Subject(); - formEvents: Subject = new Subject(); - taskCompleted: Subject = new Subject(); - taskCompletedError: Subject = new Subject(); - taskSaved: Subject = new Subject(); - taskSavedError: Subject = new Subject(); - formContentClicked: Subject = new Subject(); + formLoaded = new Subject(); + formDataRefreshed = new Subject(); + formFieldValueChanged = new Subject(); + formEvents = new Subject(); + taskCompleted = new Subject(); + taskCompletedError = new Subject(); + taskSaved = new Subject(); + taskSavedError = new Subject(); + formContentClicked = new Subject(); - executeOutcome: Subject = new Subject(); + validateForm = new Subject(); + validateFormField = new Subject(); + + executeOutcome = new Subject(); constructor(private ecmModelService: EcmModelService, private apiService: AlfrescoApiService, private logService: LogService) { } + private get contentApi(): any { + return this.apiService.getInstance().activiti.contentApi; + } + + private get taskApi(): any { + return this.apiService.getInstance().activiti.taskApi; + } + + private get modelsApi(): any { + return this.apiService.getInstance().activiti.modelsApi; + } + + private get editorApi(): any { + return this.apiService.getInstance().activiti.editorApi; + } + + private get processApi(): any { + return this.apiService.getInstance().activiti.processApi; + } + + private get usersWorkflowApi(): any { + return this.apiService.getInstance().activiti.usersWorkflowApi; + } + + private get groupsApi(): any { + return this.apiService.getInstance().activiti.groupsApi; + } + parseForm(json: any, data?: FormValues, readOnly: boolean = false): FormModel { if (json) { let form = new FormModel(json, data, readOnly, this); @@ -101,13 +132,13 @@ export class FormService { }; return Observable.fromPromise( - this.apiService.getInstance().activiti.modelsApi.createModel(dataModel) + this.modelsApi.createModel(dataModel) ); } saveForm(formId: string, formModel: FormDefinitionModel): Observable { return Observable.fromPromise( - this.apiService.getInstance().activiti.editorApi.saveForm(formId, formModel) + this.editorApi.saveForm(formId, formModel) ); } @@ -119,7 +150,7 @@ export class FormService { addFieldsToAForm(formId: string, formModel: FormDefinitionModel): Observable { console.log('addFieldsToAForm is deprecated in 1.7.0, use saveForm API instead'); return Observable.fromPromise( - this.apiService.getInstance().activiti.editorApi.saveForm(formId, formModel) + this.editorApi.saveForm(formId, formModel) ); } @@ -133,7 +164,7 @@ export class FormService { }; return Observable.fromPromise( - this.apiService.getInstance().activiti.modelsApi.getModels(opts) + this.modelsApi.getModels(opts) ) .map(function (forms: any) { return forms.data.find(formdata => formdata.name === name); @@ -150,7 +181,7 @@ export class FormService { 'modelType': 2 }; - return Observable.fromPromise(this.apiService.getInstance().activiti.modelsApi.getModels(opts)) + return Observable.fromPromise(this.modelsApi.getModels(opts)) .map(this.toJsonArray) .catch(err => this.handleError(err)); } @@ -160,7 +191,7 @@ export class FormService { * @returns {Observable} */ getProcessDefinitions(): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.processApi.getProcessDefinitions({})) + return Observable.fromPromise(this.processApi.getProcessDefinitions({})) .map(this.toJsonArray) .catch(err => this.handleError(err)); } @@ -170,7 +201,7 @@ export class FormService { * @returns {Observable} */ getTasks(): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.listTasks({})) + return Observable.fromPromise(this.taskApi.listTasks({})) .map(this.toJsonArray) .catch(err => this.handleError(err)); } @@ -181,7 +212,7 @@ export class FormService { * @returns {Observable} */ getTask(taskId: string): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.getTask(taskId)) + return Observable.fromPromise(this.taskApi.getTask(taskId)) .map(this.toJson) .catch(err => this.handleError(err)); } @@ -195,7 +226,7 @@ export class FormService { saveTaskForm(taskId: string, formValues: FormValues): Observable { let body = JSON.stringify({values: formValues}); - return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.saveTaskForm(taskId, body)) + return Observable.fromPromise(this.taskApi.saveTaskForm(taskId, body)) .catch(err => this.handleError(err)); } @@ -213,7 +244,7 @@ export class FormService { } let body = JSON.stringify(data); - return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.completeTaskForm(taskId, body)) + return Observable.fromPromise(this.taskApi.completeTaskForm(taskId, body)) .catch(err => this.handleError(err)); } @@ -223,7 +254,7 @@ export class FormService { * @returns {Observable} */ getTaskForm(taskId: string): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.getTaskForm(taskId)) + return Observable.fromPromise(this.taskApi.getTaskForm(taskId)) .map(this.toJson) .catch(err => this.handleError(err)); } @@ -234,7 +265,7 @@ export class FormService { * @returns {Observable} */ getFormDefinitionById(formId: string): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.editorApi.getForm(formId)) + return Observable.fromPromise(this.editorApi.getForm(formId)) .map(this.toJson) .catch(err => this.handleError(err)); } @@ -251,7 +282,7 @@ export class FormService { 'modelType': 2 }; - return Observable.fromPromise(this.apiService.getInstance().activiti.modelsApi.getModels(opts)) + return Observable.fromPromise(this.modelsApi.getModels(opts)) .map(this.getFormId) .catch(err => this.handleError(err)); } @@ -263,7 +294,7 @@ export class FormService { */ getStartFormInstance(processId: string): Observable { return Observable.fromPromise( - this.apiService.getInstance().activiti.processApi.getProcessInstanceStartForm(processId)) + this.processApi.getProcessInstanceStartForm(processId)) .map(this.toJson) .catch(err => this.handleError(err)); } @@ -275,7 +306,7 @@ export class FormService { */ getStartFormDefinition(processId: string): Observable { return Observable.fromPromise( - this.apiService.getInstance().activiti.processApi.getProcessDefinitionStartForm(processId)) + this.processApi.getProcessDefinitionStartForm(processId)) .map(this.toJson) .catch(err => this.handleError(err)); } @@ -286,47 +317,39 @@ export class FormService { * @returns {Observable} */ createTemporaryRawRelatedContent(file: any): Observable { - return Observable.fromPromise(this.apiService.getInstance().activiti.contentApi.createTemporaryRawRelatedContent(file)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.contentApi.createTemporaryRawRelatedContent(file)).catch(err => this.handleError(err)); } getFileContent(contentId: number): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.contentApi.getContent(contentId)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.contentApi.getContent(contentId)).catch(err => this.handleError(err)); } getFileRawContent(contentId: number): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.contentApi.getRawContent(contentId)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.contentApi.getRawContent(contentId)).catch(err => this.handleError(err)); } getFileRawContentUrl(contentId: number): string { - let alfrescoApi = this.apiService.getInstance(); - return alfrescoApi.activiti.contentApi.getRawContentUrl(contentId); + return this.contentApi.getRawContentUrl(contentId); } getContentThumbnailUrl(contentId: number): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.contentApi.getContentThumbnailUrl(contentId)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.contentApi.getContentThumbnailUrl(contentId)).catch(err => this.handleError(err)); } getRestFieldValues(taskId: string, field: string): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.taskApi.getRestFieldValues(taskId, field)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.taskApi.getRestFieldValues(taskId, field)).catch(err => this.handleError(err)); } getRestFieldValuesByProcessId(processDefinitionId: string, field: string): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.processApi.getRestFieldValues(processDefinitionId, field)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.processApi.getRestFieldValues(processDefinitionId, field)).catch(err => this.handleError(err)); } getRestFieldValuesColumnByProcessId(processDefinitionId: string, field: string, column?: string): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.processApi.getRestTableFieldValues(processDefinitionId, field, column)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.processApi.getRestTableFieldValues(processDefinitionId, field, column)).catch(err => this.handleError(err)); } getRestFieldValuesColumn(taskId: string, field: string, column?: string): Observable { - let alfrescoApi = this.apiService.getInstance(); - return Observable.fromPromise(alfrescoApi.activiti.taskApi.getRestFieldValuesColumn(taskId, field, column)).catch(err => this.handleError(err)); + return Observable.fromPromise(this.taskApi.getRestFieldValuesColumn(taskId, field, column)).catch(err => this.handleError(err)); } getWorkflowUsers(filter: string, groupId?: string): Observable { @@ -334,31 +357,21 @@ export class FormService { if (groupId) { option.groupId = groupId; } - return Observable.fromPromise(this.getWorkflowUserApi(option)) + return Observable.fromPromise(this.usersWorkflowApi.getUsers(option)) .map((response: any) => response.data || []) .catch(err => this.handleError(err)); } - private getWorkflowUserApi(options: any) { - let alfrescoApi = this.apiService.getInstance(); - return alfrescoApi.activiti.usersWorkflowApi.getUsers(options); - } - getWorkflowGroups(filter: string, groupId?: string): Observable { let option: any = {filter: filter}; if (groupId) { option.groupId = groupId; } - return Observable.fromPromise(this.getWorkflowGroupsApi(option)) + return Observable.fromPromise(this.groupsApi.getGroups(option)) .map((response: any) => response.data || []) .catch(err => this.handleError(err)); } - private getWorkflowGroupsApi(options: any) { - let alfrescoApi = this.apiService.getInstance(); - return alfrescoApi.activiti.groupsApi.getGroups(options); - } - getFormId(res: any): string { let result = null;