Skip to content

Commit e3c3676

Browse files
committed
(feat) : Adapt data sources to new schema specification and add remote-select Question
1 parent 6187fe1 commit e3c3676

File tree

9 files changed

+165
-22
lines changed

9 files changed

+165
-22
lines changed

projects/ngx-formentry/src/components/ngx-remote-select/ngx-remote-select.component.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,22 @@ export class RemoteSelectComponent implements OnInit, ControlValueAccessor {
138138

139139
private loadOptions() {
140140
this.remoteOptions$ = concat(
141-
of([]), // default items
141+
this.dataSource.searchOptions(
142+
'',
143+
this.dataSource?.dataSourceOptions ?? {}
144+
) ?? of([]), // default items
142145
this.remoteOptionInput$.pipe(
143146
distinctUntilChanged(),
144147
tap(() => {
145148
this.loading = true;
146149
}),
147150
switchMap((term) =>
148-
this.dataSource.searchOptions(term).pipe(
149-
catchError(() => of([])), // empty list on error
150-
tap(() => (this.loading = false))
151-
)
151+
this.dataSource
152+
.searchOptions(term, this.dataSource?.dataSourceOptions ?? {})
153+
.pipe(
154+
catchError(() => of([])), // empty list on error
155+
tap(() => (this.loading = false))
156+
)
152157
)
153158
)
154159
);

projects/ngx-formentry/src/form-entry/form-factory/question.factory.ts

+35
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { MinLengthValidationModel } from '../question-models/min-length-validati
3333
import { WorkspaceLauncherQuestion } from '../question-models';
3434
import { DecimalValidationModel } from '../question-models/decimal-validation.model';
3535
import { DisallowDecimalsValidationModel } from '../question-models/disallow-decimals-validation.model';
36+
import { RemoteSelectQuestion } from '../question-models/remote-select-question';
3637
@Injectable()
3738
export class QuestionFactory {
3839
dataSources: any = {};
@@ -745,6 +746,38 @@ export class QuestionFactory {
745746
return question;
746747
}
747748

749+
toRemoteSelectQuestion(schemaQuestion: any): RemoteSelectQuestion {
750+
const dataSource = this.getDataSourceConfig(schemaQuestion);
751+
const question = new RemoteSelectQuestion({
752+
dataSource: dataSource.name,
753+
dataSourceOptions: dataSource.options,
754+
type: '',
755+
key: ''
756+
});
757+
question.questionIndex = this.quetionIndex;
758+
question.label = schemaQuestion.label;
759+
question.prefix = schemaQuestion.prefix;
760+
question.key = schemaQuestion.id;
761+
question.renderingType = 'remote-select';
762+
question.validators = this.addValidators(schemaQuestion);
763+
question.extras = schemaQuestion;
764+
return question;
765+
}
766+
767+
private getDataSourceConfig(
768+
schemaQuestion: any
769+
): { name: string; options: any } {
770+
const dataSourceName = schemaQuestion.questionOptions?.dataSource;
771+
const dataSourceOptions = schemaQuestion.questionOptions?.dataSourceOptions;
772+
// See https://github.com/openmrs/openmrs-contrib-json-schemas/blob/main/form.schema.json
773+
const legacyDataSource = schemaQuestion.questionOptions?.datasource;
774+
775+
return {
776+
name: dataSourceName ?? legacyDataSource?.name ?? '',
777+
options: dataSourceOptions ?? legacyDataSource?.config ?? {}
778+
};
779+
}
780+
748781
toDecimalQuestion(schemaQuestion: any): TextInputQuestion {
749782
const question = new TextInputQuestion({
750783
placeholder: '',
@@ -938,6 +971,8 @@ export class QuestionFactory {
938971
return this.toFileUploadQuestion(schema);
939972
case 'workspace-launcher':
940973
return this.toWorkspaceLauncher(schema);
974+
case 'remote-select':
975+
return this.toRemoteSelectQuestion(schema);
941976
default:
942977
console.warn('New Schema Question Type found.........' + renderType);
943978
return this.toTextQuestion(schema);

projects/ngx-formentry/src/form-entry/question-models/interfaces/data-source.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import { SelectOption } from './select-option';
22
import { Observable } from 'rxjs';
33

44
export interface DataSource {
5-
dataSourceOptions?: any;
5+
dataSourceOptions?: Record<string, unknown>;
66
dataFromSourceChanged?: Observable<SelectOption[]>;
77
resolveSelectedValue(value): Observable<SelectOption>;
8-
searchOptions(searchText): Observable<SelectOption[]>;
8+
searchOptions(
9+
searchText,
10+
dataSourceOptions?: Record<string, unknown>
11+
): Observable<SelectOption[]>;
912
fileUpload(data): Observable<any>;
1013
fetchFile(url: string, fileType?: string): Observable<any>;
1114
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { BaseOptions } from '../interfaces/base-options';
2+
3+
export interface RemoteSelectQuestionOptions extends BaseOptions {
4+
dataSource: string;
5+
dataSourceOptions?: Record<string, unknown>;
6+
}

projects/ngx-formentry/src/form-entry/question-models/question-base.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class QuestionBase implements BaseOptions {
3030
historicalDataValue?: any;
3131
extras?: any;
3232
dataSource?: string;
33-
dataSourceOptions?: any;
33+
dataSourceOptions?: Record<string, any>;
3434

3535
controlType?: AfeControlType;
3636
validators?: Array<ValidationModel>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { QuestionBase } from './question-base';
2+
import { AfeControlType } from '../../abstract-controls-extension/afe-control-type';
3+
import { RemoteSelectQuestionOptions } from './interfaces/remote-select-question-options';
4+
5+
export class RemoteSelectQuestion extends QuestionBase {
6+
rendering: string;
7+
options: any[];
8+
9+
constructor(options: RemoteSelectQuestionOptions) {
10+
super(options);
11+
this.renderingType = 'select';
12+
this.controlType = AfeControlType.AfeFormControl;
13+
this.dataSource = options.dataSource || '';
14+
this.dataSourceOptions = options.dataSourceOptions || {};
15+
}
16+
}

src/app/adult-1.6.json

+45-5
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,23 @@
318318
"rendering": "ui-select-extended"
319319
}
320320
},
321+
{
322+
"id": "admitToLocation",
323+
"type": "obs",
324+
"required": true,
325+
"label": "Admit to location",
326+
"questionOptions": {
327+
"rendering": "remote-select",
328+
"required": true,
329+
"concept": "CIEL:169403",
330+
"datasource": {
331+
"name": "location_datasource",
332+
"config": {
333+
"tag": "Admission Location"
334+
}
335+
}
336+
}
337+
},
321338
{
322339
"type": "encounterLocation",
323340
"label": "Facility name (site/satellite clinic required):",
@@ -3362,7 +3379,9 @@
33623379
"type": "obs",
33633380
"hide": {
33643381
"field": "tb_current",
3365-
"value": ["b8aa06ca-93c6-40ea-b144-c74f841926f4"]
3382+
"value": [
3383+
"b8aa06ca-93c6-40ea-b144-c74f841926f4"
3384+
]
33663385
},
33673386
"id": "__ptxCzFD2s"
33683387
}
@@ -6991,7 +7010,9 @@
69917010
"type": "obs",
69927011
"hide": {
69937012
"field": "q26f",
6994-
"value": ["b8aa06ca-93c6-40ea-b144-c74f841926f4"]
7013+
"value": [
7014+
"b8aa06ca-93c6-40ea-b144-c74f841926f4"
7015+
]
69957016
},
69967017
"id": "__Jywyp94Lw"
69977018
}
@@ -8012,7 +8033,16 @@
80128033
"questionOptions": {
80138034
"concept": "318a5e8b-218c-4f66-9106-cd581dec1f95",
80148035
"rendering": "date",
8015-
"weeksList": [2, 4, 6, 8, 12, 16, 24, 36]
8036+
"weeksList": [
8037+
2,
8038+
4,
8039+
6,
8040+
8,
8041+
12,
8042+
16,
8043+
24,
8044+
36
8045+
]
80168046
},
80178047
"validators": [
80188048
{
@@ -8042,7 +8072,17 @@
80428072
"questionOptions": {
80438073
"concept": "a8a666ba-1350-11df-a1f1-0026b9348838",
80448074
"rendering": "date",
8045-
"weeksList": [2, 4, 6, 8, 12, 16, 20, 24, 36]
8075+
"weeksList": [
8076+
2,
8077+
4,
8078+
6,
8079+
8,
8080+
12,
8081+
16,
8082+
20,
8083+
24,
8084+
36
8085+
]
80468086
},
80478087
"validators": [
80488088
{
@@ -8178,4 +8218,4 @@
81788218
]
81798219
}
81808220
]
8181-
}
8221+
}

src/app/app.component.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ export class AppComponent implements OnInit {
7272
searchOptions: this.sampleSearch,
7373
resolveSelectedValue: this.sampleResolve
7474
});
75+
this.dataSources.registerDataSource('location_datasource', {
76+
searchOptions: this.sampleLocationSearch,
77+
resolveSelectedValue: this.sampleResolve
78+
});
7579
this.dataSources.registerDataSource('provider', {
7680
searchOptions: this.sampleSearch,
7781
resolveSelectedValue: this.sampleResolve
@@ -350,7 +354,32 @@ export class AppComponent implements OnInit {
350354
});
351355
}
352356

353-
public sampleSearch(): Observable<any> {
357+
public sampleLocationSearch(): Observable<Array<Record<string, string>>> {
358+
return of([
359+
{
360+
value: 'ba685651-ed3b-4e63-9b35-78893060758a',
361+
label: 'Inpatient Ward'
362+
},
363+
{
364+
value: '184ac7d9-225a-41f8-bac7-c87b1327e1b0',
365+
label: 'Ward 1'
366+
},
367+
{
368+
value: '5a7f3c53-6bb4-448b-a966-5e65b397b9f3',
369+
label: 'Ward 2'
370+
},
371+
{
372+
value: '2272b8cd-b690-4878-a50c-40d22235b3f3',
373+
label: 'Ward 3'
374+
},
375+
{
376+
value: 'db0253bb-8e2e-4b2c-b60c-6c88110e3c2e',
377+
label: 'Duplix'
378+
}
379+
]);
380+
}
381+
382+
public sampleSearch(searchText: string): Observable<any> {
354383
const items: Array<any> = [
355384
{ value: '0', label: 'Aech' },
356385
{ value: '5b6e58ea-1359-11df-a1f1-0026b9348838', label: 'Art3mis' },

src/app/app.module.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2-
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
2+
import {
3+
provideHttpClient,
4+
withInterceptorsFromDi
5+
} from '@angular/common/http';
36
import { BrowserModule } from '@angular/platform-browser';
47
import { ReactiveFormsModule } from '@angular/forms';
58
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
69
import { FormEntryModule } from '@openmrs/ngx-formentry';
710
import { AppComponent } from './app.component';
811
import { NgxTranslateModule } from './translate/translate.module';
912

10-
@NgModule({ declarations: [AppComponent],
11-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
12-
bootstrap: [AppComponent], imports: [BrowserModule,
13-
BrowserAnimationsModule,
14-
FormEntryModule,
15-
ReactiveFormsModule,
16-
NgxTranslateModule], providers: [provideHttpClient(withInterceptorsFromDi())] })
13+
@NgModule({
14+
declarations: [AppComponent],
15+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
16+
bootstrap: [AppComponent],
17+
imports: [
18+
BrowserModule,
19+
BrowserAnimationsModule,
20+
FormEntryModule,
21+
ReactiveFormsModule,
22+
NgxTranslateModule
23+
],
24+
providers: [provideHttpClient(withInterceptorsFromDi())]
25+
})
1726
export class AppModule {}

0 commit comments

Comments
 (0)