diff --git a/lib/core/src/lib/form/components/form-renderer.component.spec.ts b/lib/core/src/lib/form/components/form-renderer.component.spec.ts index 6b00784efb..f1225820f8 100644 --- a/lib/core/src/lib/form/components/form-renderer.component.spec.ts +++ b/lib/core/src/lib/form/components/form-renderer.component.spec.ts @@ -38,7 +38,8 @@ import { checkboxWidgetFormVisibilityMock, dateWidgetFormVisibilityMock, multilineWidgetFormVisibilityMock, - displayTextWidgetFormVisibilityMock + displayTextWidgetFormVisibilityMock, + displayBigDecimalWidgetMock } from './mock/form-renderer.component.mock'; import { FormService } from '../services/form.service'; import { CoreTestingModule } from '../../testing'; @@ -849,4 +850,15 @@ describe('Form Renderer Component', () => { expectElementToBeHidden(displayTextContainer); }); }); + + describe('Display Bigdecimal Widget', () => { + it('should round decimal field value to correct precision', async () => { + formRendererComponent.formDefinition = formService.parseForm(displayBigDecimalWidgetMock.formRepresentation.formDefinition); + fixture.detectChanges(); + await fixture.whenStable(); + + const decimalInputElement = fixture.nativeElement.querySelector('#Decimal0tzu53'); + expect(decimalInputElement.value).toBeTruthy('10.12'); + }); + }); }); diff --git a/lib/core/src/lib/form/components/form-renderer.component.ts b/lib/core/src/lib/form/components/form-renderer.component.ts index cd8cda70a6..078244c6d5 100644 --- a/lib/core/src/lib/form/components/form-renderer.component.ts +++ b/lib/core/src/lib/form/components/form-renderer.component.ts @@ -15,11 +15,12 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, Input, OnDestroy, Injector, OnChanges } from '@angular/core'; +import { Component, ViewEncapsulation, Input, OnDestroy, Injector, OnChanges, OnInit, Inject } from '@angular/core'; import { FormRulesManager, formRulesManagerFactory } from '../models/form-rules.model'; import { FormModel } from './widgets/core/form.model'; import { ContainerModel, FormFieldModel, TabModel } from './widgets'; import { FormService } from '../services/form.service'; +import { FORM_FIELD_MODEL_RENDER_MIDDLEWARE, FormFieldModelRenderMiddleware } from './middlewares/middleware'; @Component({ selector: 'adf-form-renderer', @@ -31,10 +32,11 @@ import { FormService } from '../services/form.service'; useFactory: formRulesManagerFactory, deps: [Injector] } + ], encapsulation: ViewEncapsulation.None }) -export class FormRendererComponent implements OnChanges, OnDestroy { +export class FormRendererComponent implements OnInit, OnChanges, OnDestroy { /** Toggle debug options. */ @Input() showDebugButton: boolean = false; @@ -46,7 +48,16 @@ export class FormRendererComponent implements OnChanges, OnDestroy { fields: FormFieldModel[]; - constructor(public formService: FormService, private formRulesManager: FormRulesManager) {} + constructor( + public formService: FormService, + private formRulesManager: FormRulesManager, + @Inject(FORM_FIELD_MODEL_RENDER_MIDDLEWARE) + private middlewareServices: FormFieldModelRenderMiddleware[] + ) {} + + ngOnInit(): void { + this.runMiddlewareServices(); + } ngOnChanges(): void { this.formRulesManager.initialize(this.formDefinition); @@ -123,4 +134,16 @@ export class FormRendererComponent implements OnChanges, OnDestroy { const colspan = container ? container.field.colspan : 1; return (100 / container.field.numberOfColumns) * colspan + ''; } + + private runMiddlewareServices(): void { + const formFields = this.formDefinition.getFormFields(); + + formFields.forEach(field => { + this.middlewareServices.forEach((middlewareService) => { + if (middlewareService.type === field.type) { + field = middlewareService.getParsedField(field); + } + }); + }); + } } diff --git a/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.spec.ts b/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.spec.ts new file mode 100644 index 0000000000..efc2a588ab --- /dev/null +++ b/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.spec.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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, FormFieldTypes, FormModel } from '../widgets'; +import { DecimalRenderMiddlewareService } from './decimal-middleware.service'; + +describe('DecimalRenderMiddlewareService', () => { + let decimalMiddlewareService: DecimalRenderMiddlewareService; + let formFieldModel: FormFieldModel; + + beforeEach(() => { + decimalMiddlewareService = new DecimalRenderMiddlewareService(); + + const form = new FormModel(); + formFieldModel = new FormFieldModel(form, { + type: FormFieldTypes.DECIMAL, + id: 'id', + precision: 3, + value: '10.1060' + }); + }); + + it('should return field with proper precisison', () => { + formFieldModel.value = '10.1000000000000'; + formFieldModel.precision = 3; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe('10.100'); + }); + + it('should round up number with correct precisison', () => { + formFieldModel.value = '10.1039999'; + formFieldModel.precision = 3; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe('10.104'); + }); + + it('should round up number, when removed fraction part starts with number larger or equal 5', () => { + formFieldModel.value = '10.1035000'; + formFieldModel.precision = 3; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe('10.104'); + }); + + it('should NOT round up number, when removed fraction part starts with number smaller than 5', () => { + formFieldModel.value = '10.1034999'; + formFieldModel.precision = 3; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe('10.103'); + }); + + it('should return the same value when precision is correct', () => { + formFieldModel.value = '10.123'; + formFieldModel.precision = 3; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe('10.123'); + }); + + it('should work when value is not defined', () => { + formFieldModel.value = null; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe(null); + }); + + it('should work when value is number', () => { + formFieldModel.value = 3.333; + const parsedField = decimalMiddlewareService.getParsedField(formFieldModel); + expect(parsedField.value).toBe(3.333); + }); +}); diff --git a/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.ts b/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.ts new file mode 100644 index 0000000000..bca9a75669 --- /dev/null +++ b/lib/core/src/lib/form/components/middlewares/decimal-middleware.service.ts @@ -0,0 +1,53 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { Injectable } from '@angular/core'; +import { FormFieldModelRenderMiddleware } from './middleware'; +import { FormFieldModel, FormFieldTypes } from '../widgets'; + +@Injectable() +export class DecimalRenderMiddlewareService implements FormFieldModelRenderMiddleware { + type = FormFieldTypes.DECIMAL; + + getParsedField(field: FormFieldModel): FormFieldModel { + const allowedMaxPrecision = field.precision; + const value = field.value; + + field.value = this.forceMaxPrecisionIfNeeded(value, allowedMaxPrecision); + + return field; + } + + private forceMaxPrecisionIfNeeded(value: string | number, allowedMaxPrecision): string | number { + let numberOfDecimalDigits = 0; + const stringValue = typeof value === 'string' ? value : `${value}`; + const numberChunks = stringValue.split('.'); + + if (numberChunks.length === 2) { + numberOfDecimalDigits = numberChunks[1].length; + } + + if (numberOfDecimalDigits > allowedMaxPrecision) { + const valueWithCorrectPrecision = parseFloat(value.toString()) + .toFixed(allowedMaxPrecision); + + return valueWithCorrectPrecision; + } + + return value; + } +}; diff --git a/lib/core/src/lib/form/components/middlewares/middleware.ts b/lib/core/src/lib/form/components/middlewares/middleware.ts new file mode 100644 index 0000000000..16e443fcc0 --- /dev/null +++ b/lib/core/src/lib/form/components/middlewares/middleware.ts @@ -0,0 +1,26 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { InjectionToken } from '@angular/core'; +import { FormFieldModel } from '../../components/widgets'; + +export interface FormFieldModelRenderMiddleware { + type: string; + getParsedField(field: FormFieldModel): FormFieldModel; +} + +export const FORM_FIELD_MODEL_RENDER_MIDDLEWARE = new InjectionToken('RENDER_FORM_FIELD_MODEL_MIDDLEWARE'); diff --git a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts index 41e10f86b5..331f655e49 100644 --- a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts +++ b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { FormFieldTypes } from '../widgets'; + export const formDisplayValueVisibility = { formRepresentation: { id: 'form-3175b074-53c6-4b5b-92df-246b62108db3', @@ -2086,3 +2088,50 @@ export const displayTextWidgetFormVisibilityMock = { } } }; + +export const displayBigDecimalWidgetMock = { + formRepresentation: { + id: 'form-098756a5-2222-4c3a-1111-81dabc9a88b6', + name: 'displayBigdecimalForm', + description: '', + version: 0, + standAlone: true, + formDefinition: { + tabs: [], + fields: [ + { + id: '45269202-5f2a-438e-b14c-fe13eb4b2aa1', + name: 'Label', + type: 'container', + tab: null, + numberOfColumns: 2, + fields: { + 1: [ + { + id: 'Decimal0tzu53', + name: 'Bigdecimal', + type: FormFieldTypes.DECIMAL, + required: false, + colspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + precision: 2, + value: '10.12345678', + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + } + } + ], + outcomes: [], + metadata: {}, + variables: [] + } + } +}; diff --git a/lib/core/src/lib/form/components/widgets/decimal/decimal.component.html b/lib/core/src/lib/form/components/widgets/decimal/decimal.component.html index d712aadb51..28c624efa5 100644 --- a/lib/core/src/lib/form/components/widgets/decimal/decimal.component.html +++ b/lib/core/src/lib/form/components/widgets/decimal/decimal.component.html @@ -23,7 +23,6 @@ pattern="-?[0-9]*(\.[0-9]*)?" [id]="field.id" [required]="isRequired()" - [value]="displayValue" [(ngModel)]="field.value" (ngModelChange)="onFieldChanged(field)" [disabled]="field.readOnly" diff --git a/lib/core/src/lib/form/components/widgets/decimal/decimal.component.ts b/lib/core/src/lib/form/components/widgets/decimal/decimal.component.ts index 2d88fd5be0..91412a1df0 100644 --- a/lib/core/src/lib/form/components/widgets/decimal/decimal.component.ts +++ b/lib/core/src/lib/form/components/widgets/decimal/decimal.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, ViewEncapsulation } from '@angular/core'; import { FormService } from '../../../services/form.service'; import { WidgetComponent } from '../widget.component'; @@ -36,14 +36,8 @@ import { WidgetComponent } from '../widget.component'; }, encapsulation: ViewEncapsulation.None }) -export class DecimalWidgetComponent extends WidgetComponent implements OnInit { - displayValue: number; - +export class DecimalWidgetComponent extends WidgetComponent { constructor(public formService: FormService) { super(formService); } - - ngOnInit(): void { - this.displayValue = this.field.value; - } } diff --git a/lib/core/src/lib/form/form-base.module.ts b/lib/core/src/lib/form/form-base.module.ts index 03bddbe70b..4fdf95a4f2 100644 --- a/lib/core/src/lib/form/form-base.module.ts +++ b/lib/core/src/lib/form/form-base.module.ts @@ -37,6 +37,8 @@ import { EditJsonDialogModule } from '../dialogs/edit-json/edit-json.dialog.modu import { A11yModule } from '@angular/cdk/a11y'; import { ViewerModule } from '../viewer/viewer.module'; import { InplaceFormInputComponent } from './components/inplace-form-input/inplace-form-input.component'; +import { FORM_FIELD_MODEL_RENDER_MIDDLEWARE } from './components/middlewares/middleware'; +import { DecimalRenderMiddlewareService } from './components/middlewares/decimal-middleware.service'; @NgModule({ imports: [ @@ -70,7 +72,12 @@ import { InplaceFormInputComponent } from './components/inplace-form-input/inpla ...WIDGET_DIRECTIVES, InplaceFormInputComponent, WidgetComponent - ] + ], + providers: [{ + provide: FORM_FIELD_MODEL_RENDER_MIDDLEWARE, + useClass: DecimalRenderMiddlewareService, + multi: true + }] }) export class FormBaseModule { }