AAE-27343 Listen to form rules changes from reactive widgets (#10392)

* AAE-27343 Listen to form rules changes from reactive widgets

* remove leftover

* apply interface for reactive widgets and unit test

* update readonly control status

* [ci:force][link-adf:fix/AAE-27343-listen-to-form-rules-changes-in-reactive-widgets]
This commit is contained in:
Tomasz Gnyp
2024-11-29 10:27:24 +01:00
committed by GitHub
parent 7eb51ef58f
commit 23fe4d4486
9 changed files with 192 additions and 88 deletions

View File

@@ -42,7 +42,7 @@ describe('FormFieldComponent', () => {
fixture.destroy(); fixture.destroy();
}); });
it('should create default component instance', (done) => { it('should create default component instance', () => {
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT, type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET' id: 'FAKE-TXT-WIDGET'
@@ -51,14 +51,29 @@ describe('FormFieldComponent', () => {
component.field = field; component.field = field;
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.componentRef).toBeDefined(); expect(component.componentRef).toBeDefined();
expect(component.componentRef.instance instanceof TextWidgetComponent).toBeTruthy(); expect(component.componentRef.instance instanceof TextWidgetComponent).toBeTruthy();
done();
});
}); });
it('should create custom component instance', (done) => { it('should call update form control state for reactive type widget on formRulesEvent change', () => {
const field = new FormFieldModel(form, {
type: FormFieldTypes.DATE,
id: 'FAKE-DATE-WIDGET'
});
component.field = field;
fixture.detectChanges();
const widgetInstance = component.componentRef.instance;
const updateFormControlState = spyOn(widgetInstance, 'updateReactiveFormControl');
widgetInstance.formService.formRulesEvent.next();
fixture.detectChanges();
expect(updateFormControlState).toHaveBeenCalled();
});
it('should create custom component instance', () => {
formRenderingService.setComponentTypeResolver(FormFieldTypes.AMOUNT, () => CheckboxWidgetComponent, true); formRenderingService.setComponentTypeResolver(FormFieldTypes.AMOUNT, () => CheckboxWidgetComponent, true);
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
@@ -67,16 +82,14 @@ describe('FormFieldComponent', () => {
}); });
component.field = field; component.field = field;
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.componentRef).toBeDefined(); expect(component.componentRef).toBeDefined();
expect(component.componentRef.instance instanceof CheckboxWidgetComponent).toBeTruthy(); expect(component.componentRef.instance instanceof CheckboxWidgetComponent).toBeTruthy();
done();
});
}); });
it('should require component type to be resolved', (done) => { it('should require component type to be resolved', () => {
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT, type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET' id: 'FAKE-TXT-WIDGET'
@@ -84,16 +97,14 @@ describe('FormFieldComponent', () => {
spyOn(formRenderingService, 'resolveComponentType').and.returnValue(null); spyOn(formRenderingService, 'resolveComponentType').and.returnValue(null);
component.field = field; component.field = field;
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
expect(formRenderingService.resolveComponentType).toHaveBeenCalled(); expect(formRenderingService.resolveComponentType).toHaveBeenCalled();
expect(component.componentRef).toBeUndefined(); expect(component.componentRef).toBeUndefined();
done();
});
}); });
it('should hide the field when it is not visible', (done) => { it('should hide the field when it is not visible', () => {
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT, type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET' id: 'FAKE-TXT-WIDGET'
@@ -101,29 +112,26 @@ describe('FormFieldComponent', () => {
component.field = field; component.field = field;
component.field.isVisible = false; component.field.isVisible = false;
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
const debugElement = fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style; const debugElement = fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style;
expect(debugElement.visibility).toEqual('hidden'); expect(debugElement.visibility).toEqual('hidden');
expect(debugElement.display).toEqual('none'); expect(debugElement.display).toEqual('none');
done();
});
}); });
it('should show the field when it is visible', (done) => { it('should show the field when it is visible', () => {
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT, type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET' id: 'FAKE-TXT-WIDGET'
}); });
component.field = field; component.field = field;
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.visibility).toEqual('visible'); expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.visibility).toEqual('visible');
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.display).toEqual('block'); expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.display).toEqual('block');
done();
});
}); });
it('should hide a visible element', () => { it('should hide a visible element', () => {
@@ -142,7 +150,7 @@ describe('FormFieldComponent', () => {
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.display).toEqual('none'); expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').style.display).toEqual('none');
}); });
it('[C213878] - Should fields be correctly rendered when filled with process variables', async () => { it('[C213878] - Should fields be correctly rendered when filled with process variables', () => {
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
fieldType: 'HyperlinkRepresentation', fieldType: 'HyperlinkRepresentation',
id: 'label2', id: 'label2',

View File

@@ -20,6 +20,7 @@ import {
Component, Component,
ComponentFactory, ComponentFactory,
ComponentRef, ComponentRef,
DestroyRef,
inject, inject,
Input, Input,
NgModule, NgModule,
@@ -33,6 +34,8 @@ import { FormRenderingService } from '../../services/form-rendering.service';
import { WidgetVisibilityService } from '../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../services/widget-visibility.service';
import { FormFieldModel } from '../widgets/core/form-field.model'; import { FormFieldModel } from '../widgets/core/form-field.model';
import { FieldStylePipe } from '../../pipes/field-style.pipe'; import { FieldStylePipe } from '../../pipes/field-style.pipe';
import { FormFieldTypes } from '../widgets/core/form-field-types';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
declare const adf: any; declare const adf: any;
@@ -62,6 +65,7 @@ export class FormFieldComponent implements OnInit, OnDestroy {
private readonly formRenderingService = inject(FormRenderingService); private readonly formRenderingService = inject(FormRenderingService);
private readonly visibilityService = inject(WidgetVisibilityService); private readonly visibilityService = inject(WidgetVisibilityService);
private readonly destroyRef = inject(DestroyRef);
private readonly compiler = inject(Compiler); private readonly compiler = inject(Compiler);
ngOnInit() { ngOnInit() {
@@ -88,13 +92,28 @@ export class FormFieldComponent implements OnInit, OnDestroy {
instance.fieldChanged.subscribe((field) => { instance.fieldChanged.subscribe((field) => {
if (field && this.field.form) { if (field && this.field.form) {
this.visibilityService.refreshVisibility(field.form); this.visibilityService.refreshVisibility(field.form);
field.form.onFormFieldChanged(field); this.triggerFormFieldChanged(field);
} }
}); });
if (FormFieldTypes.isReactiveType(instance?.field?.type)) {
this.updateReactiveFormControlOnFormRulesEvent(instance);
} }
} }
} }
} }
}
private updateReactiveFormControlOnFormRulesEvent(instance: any): void {
instance?.formService.formRulesEvent.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
instance?.updateReactiveFormControl();
this.triggerFormFieldChanged(instance.field);
});
}
private triggerFormFieldChanged(field: FormFieldModel): void {
field.form.onFormFieldChanged(field);
}
ngOnDestroy() { ngOnDestroy() {
if (this.componentRef) { if (this.componentRef) {

View File

@@ -57,15 +57,14 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit {
maxDate: Date; maxDate: Date;
datetimeInputControl: FormControl<Date> = new FormControl<Date>(null); datetimeInputControl: FormControl<Date> = new FormControl<Date>(null);
public readonly formService = inject(FormService); public readonly formService = inject(FormService);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly dateAdapter = inject(DateAdapter); private readonly dateAdapter = inject(DateAdapter);
private readonly dateTimeAdapter = inject(DatetimeAdapter); private readonly dateTimeAdapter = inject(DatetimeAdapter);
ngOnInit(): void { ngOnInit(): void {
this.patchFormControl(); this.setFormControlValue();
this.updateFormControlState();
this.initDateAdapter(); this.initDateAdapter();
this.initDateRange(); this.initDateRange();
this.subscribeToDateChanges(); this.subscribeToDateChanges();
@@ -77,13 +76,21 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit {
this.onFieldChanged(this.field); this.onFieldChanged(this.field);
} }
private patchFormControl(): void { updateReactiveFormControl(): void {
this.datetimeInputControl.setValue(this.field.value, { emitEvent: false }); this.updateFormControlState();
this.datetimeInputControl.setValidators(this.isRequired() ? [Validators.required] : []); this.validateField();
if (this.field?.readOnly || this.readOnly) {
this.datetimeInputControl.disable({ emitEvent: false });
} }
private setFormControlValue(): void {
this.datetimeInputControl.setValue(this.field.value, { emitEvent: false });
}
private updateFormControlState(): void {
this.datetimeInputControl.setValidators(this.isRequired() ? [Validators.required] : []);
this.field?.readOnly || this.readOnly
? this.datetimeInputControl.disable({ emitEvent: false })
: this.datetimeInputControl.enable({ emitEvent: false });
this.datetimeInputControl.updateValueAndValidity({ emitEvent: false }); this.datetimeInputControl.updateValueAndValidity({ emitEvent: false });
} }

View File

@@ -32,6 +32,7 @@ import { WidgetComponent } from '../widget.component';
import { ErrorMessageModel } from '../core/error-message.model'; import { ErrorMessageModel } from '../core/error-message.model';
import { parseISO } from 'date-fns'; import { parseISO } from 'date-fns';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ReactiveFormWidget } from '../reactive-widget.interface';
@Component({ @Component({
selector: 'date-widget', selector: 'date-widget',
@@ -55,7 +56,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf], imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DateWidgetComponent extends WidgetComponent implements OnInit { export class DateWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget {
minDate: Date; minDate: Date;
maxDate: Date; maxDate: Date;
startAt: Date; startAt: Date;
@@ -68,7 +69,8 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void { ngOnInit(): void {
this.patchFormControl(); this.setFormControlValue();
this.updateFormControlState();
this.initDateAdapter(); this.initDateAdapter();
this.initDateRange(); this.initDateRange();
this.initStartAt(); this.initStartAt();
@@ -80,13 +82,22 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit {
this.validateField(); this.validateField();
this.onFieldChanged(this.field); this.onFieldChanged(this.field);
} }
private patchFormControl(): void {
this.dateInputControl.setValue(this.field.value, { emitEvent: false }); updateReactiveFormControl(): void {
this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []); this.updateFormControlState();
if (this.field?.readOnly || this.readOnly) { this.validateField();
this.dateInputControl.disable({ emitEvent: false });
} }
private setFormControlValue(): void {
this.dateInputControl.setValue(this.field.value, { emitEvent: false });
}
private updateFormControlState(): void {
this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []);
this.field?.readOnly || this.readOnly
? this.dateInputControl.disable({ emitEvent: false })
: this.dateInputControl.enable({ emitEvent: false });
this.dateInputControl.updateValueAndValidity({ emitEvent: false }); this.dateInputControl.updateValueAndValidity({ emitEvent: false });
} }

View File

@@ -34,6 +34,7 @@ import { DecimalWidgetComponent } from './decimal/decimal.component';
// core // core
export * from './widget.component'; export * from './widget.component';
export * from './reactive-widget.interface';
export * from './core'; export * from './core';
// primitives // primitives

View File

@@ -0,0 +1,23 @@
/*!
* @license
* Copyright © 2005-2024 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 { FormService } from '../../services/form.service';
export interface ReactiveFormWidget {
updateReactiveFormControl(): void;
formService: FormService;
}

View File

@@ -17,7 +17,7 @@
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, OnInit, ViewEncapsulation, DestroyRef, inject } from '@angular/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { import {
ADF_DATE_FORMATS, ADF_DATE_FORMATS,
@@ -27,7 +27,8 @@ import {
ErrorMessageModel, ErrorMessageModel,
ErrorWidgetComponent, ErrorWidgetComponent,
FormService, FormService,
WidgetComponent WidgetComponent,
ReactiveFormWidget
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { addDays, parseISO } from 'date-fns'; import { addDays, parseISO } from 'date-fns';
@@ -61,7 +62,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DateCloudWidgetComponent extends WidgetComponent implements OnInit { export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget {
typeId = 'DateCloudWidgetComponent'; typeId = 'DateCloudWidgetComponent';
minDate: Date = null; minDate: Date = null;
@@ -76,7 +77,8 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit
private readonly dateAdapter = inject(DateAdapter); private readonly dateAdapter = inject(DateAdapter);
ngOnInit(): void { ngOnInit(): void {
this.patchFormControl(); this.setFormControlValue();
this.updateFormControlState();
this.initDateAdapter(); this.initDateAdapter();
this.initRangeSelection(); this.initRangeSelection();
this.initStartAt(); this.initStartAt();
@@ -89,13 +91,21 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit
this.onFieldChanged(this.field); this.onFieldChanged(this.field);
} }
private patchFormControl(): void { updateReactiveFormControl(): void {
this.dateInputControl.setValue(this.field.value, { emitEvent: false }); this.updateFormControlState();
this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []); this.validateField();
if (this.field?.readOnly || this.readOnly) {
this.dateInputControl.disable({ emitEvent: false });
} }
private setFormControlValue(): void {
this.dateInputControl.setValue(this.field.value, { emitEvent: false });
}
private updateFormControlState(): void {
this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []);
this.field?.readOnly || this.readOnly
? this.dateInputControl.disable({ emitEvent: false })
: this.dateInputControl.enable({ emitEvent: false });
this.dateInputControl.updateValueAndValidity({ emitEvent: false }); this.dateInputControl.updateValueAndValidity({ emitEvent: false });
} }

View File

@@ -24,6 +24,7 @@ import {
FormFieldOption, FormFieldOption,
FormFieldTypes, FormFieldTypes,
FormService, FormService,
ReactiveFormWidget,
RuleEntry, RuleEntry,
SelectFilterInputComponent, SelectFilterInputComponent,
WidgetComponent WidgetComponent
@@ -66,10 +67,11 @@ export const HIDE_FILTER_LIMIT = 5;
SelectFilterInputComponent SelectFilterInputComponent
] ]
}) })
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit { export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget {
public formService = inject(FormService); public formService = inject(FormService);
private formCloudService = inject(FormCloudService); private formCloudService = inject(FormCloudService);
private appConfig = inject(AppConfigService); private appConfig = inject(AppConfigService);
private destroyRef = inject(DestroyRef);
typeId = 'DropdownCloudWidgetComponent'; typeId = 'DropdownCloudWidgetComponent';
showInputFilter = false; showInputFilter = false;
@@ -85,7 +87,6 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
private readonly defaultVariableOptionId = 'id'; private readonly defaultVariableOptionId = 'id';
private readonly defaultVariableOptionLabel = 'name'; private readonly defaultVariableOptionLabel = 'name';
private readonly defaultVariableOptionPath = 'data'; private readonly defaultVariableOptionPath = 'data';
private readonly destroyRef = inject(DestroyRef);
get showRequiredMessage(): boolean { get showRequiredMessage(): boolean {
return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed; return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed;
@@ -133,6 +134,11 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}); });
} }
updateReactiveFormControl(): void {
this.updateFormControlState();
this.handleErrors();
}
compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean { compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean {
if (!opt1 || !opt2) { if (!opt1 || !opt2) {
return false; return false;
@@ -165,19 +171,14 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
this.checkFieldOptionsSource(); this.checkFieldOptionsSource();
this.updateOptions(); this.updateOptions();
this.initFormControl(); this.setFormControlValue();
this.updateFormControlState();
this.subscribeToInputChanges();
this.initFilter(); this.initFilter();
this.handleErrors();
} }
private initFormControl(): void { private subscribeToInputChanges(): void {
if (this.field?.required) {
this.dropdownControl.addValidators([Validators.required]);
}
if (this.field?.readOnly || this.readOnly) {
this.dropdownControl.disable({ emitEvent: false });
}
this.dropdownControl.valueChanges this.dropdownControl.valueChanges
.pipe( .pipe(
filter(() => !!this.field), filter(() => !!this.field),
@@ -188,19 +189,31 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
this.handleErrors(); this.handleErrors();
this.selectionChangedForField(this.field); this.selectionChangedForField(this.field);
}); });
}
private setFormControlValue(): void {
this.dropdownControl.setValue(this.field?.value, { emitEvent: false }); this.dropdownControl.setValue(this.field?.value, { emitEvent: false });
this.handleErrors(); }
private updateFormControlState(): void {
this.dropdownControl.setValidators(this.isRequired() ? [Validators.required] : []);
this.field?.readOnly || this.readOnly
? this.dropdownControl.disable({ emitEvent: false })
: this.dropdownControl.enable({ emitEvent: false });
this.dropdownControl.updateValueAndValidity({ emitEvent: false });
} }
private handleErrors(): void { private handleErrors(): void {
if (this.dropdownControl.valid) { if (this.dropdownControl.valid) {
this.field.validationSummary = new ErrorMessageModel(''); this.field.validationSummary = new ErrorMessageModel('');
this.field.markAsValid();
return; return;
} }
if (this.dropdownControl.invalid && this.dropdownControl.errors.required) { if (this.dropdownControl.invalid && this.dropdownControl.errors.required) {
this.field.validationSummary = new ErrorMessageModel({ message: 'FORM.FIELD.REQUIRED' }); this.field.validationSummary = new ErrorMessageModel({ message: 'FORM.FIELD.REQUIRED' });
this.field.markAsInvalid();
} }
} }

View File

@@ -19,12 +19,13 @@
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { import {
ErrorMessageModel,
ErrorWidgetComponent,
FormFieldModel,
FormFieldOption,
FormService, FormService,
WidgetComponent FormFieldOption,
WidgetComponent,
ErrorWidgetComponent,
ErrorMessageModel,
FormFieldModel,
ReactiveFormWidget
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ProcessDefinitionService } from '../../services/process-definition.service'; import { ProcessDefinitionService } from '../../services/process-definition.service';
import { TaskFormService } from '../../services/task-form.service'; import { TaskFormService } from '../../services/task-form.service';
@@ -55,15 +56,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DropdownWidgetComponent extends WidgetComponent implements OnInit { export class DropdownWidgetComponent extends WidgetComponent implements OnInit, ReactiveFormWidget {
public formsService = inject(FormService); public formService = inject(FormService);
public taskFormService = inject(TaskFormService); public taskFormService = inject(TaskFormService);
public processDefinitionService = inject(ProcessDefinitionService); public processDefinitionService = inject(ProcessDefinitionService);
private readonly destroyRef = inject(DestroyRef);
dropdownControl = new FormControl<FormFieldOption | string>(undefined); dropdownControl = new FormControl<FormFieldOption | string>(undefined);
private readonly destroyRef = inject(DestroyRef);
get isReadOnlyType(): boolean { get isReadOnlyType(): boolean {
return this.field.type === 'readonly'; return this.field.type === 'readonly';
} }
@@ -93,7 +93,15 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
} }
} }
this.initFormControl(); this.setFormControlValue();
this.updateFormControlState();
this.subscribeToInputChanges();
this.handleErrors();
}
updateReactiveFormControl(): void {
this.updateFormControlState();
this.handleErrors();
} }
getValuesByTaskId() { getValuesByTaskId() {
@@ -124,15 +132,7 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
return !!this.field?.form?.readOnly; return !!this.field?.form?.readOnly;
} }
private initFormControl() { private subscribeToInputChanges(): void {
if (this.field?.required) {
this.dropdownControl.addValidators([this.customRequiredValidator(this.field)]);
}
if (this.field?.readOnly || this.readOnly) {
this.dropdownControl.disable({ emitEvent: false });
}
this.dropdownControl.valueChanges this.dropdownControl.valueChanges
.pipe( .pipe(
filter(() => !!this.field), filter(() => !!this.field),
@@ -143,9 +143,19 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
this.handleErrors(); this.handleErrors();
this.onFieldChanged(this.field); this.onFieldChanged(this.field);
}); });
}
private setFormControlValue(): void {
this.dropdownControl.setValue(this.getOptionValue(this.field?.value), { emitEvent: false }); this.dropdownControl.setValue(this.getOptionValue(this.field?.value), { emitEvent: false });
this.handleErrors(); }
private updateFormControlState(): void {
this.dropdownControl.setValidators(this.isRequired() ? [this.customRequiredValidator(this.field)] : []);
this.field?.readOnly || this.readOnly
? this.dropdownControl.disable({ emitEvent: false })
: this.dropdownControl.enable({ emitEvent: false });
this.dropdownControl.updateValueAndValidity({ emitEvent: false });
} }
private handleErrors() { private handleErrors() {
@@ -155,11 +165,13 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
if (this.dropdownControl.valid) { if (this.dropdownControl.valid) {
this.field.validationSummary = new ErrorMessageModel(''); this.field.validationSummary = new ErrorMessageModel('');
this.field.markAsValid();
return; return;
} }
if (this.dropdownControl.invalid && this.dropdownControl.errors.required) { if (this.dropdownControl.invalid && this.dropdownControl.errors.required) {
this.field.validationSummary = new ErrorMessageModel({ message: 'FORM.FIELD.REQUIRED' }); this.field.validationSummary = new ErrorMessageModel({ message: 'FORM.FIELD.REQUIRED' });
this.field.markAsInvalid();
} }
} }