Kam-pickers consist of a number of generic pickers which can be used by other micro frontends when loaded into the shell application “Fælles arbejdsplads”.
Import kam-pickers
Kam-pickers is implemented as a WebComponent and can be imported by the use of WebPack and Module federation.
Example of import
There is several ways to import kam-pickers. The first approach it to add a remote entry in the ModuleFederationPlugin in webpack.config.js. For demonstration purposes the kam-pickers project is running on http://localhost:4001.
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); ... module.exports = { ..., plugins: [ new ModuleFederationPlugin({ name: 'kamUi', remotes: { kamPickers: 'kamPickers@http://localhost:4001/remoteEntry.js', }, shared: share({ '@angular/core': { singleton: true, strictVersion: true, eager: true, requiredVersion: 'auto' }, '@angular/common': { singleton: true, strictVersion: true, eager: true, requiredVersion: 'auto' }, '@angular/common/http': { singleton: true, strictVersion: true, eager: true, requiredVersion: 'auto' }, '@angular/router': { singleton: true, strictVersion: true, eager: true, requiredVersion: 'auto' }, }), }), ], };
If you are using TypeScript in your host application you have to make sure you have the following compiler options specified in tsconfig.json. To enable dynamic module loading.
{ ..., "compilerOptions": { ..., "moduleResolution": "node", "target": "es2017", "module": "es2020" }, ... }
Additionally you have to declare a module to "trick" TypeScript into thinking the module exists. You do this by adding a kamPickers.d.ts file in the root of the project with the following content.
declare module 'kamPickers/WebComponent';
Now you can import a remote module as a JavaScript Promise and use WebComponents like the following example in Angular:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'kam-information', templateUrl: './information.component.html', styleUrls: ['./information.component.scss'], }) export class InformationComponent { webcomponent$ = from(import('kamPickers/WebComponent')); isLoaded$ = this.webcomponent$.pipe( startWith(false), map((res) => !!res) ); constructor() {} openOrganizationPicker() { window.dispatchEvent( new CustomEvent('openOrganizationPicker', { detail: { callerId: '1', selected: [ '139636' ], }, }) ); } }
information.component.html
<button [disabled]="(isLoaded$ | async) === false" mat-raised-button color="accent" (click)="openOrganizationPicker()"> Open organization picker </button> <ng-container *ngIf="webcomponent$ | async"> <kam-pickers></kam-pickers> </ng-container>
The above code snippets also showcases how inputs which interacts with the web component are disabled while the web component loads. The tag <kam-pickers> is custom tag for the WebComponent and needs to be added before it is possible to trigger the pickers by custom events.
Dynamic import
When using Angular as frontend framework a library called @angular-architects/module-federation and @angular-architects/module-federation-tools can be used instead of doing the ModuleFederation setup yourself. By using angular-architects solution it is possible to load the module in code without specifying the remote in webpack.config.js. As shown below it is possible to call loadRemoteModule() instead.
information.component.ts
... webcomponent$ = from( loadRemoteModule({ remoteEntry: 'http://localhost:4001/remoteEntry.js', exposedModule: './WebComponent', remoteName: 'kamPickers', }) ); ... }
Description of Kam-pickers
Below is a technical guide for each of the pickers implemented by Kam-pickers.
ValueSet picker
The valueSet picker can be used to select the following valueSets:
ObservationCodes
Conditions
To open the valueSet picker, sent a custom event with name “openValueSetPicker”.
Example:
window.dispatchEvent( new CustomEvent('openValueSetPicker', { detail: { callerId: 'useContextCondition', readonlyMode: false, multiSelect: true, language: 'da', designationUseCode: 'consumer', valueSetType: 'Conditions', codeableConcepts: this.form.value.useContext, }, }) );
Input data:
interface ValueSetPickerData { callerId: string; readonlyMode: boolean; multiSelect: boolean; language: string; designationUseCode: string; valueSetType: ValueSetType; selectedValues: CodeableConcept[]; } enum ValueSetType { ObservationCodes = 'ObservationCodes', Conditions = 'Conditions', }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened as the same picker can be used multiple times
readonlyMode: true for readonly mode
multiSelect: true if it should be possible to select more than one
language: language code. eg. "da" for danish
designationUseCode: display text targeting specific audience
values:
"consumer"
description: Designation for use in display to non-clinicians and those not in healthcare professions as a more friendly term for coommunication.
valueSetType:
values:
"ObservationCodes"
"Conditions"
selectedValues: Which CodeSystem should be preselected when the valueSet picker is opened.
Supported CodeableConcept:
Only one coding per CodeableConcept
Output data:
interface ValueSetPickerDialogResult { callerId: string; codeableConcepts: CodeableConcept[]; }
Explanation:
callerId: An unique id that will be returned back with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened from as the same picker can be used multiple times
codeableConcepts: List of all the selected codes.
Always only one code per CodeableConcept
Organization picker
The organization picker can be used to select organizations from STS-ORG or SOR.
To open the organization picker, sent a custom event with name “openOrganizationPicker”.
Example:
window.dispatchEvent( new CustomEvent('openOrganizationPicker', { detail: { callerId: 'ownerField', onlyAllowRootSelection: false, selectedIds: ['139636'], }, }) );
Input data:
interface OrganizationPickerData { callerId: string; onlyAllowRootSelection: boolean; selectedIds: string[]; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened from as the same picker can be used multiple times
onlyAllowRootSelection: If true only organization on top level i selectable. At the moment top level is: Regions and municipalities. Be aware that the top level for a municipality is the first organization from the top of type “OrganisationsEnhed” (system:
http://ehealth.sundhed.dk/cs/oio-organization-type
, code:OrganisationEnhed
selectedIds: Which organization should be preselected when the organization picker is opened.
Output data:
interface OrganizationPickerDialogResult { callerId: string; selected: Organization[]; } interface Organization { id: string; name: string; source: Source | null; alias: string[]; parentReferenceId: string | null; cvrNumber: string | null; referenceUrl: string; }
Explanation:
OrganizationPickerDialogResult
callerId: an unique id that will be returned back with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened from as the same picker can be used multiple times
selected: List of all the selected organizations
Organization
id: organization id
name: organization name
source: Possible values: 'SOR', 'STS-ORG', 'manual'
alias: A hierarchy from the root organization down to the specific organization
parentReferenceId: id of the parent organization
cvrNumber: cvr number
referenceUrl: URL to the specific organization
Questionnaire picker
The questionnaire picker can be used to select questionnaire. To open the questionnaire picker, sent a custom event with name “openQuestionnairePicker”.
Example:
window.dispatchEvent( new CustomEvent('openQuestionnairePicker', { detail: { callerId: 'questionnaireField', selectedIds: ['23'], multiSelect: true, }, }) );
Input data:
interface QuestionnairePickerData { callerId: string; selectedIds: string[]; multiSelect: boolean; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened as the same picker can be used multiple times
selectedIds: Which organization should be preselected when the organization picker is opened.
multiSelect: true if it must be possible to select more than one questionnaire
Output data:
interface QuestionnairePickerDialogResult { callerId: string; selected: Questionnaire[]; }
Explanation:
callerId: an unique id that will be returned back with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened from as the same picker can be used multiple times
selected: List of all the selected questionnaire
Timing picker
The timing picker can be used to setup timing expressions. To open the timing picker, sent a custom event with name “openTimingPicker”.
Example:
window.dispatchEvent( new CustomEvent('openTimingPicker', { detail: { timingPickerData: { callerId: 'timingField', timing: timingObject, }, }, }) );
Input data:
interface TimingPickerData { callerId: string; timing: Timing; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened as the same picker can be used multiple times
timing: fhir timing object.
Output data:
interface TimingPickerData { callerId: string; timing: Timing; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened as the same picker can be used multiple times
timing: fhir timing object
Reference range picker
The reference range picker can be used to setup thresholds for measurement. To open the reference range picker, sent a custom event with name “openReferenceRangePicker”.
Example:
window.dispatchEvent( new CustomEvent('openReferenceRangePicker', { detail: { callerId: 'ReferenceRangeField', readonlyMode: false, measurementType: this.form.code.value, referenceRangeData: this.ReferenceRangeExtensions }, }) );
Input data:
interface ReferenceRangePickerData { callerId: string; readonlyMode: boolean; measurementType: CodeableConcept; referenceRangeData: Extension[] | undefined; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened with as the same picker can be used multiple times.
readonlyMode: true for readonly mode.
measurementType: ValueSet: observation codes
Supported CodeableConcept:
Only one coding per CodeableConcept
referenceRangeData: Extensions of type reference range
Output data:
export interface ReferenceRangePickerDialogResult { callerId: string; referenceRangeData: Extension[]; }
Explanation:
callerId: an unique id that will be returned with the output result. CallerId makes it possible for the caller to distingues which form field the pickers was opened as the same picker can be used multiple times
referenceRangeData: Extensions of type reference range