Skip to content

Commit 12d15f2

Browse files
Garfield-frjma
andcommitted
fix(editor): change props options to obervable
* Fixes tooltip position. Co-Authored-by: Bertrand Zuchuat <[email protected]> Co-Authored-by: Johnny Mariéthoz <[email protected]>
1 parent f68faae commit 12d15f2

File tree

12 files changed

+95
-57
lines changed

12 files changed

+95
-57
lines changed

projects/rero/ng-core/assets/scss/_theme.scss

-5
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ dl.metadata {
5757
@extend .mb-0, .border-none, .bg-white;
5858
}
5959

60-
// bootstrap patches
61-
label {
62-
display: block;
63-
}
64-
6560
legend {
6661
@extend .text-base;
6762
width: auto;

projects/rero/ng-core/src/lib/record/editor/extensions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { UntypedFormControl } from '@angular/forms';
1919
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
2020
import { FormlyExtension, FormlyFieldConfig, FormlyFieldProps } from '@ngx-formly/core';
2121
import { TranslateService } from '@ngx-translate/core';
22-
import { isObservable } from 'rxjs';
22+
import { isObservable, of } from 'rxjs';
2323
import { Validators } from '../../validator/validators';
2424
import { RecordService } from '../record.service';
2525
import { isEmpty, removeEmptyValues } from './utils';
@@ -410,7 +410,7 @@ export class FormOptionsProcessExtension implements FormlyExtension {
410410
prePopulate(field: FormlyFieldConfig<FormlyFieldProps & { [additionalProperties: string]: any; }>): void {
411411
// Process options
412412
if (field.props?.options && !isObservable(field.props?.options)) {
413-
field.props.options = this.processOptions(field.props);
413+
field.props.options = of(this.processOptions(field.props));
414414
}
415415
}
416416

projects/rero/ng-core/src/lib/record/editor/formly/primeng/multi-checkbox/multi-checkbox.spec.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
1919
import { createFieldComponent } from '@ngx-formly/core/testing';
20+
import { of } from 'rxjs';
2021
import { FormFieldWrapperComponent } from '../../../wrappers/form-field-wrapper/form-field-wrapper.component';
2122
import { IMultiCheckBoxProps, NgCoreFormlyMultiCheckboxModule } from './multi-checkbox';
2223

@@ -41,10 +42,10 @@ describe('MultiCheckboxComponent', () => {
4142
type: 'multi-checkbox',
4243
props: {
4344
style: 'stacked',
44-
options: [
45+
options: of([
4546
{ label: 'foo', value: 'foo', untranslatedLabel: 'foo' },
4647
{ label: 'bar', value: 'bar', untranslatedLabel: 'bar' },
47-
],
48+
]),
4849
}
4950
});
5051
expect(queryAll('p-checkbox')).toHaveSize(2);
@@ -57,10 +58,10 @@ describe('MultiCheckboxComponent', () => {
5758
type: 'multi-checkbox',
5859
props: {
5960
style: 'inline',
60-
options: [
61+
options: of([
6162
{ label: 'foo', value: 'foo', untranslatedLabel: 'foo' },
6263
{ label: 'bar', value: 'bar', untranslatedLabel: 'bar' },
63-
],
64+
]),
6465
}
6566
});
6667
expect(queryAll('p-checkbox')).toHaveSize(2);

projects/rero/ng-core/src/lib/record/editor/formly/primeng/multi-checkbox/multi-checkbox.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717
import { CommonModule } from '@angular/common';
18-
import { Component, inject, NgModule, OnDestroy, OnInit } from '@angular/core';
18+
import { ChangeDetectorRef, Component, inject, NgModule, OnDestroy, OnInit } from '@angular/core';
1919
import { FormsModule } from '@angular/forms';
2020
import { FieldType, FormlyFieldConfig, FormlyFieldProps, FormlyModule } from '@ngx-formly/core';
2121
import { FormlySelectModule } from '@ngx-formly/core/select';
2222
import { TranslateModule, TranslateService } from '@ngx-translate/core';
2323
import { TranslateLabelService } from '@rero/ng-core/src/lib/record/editor/formly/primeng/select';
2424
import { CheckboxChangeEvent, CheckboxModule } from 'primeng/checkbox';
25-
import { Subscription } from 'rxjs';
25+
import { map, merge, Observable, Subscription, switchMap } from 'rxjs';
2626

2727
export interface IMultiCheckBoxProps extends FormlyFieldProps {
2828
labelStyleClass?: string;
2929
style: 'stacked' | 'inline';
3030
styleClass?: string;
31-
options?: any[]
31+
options?: Observable<any[]>;
3232
}
3333

3434
@Component({
3535
selector: 'ng-core-multi-checkbox',
3636
template: `
3737
<div class="flex" [ngClass]="{ 'gap-3': props.style === 'inline', 'flex-column gap-1': props.style === 'stacked' }">
38-
@for (option of props.options; track option) {
38+
@for (option of optionValues$|async; track option) {
3939
<div class="flex align-items-center">
4040
<p-checkbox
4141
[disabled]="option.disabled"
@@ -58,6 +58,7 @@ export class MultiCheckboxComponent extends FieldType<FormlyFieldConfig<IMultiCh
5858

5959
private translateService: TranslateService = inject(TranslateService);
6060
private translateLabelService: TranslateLabelService = inject(TranslateLabelService);
61+
private ref: ChangeDetectorRef = inject(ChangeDetectorRef);
6162

6263
private subscription: Subscription = new Subscription();
6364

@@ -68,13 +69,20 @@ export class MultiCheckboxComponent extends FieldType<FormlyFieldConfig<IMultiCh
6869
}
6970
};
7071

72+
optionValues$: Observable<any[]>;
73+
7174
multiCheckBoxValue: string[] = [];
7275

7376
ngOnInit(): void {
74-
this.translateLabelService.translateLabel(this.props.options);
75-
this.subscription.add(this.translateService.onLangChange.subscribe(() => {
76-
this.translateLabelService.translateLabel(this.props.options);
77-
}));
77+
const optionsObs = this.props.options;
78+
const changeObs = this.translateService.onLangChange.pipe(switchMap(() => this.optionValues$));
79+
this.optionValues$ = merge(...[optionsObs, changeObs])
80+
.pipe(
81+
map(options => {
82+
this.ref.markForCheck();
83+
return this.translateLabelService.translateLabel(options);
84+
})
85+
);
7886
}
7987

8088
ngOnDestroy(): void {

projects/rero/ng-core/src/lib/record/editor/formly/primeng/multi-select/multi-select.spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* RERO angular core
3-
* Copyright (C) 2024 RERO
3+
* Copyright (C) 2024-2025 RERO
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU Affero General Public License as published by
@@ -17,6 +17,7 @@
1717
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
1818
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
1919
import { createFieldComponent } from '@ngx-formly/core/testing';
20+
import { of } from 'rxjs';
2021
import { FormFieldWrapperComponent } from '../../../wrappers/form-field-wrapper/form-field-wrapper.component';
2122
import { IMultiSelectProps, NgCoreFormlyMultiSelectModule } from './multi-select';
2223

@@ -46,7 +47,7 @@ describe('MultiSelectComponent', () => {
4647
filter: true,
4748
filterMatchMode: 'contains',
4849
group: false,
49-
options: [],
50+
options: of([]),
5051
panelStyleClass: 'w-full',
5152
required: false,
5253
scrollHeight: '250px',

projects/rero/ng-core/src/lib/record/editor/formly/primeng/multi-select/multi-select.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717
import { CommonModule } from '@angular/common';
18-
import { Component, inject, NgModule, OnDestroy, OnInit, Type } from '@angular/core';
18+
import { ChangeDetectorRef, Component, inject, NgModule, OnDestroy, OnInit, Type } from '@angular/core';
1919
import { ReactiveFormsModule } from '@angular/forms';
2020
import { FieldType, FormlyFieldConfig, FormlyFieldProps, FormlyModule } from '@ngx-formly/core';
2121
import { FormlyFieldSelectProps, FormlySelectModule } from '@ngx-formly/core/select';
2222
import { TranslateModule, TranslateService } from '@ngx-translate/core';
2323
import { TranslateLabelService } from '@rero/ng-core/src/lib/record/editor/formly/primeng/select';
2424
import { MultiSelectModule as PrimeNgMultiSelectModule } from 'primeng/multiselect';
25-
import { Subscription } from 'rxjs';
25+
import { map, merge, Observable, Subscription, switchMap } from 'rxjs';
2626

2727
export interface IMultiSelectProps extends FormlyFieldProps, FormlyFieldSelectProps {
2828
appendTo?: any;
@@ -47,6 +47,7 @@ export interface IMultiSelectProps extends FormlyFieldProps, FormlyFieldSelectPr
4747
tooltipPositionStyle: string;
4848
tooltipStyleClass?: string;
4949
variant: 'outlined' | 'filled';
50+
options?: Observable<any[]>;
5051
}
5152

5253
export interface FormlyMultiSelectFieldConfig extends FormlyFieldConfig<IMultiSelectProps> {
@@ -69,7 +70,7 @@ export interface FormlyMultiSelectFieldConfig extends FormlyFieldConfig<IMultiSe
6970
[formlyAttributes]="field"
7071
[group]="props.group"
7172
[loadingIcon]="props.loadingIcon"
72-
[options]="props.options"
73+
[options]="optionValues$|async"
7374
optionLabel="label"
7475
optionValue="value"
7576
[panelStyleClass]="props.panelStyleClass"
@@ -103,6 +104,7 @@ export class MultiSelectComponent extends FieldType<FormlyFieldConfig<IMultiSele
103104

104105
private translateService: TranslateService = inject(TranslateService);
105106
private translateLabelService: TranslateLabelService = inject(TranslateLabelService);
107+
private ref: ChangeDetectorRef = inject(ChangeDetectorRef);
106108

107109
private subscription: Subscription = new Subscription();
108110

@@ -127,16 +129,23 @@ export class MultiSelectComponent extends FieldType<FormlyFieldConfig<IMultiSele
127129
},
128130
};
129131

132+
optionValues$: Observable<any[]>;
133+
130134
ngOnInit(): void {
131-
this.translateLabelService.translateLabel(this.props.options);
132-
this.subscription.add(this.translateService.onLangChange.subscribe(() => {
133-
this.translateLabelService.translateLabel(this.props.options);
134-
}));
135+
const optionsObs = this.props.options;
136+
const changeObs = this.translateService.onLangChange.pipe(switchMap(() => this.optionValues$));
137+
this.optionValues$ = merge(...[optionsObs, changeObs])
138+
.pipe(
139+
map(options => {
140+
this.ref.markForCheck();
141+
return this.translateLabelService.translateLabel(options);
142+
})
143+
);
135144
}
136145

137146
// Clear all validators except required.
138147
clearValidators() {
139-
const errors = this.formControl.errors;
148+
const {errors} = this.formControl;
140149
this.formControl.setErrors(errors.required ? { required: true } : null);
141150
}
142151

projects/rero/ng-core/src/lib/record/editor/formly/primeng/select/select.spec.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* RERO angular core
3-
* Copyright (C) 2024 RERO
3+
* Copyright (C) 2024-2025 RERO
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU Affero General Public License as published by
@@ -19,6 +19,7 @@ import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
1919
import { createFieldComponent } from '@ngx-formly/core/testing';
2020
import { FormFieldWrapperComponent } from '../../../wrappers/form-field-wrapper/form-field-wrapper.component';
2121
import { ISelectProps, NgCoreFormlySelectModule } from './select';
22+
import { of } from 'rxjs';
2223

2324
const renderComponent = (field: FormlyFieldConfig<ISelectProps>) => {
2425
return createFieldComponent(field, {
@@ -45,10 +46,10 @@ describe('SelectComponent', () => {
4546
filter: false,
4647
filterMatchMode: 'contains',
4748
group: false,
48-
options: [
49+
options: of([
4950
{ label: 'Foo', value: 'foo', untranslatedLabel: 'foo' },
5051
{ label: 'Bar', value: 'bar', untranslatedLabel: 'bar' },
51-
],
52+
]),
5253
panelStyleClass: 'w-full',
5354
required: false,
5455
scrollHeight: '250px',

projects/rero/ng-core/src/lib/record/editor/formly/primeng/select/select.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717
import { CommonModule } from '@angular/common';
18-
import { ChangeDetectionStrategy, Component, inject, NgModule, OnDestroy, OnInit, Type } from '@angular/core';
18+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, NgModule, OnDestroy, OnInit, Type } from '@angular/core';
1919
import { ReactiveFormsModule } from '@angular/forms';
2020
import { FieldType, FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
2121
import { FormlySelectModule as FormlyCoreSelectModule, FormlyFieldSelectProps } from '@ngx-formly/core/select';
2222
import { FormlyFieldProps } from '@ngx-formly/primeng/form-field';
2323
import { TranslateModule, TranslateService } from '@ngx-translate/core';
2424
import { DropdownModule } from 'primeng/dropdown';
25-
import { Subscription } from 'rxjs';
25+
import { map, merge, Observable, Subscription, switchMap, tap } from 'rxjs';
2626
import { TranslateLabelService } from './translate-label.service';
2727

2828
export interface ISelectProps extends FormlyFieldProps, FormlyFieldSelectProps {
@@ -46,6 +46,7 @@ export interface ISelectProps extends FormlyFieldProps, FormlyFieldSelectProps {
4646
tooltipPosition: 'left' | 'top' | 'bottom' | 'right';
4747
tooltipPositionStyle: string;
4848
tooltipStyleClass?: string;
49+
options?: Observable<any[]>;
4950
}
5051

5152
export interface IFormlySelectFieldConfig extends FormlyFieldConfig<ISelectProps> {
@@ -71,7 +72,7 @@ export interface IFormlySelectFieldConfig extends FormlyFieldConfig<ISelectProps
7172
[formlyAttributes]="field"
7273
[group]="props.group"
7374
[loadingIcon]="props.loadingIcon"
74-
[options]="props.options"
75+
[options]="optionValues$|async"
7576
[optionLabel]="props.group ? undefined : 'label'"
7677
[optionValue]="props.group ? undefined : 'value'"
7778
[panelStyleClass]="props.panelStyleClass"
@@ -120,6 +121,7 @@ export class SelectComponent extends FieldType<FormlyFieldConfig<ISelectProps>>
120121

121122
private translateService: TranslateService = inject(TranslateService);
122123
private translateLabelService: TranslateLabelService = inject(TranslateLabelService);
124+
private ref: ChangeDetectorRef = inject(ChangeDetectorRef);
123125

124126
private subscription: Subscription = new Subscription();
125127

@@ -142,19 +144,26 @@ export class SelectComponent extends FieldType<FormlyFieldConfig<ISelectProps>>
142144
}
143145
};
144146

147+
optionValues$: Observable<any[]>;
148+
145149
ngOnInit(): void {
146-
this.translateLabelService.translateLabel(this.props.options);
147-
this.subscription.add(this.translateService.onLangChange.subscribe(() => {
148-
this.translateLabelService.translateLabel(this.props.options);
149-
}));
150+
const optionsObs = this.props.options;
151+
const changeObs = this.translateService.onLangChange.pipe(switchMap(() => this.optionValues$));
152+
this.optionValues$ = merge(...[optionsObs, changeObs])
153+
.pipe(
154+
map(options => {
155+
this.ref.markForCheck();
156+
return this.translateLabelService.translateLabel(options);
157+
})
158+
);
150159
}
151160

152161
ngOnDestroy(): void {
153162
this.subscription.unsubscribe();
154163
}
155164

156165
clearValidators() {
157-
const errors = this.formControl.errors;
166+
const { errors } = this.formControl;
158167
this.formControl.setErrors(errors.required? {required: true}: null);
159168
}
160169
}

projects/rero/ng-core/src/lib/record/editor/formly/primeng/select/translate-label.service.ts

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export class TranslateLabelService {
2626

2727
translateLabel(options: any): any[] {
2828
options.map((option: any) => {
29+
if (!option.untranslatedLabel) {
30+
option.untranslatedLabel = option.label;
31+
}
2932
option.label = this.translateService.instant(option.untranslatedLabel);
3033
if (option.items) {
3134
this.translateLabel(option.items);

projects/rero/ng-core/src/lib/record/editor/formly/primeng/tree-select/tree-select.spec.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* RERO angular core
3-
* Copyright (C) 2024 RERO
3+
* Copyright (C) 2024-2025 RERO
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,7 @@ import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
2020
import { createFieldComponent } from '@ngx-formly/core/testing';
2121
import { FormFieldWrapperComponent } from '../../../wrappers/form-field-wrapper/form-field-wrapper.component';
2222
import { ITreeSelectProps, NgCoreFormlyTreeSelectModule } from './tree-select';
23+
import { of } from 'rxjs';
2324

2425
const renderComponent = (field: FormlyFieldConfig<ITreeSelectProps>) => {
2526
return createFieldComponent(field, {
@@ -45,7 +46,7 @@ describe('TreeSelectComponent', () => {
4546
containerStyleClass: 'w-full',
4647
filter: false,
4748
filterBy: 'label',
48-
options: [
49+
options: of([
4950
{
5051
key: 'label_1',
5152
label: 'Tree 1',
@@ -55,7 +56,7 @@ describe('TreeSelectComponent', () => {
5556
{ key: 'label_1_2', label: 'Subtree 2', data: 'subtree2', untranslatedLabel: 'Subtree 2' }
5657
]
5758
}
58-
],
59+
]),
5960
panelClass: 'w-full',
6061
panelStyleClass: 'w-full',
6162
scrollHeight: '400px',

0 commit comments

Comments
 (0)