mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
AAE-30864 Refactored services to accept injected validators (#10660)
* [AAE-30864] refactored services to accept injected validators * [AAE-30864] updated documentation, applied pr comments
This commit is contained in:
parent
70d899f5ba
commit
f39f104d45
114
docs/core/models/form-model.md
Normal file
114
docs/core/models/form-model.md
Normal file
@ -0,0 +1,114 @@
|
||||
---
|
||||
Title: Form model
|
||||
Added: 2025-02-19
|
||||
Status: Active
|
||||
Last reviewed: 2025-02-19
|
||||
---
|
||||
|
||||
# [Form model](../../../lib/core/src/lib/form/components/widgets/core/form.model.ts "Defined in form.model.ts")
|
||||
|
||||
Contains the value and metadata for a form.
|
||||
|
||||
## Properties
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ---- | ---- | ------- | ----------- |
|
||||
|UNSET_TASK_NAME| string | 'Nameless task'|static property|
|
||||
|SAVE_OUTCOME| string | '$save'|static property|
|
||||
|COMPLETE_OUTCOME| string | '$complete'|static property|
|
||||
|START_PROCESS_OUTCOME| string | '$startProcess'|static property|
|
||||
|id| string | number||id of form|
|
||||
|name| string||form name|
|
||||
|taskId| string||task id|
|
||||
|confirmMessage| ConfirmMessage||confirmation message|
|
||||
|taskName |string| FormModel.UNSET_TASK_NAME|task name|
|
||||
|processDefinitionId| string||Process definition id |
|
||||
|selectedOutcome| string||selected outcome|
|
||||
|enableFixedSpace| boolean||should fixed space be enabled|
|
||||
|displayMode| any||which mode should be displayed|
|
||||
|fieldsCache| FormFieldModel[] | []|cache for fields|
|
||||
|json| any||json with form configuration|
|
||||
|nodeId| string||id of node|
|
||||
|values| FormValues | {}|form values|
|
||||
|tabs| TabModel[] | []|tabs|
|
||||
|fields| (ContainerModel | FormFieldModel)[] | []|form fields|
|
||||
|outcomes| FormOutcomeModel[] | []|set of outcomes|
|
||||
|fieldValidators| FormFieldValidator[] | []|validators for fields|
|
||||
|customFieldTemplates| FormFieldTemplates | {}|custom templates|
|
||||
|theme?| ThemeModel||theme|
|
||||
|className| string||class name|
|
||||
|readOnly | false||is form read only|
|
||||
|isValid | true||is form valid|
|
||||
|processVariables| ProcessVariableModel[] | []|process variables|
|
||||
|variables| FormVariableModel[] | []|variables|
|
||||
|
||||
## Methods
|
||||
|
||||
- `onFormFieldChanged(field: FormFieldModel)`
|
||||
Triggered when field is changed. Validates field and calls FormService
|
||||
- `validateForm(): void`
|
||||
Validates entire form and all form fields.
|
||||
- `validateField(field: FormFieldModel): void`
|
||||
Validates a specific form field, triggers form validation.
|
||||
- `parseRootFields(json: any): (ContainerModel | FormFieldModel)[]`
|
||||
Activiti supports 3 types of root fields: container|group|dynamic-table
|
||||
- `loadData(formValues: FormValues)`
|
||||
Loads external data and overrides field values. Typically used when form definition and form data coming from different sources
|
||||
- `canOverrideFieldValueWithProcessValue(field: FormFieldModel, variableId: string, formValues: FormValues): boolean`
|
||||
Checks if field value can be overriden with process value
|
||||
- `isDefined(value: string): boolean`
|
||||
Check if variable is defined
|
||||
- `getFormVariable(identifier: string): FormVariableModel`
|
||||
Returns a form variable that matches the identifier.
|
||||
- `getDefaultFormVariableValue(identifier: string): any`
|
||||
Returns a value of the form variable that matches the identifier. Provides additional conversion of types (date, boolean).
|
||||
- `getProcessVariableValue(name: string): any`
|
||||
Returns a process variable value. When mapping a process variable with a form variable the mapping is already resolved by the rest API with the name of variables.formVariableName.
|
||||
- `parseValue(type: string, value: any): any`
|
||||
Parse value data and boolean
|
||||
- `hasTabs(): boolean`
|
||||
Check if form has tabs
|
||||
- `hasFields(): boolean`
|
||||
Check if there are any fields
|
||||
- `hasOutcomes(): boolean`
|
||||
Check if form has outcomes
|
||||
- `getFieldById(fieldId: string): FormFieldModel`
|
||||
Find field by id
|
||||
- `getFormFields(filterTypes?: string[]): FormFieldModel[]`
|
||||
Get form fields
|
||||
- `processFields(fields: (ContainerModel | FormFieldModel)[], formFieldModel: FormFieldModel[]): void`
|
||||
Process fields
|
||||
- `isContainerField(field: ContainerModel | FormFieldModel): field is ContainerModel`
|
||||
Check if it is container
|
||||
- `isSectionField(field: ContainerModel | FormFieldModel): field is FormFieldModel`
|
||||
Check if it is section
|
||||
- `handleSectionField(section: FormFieldModel, formFieldModel: FormFieldModel[]): void`
|
||||
Handle section
|
||||
- `handleContainerField(container: ContainerModel, formFieldModel: FormFieldModel[]): void`
|
||||
Handle container
|
||||
- `handleSingleField(field: FormFieldModel, formFieldModel: FormFieldModel[]): void`
|
||||
Handle single field
|
||||
- `filterFieldsByType(fields: FormFieldModel[], types?: string[]): FormFieldModel[]`
|
||||
Filter fields based on type
|
||||
- `markAsInvalid(): void`
|
||||
Set form as invalid
|
||||
- `parseOutcomes()`
|
||||
Parse outcomes from json
|
||||
- `addValuesNotPresent(valuesToSetIfNotPresent: FormValues)`
|
||||
Set values if they are not present
|
||||
- `isValidDropDown(key: string): boolean`
|
||||
Validates dropdown
|
||||
- `setNodeIdValueForViewersLinkedToUploadWidget(linkedUploadWidgetContentSelected: UploadWidgetContentLinkModel)`
|
||||
Set node id
|
||||
- `changeFieldVisibility(fieldId: string, visibility: boolean): void`
|
||||
Changes field visibility
|
||||
- `changeFieldDisabled(fieldId: string, disabled: boolean): void`
|
||||
Changes disabled status of field
|
||||
- `changeFieldRequired(fieldId: string, required: boolean): void`
|
||||
Changes required status of field
|
||||
- `changeFieldValue(fieldId: string, value: any): void`
|
||||
Changes field value
|
||||
- `changeVariableValue(variableId: string, value: any): void`
|
||||
Changes variable value
|
||||
- `loadInjectedFieldValidators(injectedFieldValidators: FormFieldValidator[]): void`
|
||||
Checks it there are any injectedValidators and adds them to the array of field validators.
|
@ -191,3 +191,32 @@ class MyComponent {
|
||||
- `handleError(error: any):`[`Observable`](http://reactivex.io/documentation/observable.html)`<any>`
|
||||
Reports an error message.
|
||||
- `error` - Data object with optional \`message\` and \`status\` fields for the error
|
||||
|
||||
### Properties
|
||||
| Name | Type | Description |
|
||||
| ---- | --------- | ----------- |
|
||||
| fieldValidators | FormFieldValidator[] | Array of Field Validators injected with token and then passed to FormModel |
|
||||
|
||||
|
||||
### Inject Preference service
|
||||
|
||||
Token: [`FORM_SERVICE_FIELD_VALIDATORS_TOKEN`]
|
||||
A DI token that allows to inject additional form field validators.
|
||||
|
||||
```ts
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FORM_SERVICE_FIELD_VALIDATORS_TOKEN } from '@alfresco/adf-core';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
...Import Required Modules
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: FORM_SERVICE_FIELD_VALIDATORS_TOKEN,
|
||||
useValue: [new AdditionalFormFieldValidator()]
|
||||
}
|
||||
]
|
||||
})
|
||||
export class ExampleModule {}
|
||||
```
|
||||
|
@ -92,6 +92,36 @@ class MyComponent {
|
||||
- _values:_ [`FormValues`](../../../lib/core/src/lib/form/components/widgets/core/form-values.ts) - [Form](../../../lib/process-services/src/lib/task-list/models/form.model.ts) values object
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskDetailsCloudModel`](../../../lib/process-services-cloud/src/lib/task/models/task-details-cloud.model.ts)`>` - Updated task details
|
||||
|
||||
### Properties
|
||||
| Name | Type | Description |
|
||||
| ---- | --------- | ----------- |
|
||||
| fieldValidators | FormFieldValidator[] | Array of Field Validators injected with token and then passed to FormModel |
|
||||
|
||||
|
||||
### Inject Preference service
|
||||
|
||||
Token: [`FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN`]
|
||||
A DI token that allows to inject additional form field validators.
|
||||
|
||||
```ts
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN } from '@alfresco/adf-process-services-cloud';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
...Import Required Modules
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN,
|
||||
useValue: [new AdditionalFormFieldValidator()]
|
||||
}
|
||||
]
|
||||
})
|
||||
export class ExampleModule {}
|
||||
```
|
||||
|
||||
|
||||
## See also
|
||||
|
||||
- [Form cloud component](../components/form-cloud.component.md)
|
||||
|
@ -1686,3 +1686,9 @@ export const mockFormWithSections = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const fakeValidatorMock = {
|
||||
supportedTypes: ['test'],
|
||||
isSupported: () => true,
|
||||
validate: () => true
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ import { FormFieldModel } from './form-field.model';
|
||||
import { FormOutcomeModel } from './form-outcome.model';
|
||||
import { FormModel } from './form.model';
|
||||
import { TabModel } from './tab.model';
|
||||
import { fakeMetadataForm, mockDisplayExternalPropertyForm, mockFormWithSections } from '../../mock/form.mock';
|
||||
import { fakeMetadataForm, mockDisplayExternalPropertyForm, mockFormWithSections, fakeValidatorMock } from '../../mock/form.mock';
|
||||
import { CoreTestingModule } from '../../../../testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
@ -433,6 +433,13 @@ describe('FormModel', () => {
|
||||
expect(FORM_FIELD_VALIDATORS.length).toBe(defaultLength);
|
||||
});
|
||||
|
||||
it('should include injected field validators', () => {
|
||||
const form = new FormModel({}, null, false, formService, undefined, [fakeValidatorMock]);
|
||||
const defaultLength = FORM_FIELD_VALIDATORS.length;
|
||||
|
||||
expect(form.fieldValidators.length).toBe(defaultLength + 1);
|
||||
});
|
||||
|
||||
describe('variables', () => {
|
||||
let form: FormModel;
|
||||
|
||||
|
@ -85,7 +85,7 @@ export class FormModel implements ProcessFormModel {
|
||||
tabs: TabModel[] = [];
|
||||
fields: (ContainerModel | FormFieldModel)[] = [];
|
||||
outcomes: FormOutcomeModel[] = [];
|
||||
fieldValidators: FormFieldValidator[] = [...FORM_FIELD_VALIDATORS];
|
||||
fieldValidators: FormFieldValidator[] = [];
|
||||
customFieldTemplates: FormFieldTemplates = {};
|
||||
theme?: ThemeModel;
|
||||
|
||||
@ -100,7 +100,8 @@ export class FormModel implements ProcessFormModel {
|
||||
formValues?: FormValues,
|
||||
readOnly: boolean = false,
|
||||
protected formService?: FormValidationService,
|
||||
enableFixedSpace?: boolean
|
||||
enableFixedSpace?: boolean,
|
||||
injectedFieldValidators?: FormFieldValidator[]
|
||||
) {
|
||||
this.readOnly = readOnly;
|
||||
this.json = json;
|
||||
@ -133,6 +134,7 @@ export class FormModel implements ProcessFormModel {
|
||||
this.parseOutcomes();
|
||||
}
|
||||
|
||||
this.loadInjectedFieldValidators(injectedFieldValidators);
|
||||
this.validateForm();
|
||||
}
|
||||
|
||||
@ -501,4 +503,8 @@ export class FormModel implements ProcessFormModel {
|
||||
variable.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private loadInjectedFieldValidators(injectedFieldValidators: FormFieldValidator[]): void {
|
||||
this.fieldValidators = injectedFieldValidators ? [...FORM_FIELD_VALIDATORS, ...injectedFieldValidators] : [...FORM_FIELD_VALIDATORS];
|
||||
}
|
||||
}
|
||||
|
@ -17,15 +17,23 @@
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { formModelTabs } from '../../mock';
|
||||
import { FormService } from './form.service';
|
||||
import { FORM_SERVICE_FIELD_VALIDATORS_TOKEN, FormService } from './form.service';
|
||||
import { CoreTestingModule } from '../../testing';
|
||||
import { FORM_FIELD_VALIDATORS, FormFieldValidator } from '../public-api';
|
||||
|
||||
const fakeValidator = {
|
||||
supportedTypes: ['test'],
|
||||
isSupported: () => true,
|
||||
validate: () => true
|
||||
} as FormFieldValidator;
|
||||
|
||||
describe('Form service', () => {
|
||||
let service: FormService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule]
|
||||
imports: [CoreTestingModule],
|
||||
providers: [{ provide: FORM_SERVICE_FIELD_VALIDATORS_TOKEN, useValue: [fakeValidator] }]
|
||||
});
|
||||
service = TestBed.inject(FormService);
|
||||
});
|
||||
@ -36,5 +44,11 @@ describe('Form service', () => {
|
||||
const formParsed = service.parseForm(formModelTabs);
|
||||
expect(formParsed).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return form with injected field validators', () => {
|
||||
expect(formModelTabs.formRepresentation.formDefinition).toBeDefined();
|
||||
const formParsed = service.parseForm(formModelTabs);
|
||||
expect(formParsed.fieldValidators).toEqual([...FORM_FIELD_VALIDATORS, fakeValidator]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable, InjectionToken, Optional } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ContentLinkModel } from '../components/widgets/core/content-link.model';
|
||||
import { FormOutcomeEvent } from '../components/widgets/core/form-outcome-event.model';
|
||||
@ -30,12 +30,15 @@ import { ValidateFormFieldEvent } from '../events/validate-form-field.event';
|
||||
import { FormValidationService } from './form-validation-service.interface';
|
||||
import { FormRulesEvent } from '../events/form-rules.event';
|
||||
import { FormSpinnerEvent } from '../events';
|
||||
import { FormFieldModel } from '../components/widgets';
|
||||
import { FormFieldModel, FormFieldValidator } from '../components/widgets';
|
||||
|
||||
export const FORM_SERVICE_FIELD_VALIDATORS_TOKEN = new InjectionToken<FormFieldValidator[]>('FORM_SERVICE_FIELD_VALIDATORS_TOKEN');
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FormService implements FormValidationService {
|
||||
private fieldValidators: FormFieldValidator[];
|
||||
formLoaded = new Subject<FormEvent>();
|
||||
formDataRefreshed = new Subject<FormEvent>();
|
||||
formFieldValueChanged = new Subject<FormFieldEvent>();
|
||||
@ -59,7 +62,9 @@ export class FormService implements FormValidationService {
|
||||
|
||||
formRulesEvent = new Subject<FormRulesEvent>();
|
||||
|
||||
constructor() {}
|
||||
constructor(@Optional() @Inject(FORM_SERVICE_FIELD_VALIDATORS_TOKEN) injectedFieldValidators?: FormFieldValidator[]) {
|
||||
this.fieldValidators = injectedFieldValidators || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses JSON data to create a corresponding Form model.
|
||||
@ -72,7 +77,7 @@ export class FormService implements FormValidationService {
|
||||
*/
|
||||
parseForm(json: any, data?: FormValues, readOnly: boolean = false, fixedSpace?: boolean): FormModel {
|
||||
if (json) {
|
||||
const form = new FormModel(json, data, readOnly, this, fixedSpace);
|
||||
const form = new FormModel(json, data, readOnly, this, fixedSpace, this.fieldValidators);
|
||||
if (!json.fields) {
|
||||
form.outcomes = [
|
||||
new FormOutcomeModel(form, {
|
||||
|
@ -16,10 +16,11 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { FormCloudService } from './form-cloud.service';
|
||||
import { FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN, FormCloudService } from './form-cloud.service';
|
||||
import { of } from 'rxjs';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
||||
import { FORM_FIELD_VALIDATORS, FormFieldValidator } from '@alfresco/adf-core';
|
||||
|
||||
const mockTaskResponseBody = {
|
||||
entry: { id: 'id', name: 'name', formKey: 'form-key' }
|
||||
@ -27,6 +28,12 @@ const mockTaskResponseBody = {
|
||||
|
||||
const mockFormResponseBody = { formRepresentation: { id: 'form-id', name: 'task-form', taskId: 'task-id' } };
|
||||
|
||||
const fakeValidator = {
|
||||
supportedTypes: ['test'],
|
||||
isSupported: () => true,
|
||||
validate: () => true
|
||||
} as FormFieldValidator;
|
||||
|
||||
describe('Form Cloud service', () => {
|
||||
let service: FormCloudService;
|
||||
let adfHttpClient: AdfHttpClient;
|
||||
@ -37,7 +44,8 @@ describe('Form Cloud service', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule]
|
||||
imports: [ProcessServiceCloudTestingModule],
|
||||
providers: [{ provide: FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN, useValue: [fakeValidator] }]
|
||||
});
|
||||
service = TestBed.inject(FormCloudService);
|
||||
adfHttpClient = TestBed.inject(AdfHttpClient);
|
||||
@ -68,6 +76,14 @@ describe('Form Cloud service', () => {
|
||||
expect(result.id).toBe(formId);
|
||||
expect(result.name).toBe('task-form');
|
||||
});
|
||||
|
||||
it('should create form with injected validators', () => {
|
||||
const formId = 'form-id';
|
||||
const json = { formRepresentation: { id: formId, name: 'task-form', taskId: 'task-id', formDefinition: {} } };
|
||||
const result = service.parseForm(json, undefined, undefined);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.fieldValidators).toEqual([...FORM_FIELD_VALIDATORS, fakeValidator]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Task tests', () => {
|
||||
|
@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormValues, FormModel, FormFieldOption } from '@alfresco/adf-core';
|
||||
import { Inject, Injectable, InjectionToken, Optional } from '@angular/core';
|
||||
import { FormValues, FormModel, FormFieldOption, FormFieldValidator } from '@alfresco/adf-core';
|
||||
import { Observable, from, EMPTY } from 'rxjs';
|
||||
import { expand, map, reduce, switchMap } from 'rxjs/operators';
|
||||
import { TaskDetailsCloudModel } from '../../task/models/task-details-cloud.model';
|
||||
@ -27,18 +27,25 @@ import { FormContent } from '../../services/form-fields.interfaces';
|
||||
import { FormCloudServiceInterface } from './form-cloud.service.interface';
|
||||
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
||||
|
||||
export const FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN = new InjectionToken<FormFieldValidator[]>('FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN');
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FormCloudService extends BaseCloudService implements FormCloudServiceInterface {
|
||||
private _uploadApi: UploadApi;
|
||||
private fieldValidators: FormFieldValidator[];
|
||||
get uploadApi(): UploadApi {
|
||||
this._uploadApi = this._uploadApi ?? new UploadApi(this.apiService.getInstance());
|
||||
return this._uploadApi;
|
||||
}
|
||||
|
||||
constructor(adfHttpClient: AdfHttpClient) {
|
||||
constructor(
|
||||
adfHttpClient: AdfHttpClient,
|
||||
@Optional() @Inject(FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN) injectedFieldValidators?: FormFieldValidator[]
|
||||
) {
|
||||
super(adfHttpClient);
|
||||
this.fieldValidators = injectedFieldValidators || [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,7 +226,7 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
|
||||
formValues[variable.name] = variable.value;
|
||||
});
|
||||
|
||||
return new FormModel(flattenForm, formValues, readOnly);
|
||||
return new FormModel(flattenForm, formValues, readOnly, undefined, undefined, this.fieldValidators);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user