+
+
-
- {{ defaultOption.name }}
-
-
+
+
- {{opt.name}}
-
- {{field.value}}
+ {{opt.name}}
+ {{field.value}}
-
diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.scss b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.scss
index 7494893624e..db0671c88ec 100644
--- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.scss
+++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.scss
@@ -1,3 +1,5 @@
+@import 'styles/mat-selectors';
+
.adf {
&-dropdown-widget {
width: 100%;
@@ -25,5 +27,9 @@
&-dropdown-failed-message .adf-error-container {
margin-top: 1px;
}
+
+ #{$mat-select-arrow-wrapper} {
+ height: auto;
+ }
}
}
diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts
index f0b6dbba82b..7f1718491d4 100644
--- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts
+++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts
@@ -36,7 +36,6 @@ import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
-import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { DebugElement } from '@angular/core';
describe('DropdownCloudWidgetComponent', () => {
@@ -95,7 +94,7 @@ describe('DropdownCloudWidgetComponent', () => {
it('should select the default value when an option is chosen as default', async () => {
widget.field.value = 'option_2';
- expect(widget.fieldValue).toEqual('option_2');
+ expect(widget.field.value).toEqual('option_2');
});
it('should select the empty value when no default is chosen', async () => {
@@ -103,7 +102,7 @@ describe('DropdownCloudWidgetComponent', () => {
const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-select' }));
await dropdown.open();
- expect(widget.fieldValue).toEqual('empty');
+ expect(widget.field.value).toEqual('empty');
});
it('should load data from restUrl and populate options', async () => {
@@ -295,17 +294,14 @@ describe('DropdownCloudWidgetComponent', () => {
await dropdown.clickOptions({ selector: '[id="opt_1"]' });
expect(await dropdown.getValueText()).toEqual('option_1');
- expect(widget.fieldValue).toEqual('opt_1');
+ expect(widget.field.value).toEqual('opt_1');
await dropdown.open();
await dropdown.clickOptions({ selector: '[id="empty"]' });
- const formField = await loader.getHarness(MatFormFieldHarness);
- const dropdownLabel = await formField.getLabel();
+ expect(widget.field.value).toEqual(undefined);
- expect(dropdownLabel).toEqual('This is a mock none option');
- expect(widget.fieldValue).toEqual(undefined);
- expect(await dropdown.getValueText()).toEqual('');
+ expect(await dropdown.getValueText()).toEqual('This is a mock none option');
});
});
@@ -528,6 +524,58 @@ describe('DropdownCloudWidgetComponent', () => {
{ id: 'opt_4', name: 'option_4' }
]);
});
+
+ it('should fail (display error) for multiple type dropdown with zero selection', async () => {
+ widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: 'false' }), {
+ type: FormFieldTypes.DROPDOWN,
+ value: [{ id: 'id_cat', name: 'Cat' }],
+ required: true,
+ selectionType: 'multiple',
+ hasEmptyValue: false,
+ options: [
+ { id: 'id_cat', name: 'Cat' },
+ { id: 'id_dog', name: 'Dog' }
+ ]
+ });
+
+ const validateBeforeUnselect = widget.dropdownControl.valid;
+
+ const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-select' }));
+ await dropdown.clickOptions({ selector: '[id="id_cat"]' });
+
+ const validateAfterUnselect = widget.dropdownControl.valid;
+
+ expect(validateBeforeUnselect).toBe(true);
+ expect(validateAfterUnselect).toBe(false);
+ });
+
+ it('should fail (display error) for dropdown with null value', () => {
+ widget.field = new FormFieldModel(new FormModel(), {
+ type: FormFieldTypes.DROPDOWN,
+ value: null,
+ required: true,
+ options: [{ id: 'one', name: 'one' }],
+ selectionType: 'multiple'
+ });
+
+ widget.ngOnInit();
+
+ expect(widget.dropdownControl.valid).toBe(false);
+ });
+
+ it('should fail (display error) for dropdown with empty object', () => {
+ widget.field = new FormFieldModel(new FormModel(), {
+ type: FormFieldTypes.DROPDOWN,
+ value: {},
+ required: true,
+ options: [{ id: 'one', name: 'one' }],
+ selectionType: 'multiple'
+ });
+
+ widget.ngOnInit();
+
+ expect(widget.dropdownControl.valid).toBe(false);
+ });
});
describe('Linked Dropdown', () => {
@@ -697,7 +745,7 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges();
expect(widget.field.options).toEqual(mockConditionalEntries[0].options);
- expect(widget.fieldValue).toEqual('');
+ expect(widget.field.value).toEqual('');
});
it('should not reset the current value when it is part of the available options', () => {
@@ -710,7 +758,7 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges();
expect(widget.field.options).toEqual(mockConditionalEntries[0].options);
- expect(widget.fieldValue).toEqual('ATH');
+ expect(widget.field.value).toEqual('ATH');
});
it('should fire a form field value changed event when the value gets reset (notify children on the chain to reset)', () => {
diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts
index 2a1b147b71f..c9ce452aa16 100644
--- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts
+++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts
@@ -15,9 +15,11 @@
* limitations under the License.
*/
-import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import {
AppConfigService,
+ CardViewModule,
+ ErrorMessageModel,
+ ErrorWidgetComponent,
FormFieldEvent,
FormFieldModel,
FormFieldOption,
@@ -26,10 +28,16 @@ import {
RuleEntry,
WidgetComponent
} from '@alfresco/adf-core';
-import { FormCloudService } from '../../../services/form-cloud.service';
-import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs';
+import { AsyncPipe, NgFor, NgIf } from '@angular/common';
+import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
+import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatSelectModule } from '@angular/material/select';
+import { TranslateModule } from '@ngx-translate/core';
+import { BehaviorSubject, Subject } from 'rxjs';
import { filter, map, takeUntil } from 'rxjs/operators';
import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
+import { FormCloudService } from '../../../services/form-cloud.service';
export const DEFAULT_OPTION = {
id: 'empty',
@@ -43,27 +51,34 @@ export const HIDE_FILTER_LIMIT = 5;
selector: 'dropdown-cloud-widget',
templateUrl: './dropdown-cloud.widget.html',
styleUrls: ['./dropdown-cloud.widget.scss'],
- host: {
- '(click)': 'event($event)',
- '(blur)': 'event($event)',
- '(change)': 'event($event)',
- '(focus)': 'event($event)',
- '(focusin)': 'event($event)',
- '(focusout)': 'event($event)',
- '(input)': 'event($event)',
- '(invalid)': 'event($event)',
- '(select)': 'event($event)'
- },
- encapsulation: ViewEncapsulation.None
+ encapsulation: ViewEncapsulation.None,
+ standalone: true,
+ imports: [
+ NgIf,
+ NgFor,
+ AsyncPipe,
+ ReactiveFormsModule,
+ MatFormFieldModule,
+ MatSelectModule,
+ ErrorWidgetComponent,
+ TranslateModule,
+ CardViewModule // imported for adf-select-filter-input
+ ]
})
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy {
+ public formService = inject(FormService);
+ private formCloudService = inject(FormCloudService);
+ private appConfig = inject(AppConfigService);
+
typeId = 'DropdownCloudWidgetComponent';
showInputFilter = false;
isRestApiFailed = false;
variableOptionsFailed = false;
previewState = false;
restApiHostName: string;
- list$: Observable
;
+ dropdownControl = new FormControl(undefined);
+
+ list$ = new BehaviorSubject([]);
filter$ = new BehaviorSubject('');
private readonly defaultVariableOptionId = 'id';
@@ -72,30 +87,149 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
protected onDestroy$ = new Subject();
- constructor(public formService: FormService, private formCloudService: FormCloudService, private appConfig: AppConfigService) {
- super(formService);
+ get showRequiredMessage(): boolean {
+ return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed;
+ }
+
+ get isReadOnlyType(): boolean {
+ return this.field.type === 'readonly';
+ }
+
+ private get isLinkedWidget(): boolean {
+ return !!this.linkedWidgetId;
+ }
+
+ private get linkedWidgetId(): string {
+ return this.field?.rule?.ruleOn;
+ }
+
+ private get isReadOnlyForm(): boolean {
+ return !!this.field?.form?.readOnly;
+ }
+
+ private get hasRestUrl(): boolean {
+ return !!this.field?.restUrl;
+ }
+
+ private get isValidRestConfig(): boolean {
+ return this.isRestOptionType && this.hasRestUrl;
+ }
+
+ private get isRestOptionType(): boolean {
+ return this.field?.optionType === 'rest';
+ }
+
+ private get isVariableOptionType(): boolean {
+ return this.field?.optionType === 'variable';
}
ngOnInit() {
this.setPreviewState();
+
this.checkFieldOptionsSource();
this.updateOptions();
+
+ this.initFormControl();
+ this.initFilter();
+ }
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+
+ compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean {
+ if (!opt1 || !opt2) {
+ return false;
+ }
+
+ if (typeof opt1 === 'string' && typeof opt2 === 'object') {
+ return opt1 === opt2.id || opt1 === opt2.name;
+ }
+
+ if (typeof opt1 === 'object' && typeof opt2 === 'string') {
+ return opt1.id === opt2 || opt1.name === opt2;
+ }
+
+ if (typeof opt1 === 'object' && typeof opt2 === 'object') {
+ return opt1.id === opt2.id || opt1.name === opt2.name;
+ }
+
+ return opt1 === opt2;
+ }
+
+ selectionChangedForField(field: FormFieldModel): void {
+ const formFieldValueChangedEvent = new FormFieldEvent(field.form, field);
+ this.formService.formFieldValueChanged.next(formFieldValueChangedEvent);
+ this.onFieldChanged(field);
+ }
+
+ private initFormControl(): void {
+ if (this.field?.required) {
+ this.dropdownControl.addValidators([Validators.required]);
+ }
+
+ if (this.field?.readOnly || this.readOnly) {
+ this.dropdownControl.disable({ emitEvent: false });
+ }
+
+ this.dropdownControl.valueChanges
+ .pipe(
+ filter(() => !!this.field),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe((value) => {
+ this.setOptionValue(value, this.field);
+ this.selectionChangedForField(this.field);
+ });
+
+ this.dropdownControl.statusChanges
+ .pipe(
+ filter(() => !!this.field),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe(() => this.handleErrors());
+
+ this.dropdownControl.setValue(this.field?.value, { emitEvent: false });
+ this.handleErrors();
+ }
+
+ private handleErrors(): void {
+ if (this.dropdownControl.valid) {
+ this.field.validationSummary = new ErrorMessageModel('');
+ return;
+ }
+
+ if (this.dropdownControl.invalid && this.dropdownControl.errors.required) {
+ this.field.validationSummary = new ErrorMessageModel({ message: 'FORM.FIELD.REQUIRED' });
+ }
+ }
+
+ private initFilter(): void {
+ this.filter$
+ .pipe(
+ filter((search) => search !== undefined),
+ map((search) =>
+ search ? this.field.options.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase())) : this.field.options
+ ),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe((result) => this.list$.next(result));
}
private checkFieldOptionsSource(): void {
switch (true) {
- case this.isReadOnlyForm():
+ case this.isReadOnlyForm:
break;
-
- case this.isValidRestConfig() && !this.isLinkedWidget():
+ case this.isValidRestConfig && !this.isLinkedWidget:
this.persistFieldOptionsFromRestApi();
break;
- case this.isLinkedWidget():
+ case this.isLinkedWidget:
this.loadFieldOptionsForLinkedWidget();
break;
- case this.isVariableOptionType():
+ case this.isVariableOptionType:
this.persistFieldOptionsFromVariable();
break;
@@ -114,7 +248,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
if (dropdownOptions) {
const formVariableOptions: FormFieldOption[] = this.getOptionsFromPath(dropdownOptions, optionsPath);
- this.field.options = formVariableOptions;
+ this.updateOptions(formVariableOptions);
this.resetInvalidValue();
this.field.updateForm();
} else {
@@ -189,42 +323,33 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
?.value;
}
- private isVariableOptionType(): boolean {
- return this.field?.optionType === 'variable';
- }
-
- private isRestOptionType(): boolean {
- return this.field?.optionType === 'rest';
- }
-
private persistFieldOptionsFromRestApi() {
- if (this.isValidRestConfig()) {
+ if (this.isValidRestConfig) {
this.resetRestApiErrorMessage();
const bodyParam = this.buildBodyParam();
this.formCloudService
.getRestWidgetData(this.field.form.id, this.field.id, bodyParam)
.pipe(takeUntil(this.onDestroy$))
- .subscribe(
- (result: FormFieldOption[]) => {
+ .subscribe({
+ next: (result: FormFieldOption[]) => {
this.resetRestApiErrorMessage();
- this.field.options = result;
- this.updateOptions();
+ this.updateOptions(result);
this.field.updateForm();
this.resetInvalidValue();
},
- (err) => {
+ error: (err) => {
this.resetRestApiOptions();
this.handleError(err);
}
- );
+ });
}
}
private buildBodyParam(): any {
const bodyParam = Object.assign({});
- if (this.isLinkedWidget()) {
+ if (this.isLinkedWidget) {
const parentWidgetValue = this.getParentWidgetValue();
- const parentWidgetId = this.getLinkedWidgetId();
+ const parentWidgetId = this.linkedWidgetId;
bodyParam[parentWidgetId] = parentWidgetValue;
}
return bodyParam;
@@ -246,20 +371,20 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
private getParentWidgetValue(): string {
- const parentWidgetId = this.getLinkedWidgetId();
+ const parentWidgetId = this.linkedWidgetId;
const parentWidget = this.getFormFieldById(parentWidgetId);
return parentWidget?.value;
}
private parentValueChanged(value: string) {
if (value && !this.isNoneValueSelected(value)) {
- this.isValidRestConfig() ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value);
+ this.isValidRestConfig ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value);
} else if (this.isNoneValueSelected(value)) {
this.resetRestApiErrorMessage();
this.resetOptions();
this.resetInvalidValue();
} else {
- this.field.options = [];
+ this.updateOptions([]);
this.resetInvalidValue();
}
}
@@ -277,7 +402,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
const rulesEntries = this.field.rule.entries;
rulesEntries.forEach((ruleEntry: RuleEntry) => {
if (ruleEntry.key === value) {
- this.field.options = ruleEntry.options;
+ this.updateOptions(ruleEntry.options);
this.resetInvalidValue();
this.field.updateForm();
}
@@ -299,92 +424,51 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
private isValidValue(): boolean {
- return this.fieldValue && this.isSelectedValueInOptions();
+ return this.field.value && this.isSelectedValueInOptions();
}
private isSelectedValueInOptions(): boolean {
- if (Array.isArray(this.fieldValue)) {
+ if (Array.isArray(this.field.value)) {
const optionIdList = [...this.field.options].map((option) => option.id);
- const fieldValueIds = this.fieldValue.map((valueOption) => valueOption.id);
+ const fieldValueIds = this.field.value.map((valueOption) => valueOption.id);
return fieldValueIds.every((valueOptionId) => optionIdList.includes(valueOptionId));
} else {
- return [...this.field.options].map((option) => option.id).includes(this.fieldValue);
+ return [...this.field.options].map((option) => option.id).includes(this.field.value);
}
}
- get fieldValue(): string {
- return this.field.value;
- }
-
private hasRuleEntries(): boolean {
return !!this.field.rule.entries.length;
}
private resetOptions() {
- this.field.options = [];
- this.updateOptions();
- }
-
- selectionChangedForField(field: FormFieldModel) {
- const formFieldValueChangedEvent = new FormFieldEvent(field.form, field);
- this.formService.formFieldValueChanged.next(formFieldValueChangedEvent);
- this.onFieldChanged(field);
+ this.updateOptions([]);
}
private isParentFormFieldEvent(event: FormFieldEvent): boolean {
- return event.field.id === this.getLinkedWidgetId();
+ return event.field.id === this.linkedWidgetId;
}
private isFormFieldEventOfTypeDropdown(event: FormFieldEvent): boolean {
return event.field.type === FormFieldTypes.DROPDOWN;
}
- isLinkedWidget(): boolean {
- return !!this.getLinkedWidgetId();
- }
-
- getLinkedWidgetId(): string {
- return this.field?.rule?.ruleOn;
- }
-
- compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean {
- if (!opt1 || !opt2) {
- return false;
- }
-
- if (typeof opt1 === 'string' && typeof opt2 === 'object') {
- return opt1 === opt2.id || opt1 === opt2.name;
- }
-
- if (typeof opt1 === 'object' && typeof opt2 === 'string') {
- return opt1.id === opt2 || opt1.name === opt2;
- }
-
- if (typeof opt1 === 'object' && typeof opt2 === 'object') {
- return opt1.id === opt2.id || opt1.name === opt2.name;
- }
-
- return opt1 === opt2;
- }
-
- getOptionValue(option: FormFieldOption, fieldValue: string): string | FormFieldOption {
- if (this.field.hasMultipleValues) {
- return option;
+ private setOptionValue(option: FormFieldOption | FormFieldOption[], field: FormFieldModel) {
+ if (Array.isArray(option) || field.hasMultipleValues) {
+ field.value = option;
+ return;
}
let optionValue: string = '';
if (option.id === DEFAULT_OPTION.id) {
optionValue = undefined;
- } else if (option.name !== fieldValue) {
+ } else if (option.name !== field.value) {
optionValue = option.id;
} else {
optionValue = option.name;
}
- return optionValue;
- }
- private isValidRestConfig(): boolean {
- return this.isRestOptionType() && this.hasRestUrl();
+ field.value = optionValue;
}
private setPreviewState(): void {
@@ -397,51 +481,26 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
}
- private isReadOnlyForm(): boolean {
- return !!this.field?.form?.readOnly;
- }
-
- get isReadOnlyField(): boolean {
- return this.field.readOnly;
- }
-
- private hasRestUrl(): boolean {
- return !!this.field?.restUrl;
- }
-
- isReadOnlyType(): boolean {
- return this.field.type === 'readonly';
- }
-
- ngOnDestroy() {
- this.onDestroy$.next(true);
- this.onDestroy$.complete();
- }
+ private updateOptions(options?: FormFieldOption[]): void {
+ if (!this.field) {
+ return;
+ }
- updateOptions(): void {
- if (this.isReadOnlyForm()) {
- this.list$ = of(this.field.options);
+ if (options) {
+ this.field.options = options;
}
+ this.list$.next(this.field.options);
this.showInputFilter = this.field.options.length > this.appConfig.get('form.dropDownFilterLimit', HIDE_FILTER_LIMIT);
- this.list$ = combineLatest([of(this.field.options), this.filter$]).pipe(
- map(([items, search]) => {
- if (!search) {
- return items;
- }
- return items.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase()));
- }),
- takeUntil(this.onDestroy$)
- );
}
- resetRestApiErrorMessage() {
+ private resetRestApiErrorMessage(): void {
this.isRestApiFailed = false;
this.restApiHostName = '';
}
- resetRestApiOptions() {
- this.field.options = [];
+ private resetRestApiOptions(): void {
+ this.updateOptions([]);
this.resetValue();
this.isRestApiFailed = true;
this.restApiHostName = this.getRestUrlHostName();
@@ -454,17 +513,4 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
return this.field?.restUrl;
}
}
-
- showRequiredMessage(): boolean {
- return (
- (this.isInvalidFieldRequired() || (this.isNoneValueSelected(this.field.value) && this.isRequired())) &&
- this.isTouched() &&
- !this.isRestApiFailed &&
- !this.variableOptionsFailed
- );
- }
-
- getDefaultOption(options: FormFieldOption[]): FormFieldOption {
- return options.find((option: FormFieldOption) => option.id === DEFAULT_OPTION.id);
- }
}
diff --git a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts
index e37e1a16b0d..a15a21ccb11 100644
--- a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts
+++ b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts
@@ -29,7 +29,6 @@ import {
CONTENT_UPLOAD_DIRECTIVES,
ContentNodeSelectorModule
} from '@alfresco/adf-content-services';
-import { DropdownCloudWidgetComponent } from './components/widgets/dropdown/dropdown-cloud.widget';
import { GroupCloudWidgetComponent } from './components/widgets/group/group-cloud.widget';
import { PeopleCloudWidgetComponent } from './components/widgets/people/people-cloud.widget';
import { AttachFileCloudWidgetComponent } from './components/widgets/attach-file/attach-file-cloud-widget.component';
@@ -73,7 +72,6 @@ import { FormCloudSpinnerService } from './services/spinner/form-cloud-spinner.s
UploadCloudWidgetComponent,
FormDefinitionSelectorCloudComponent,
FormCustomOutcomesComponent,
- DropdownCloudWidgetComponent,
RadioButtonsCloudWidgetComponent,
AttachFileCloudWidgetComponent,
PeopleCloudWidgetComponent,
@@ -90,7 +88,6 @@ import { FormCloudSpinnerService } from './services/spinner/form-cloud-spinner.s
UploadCloudWidgetComponent,
FormDefinitionSelectorCloudComponent,
FormCustomOutcomesComponent,
- DropdownCloudWidgetComponent,
RadioButtonsCloudWidgetComponent,
AttachFileCloudWidgetComponent,
PeopleCloudWidgetComponent,
diff --git a/lib/process-services/src/lib/form/form.component.ts b/lib/process-services/src/lib/form/form.component.ts
index 6880f7968fd..23414b2ed00 100644
--- a/lib/process-services/src/lib/form/form.component.ts
+++ b/lib/process-services/src/lib/form/form.component.ts
@@ -15,7 +15,19 @@
* limitations under the License.
*/
-import { Component, EventEmitter, Input, Output, ViewEncapsulation, SimpleChanges, OnInit, OnDestroy, OnChanges, inject } from '@angular/core';
+import {
+ Component,
+ EventEmitter,
+ Input,
+ Output,
+ ViewEncapsulation,
+ SimpleChanges,
+ OnInit,
+ OnDestroy,
+ OnChanges,
+ inject,
+ ChangeDetectorRef
+} from '@angular/core';
import {
WidgetVisibilityService,
FormService,
@@ -64,6 +76,7 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnDestro
protected visibilityService = inject(WidgetVisibilityService);
protected ecmModelService = inject(EcmModelService);
protected nodeService = inject(NodesApiService);
+ private cdRef = inject(ChangeDetectorRef);
/** Underlying form model instance. */
@Input()
@@ -131,6 +144,7 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnDestro
this.formService.validateForm.pipe(takeUntil(this.onDestroy$)).subscribe((validateFormEvent) => {
if (validateFormEvent.errorsField.length > 0) {
this.formError.next(validateFormEvent.errorsField);
+ this.cdRef.detectChanges();
}
});
}
diff --git a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.html b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.html
index 42d22640f0e..7728dfe8d73 100644
--- a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.html
+++ b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.html
@@ -1,23 +1,16 @@
-