diff --git a/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.en.md b/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.en.md index cc51f784f63..02be19e9d56 100644 --- a/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.en.md +++ b/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.en.md @@ -48,8 +48,8 @@ The municipality of Sogndal wishes to collect information on the newcomers emplo ### In Local Development environment 1. Create the directory `App/options` if it does not exist. -2. The municipality of Sogndal has created a [static code list](/altinn-studio/guides/development/options/static-codelists) for **industries**: [industry.json](../industry.json). Download the file and place it in `App/options`. -3. Set up the values in the code list for **Years in work force** as an [open dynamic code list](/altinn-studio/guides/development/options/dynamic-codelists) in `App/options` (follow the directions in the documentation). +2. The municipality of Sogndal has created a [static code list](/altinn-studio/guides/development/options/sources/static) for **industries**: [industry.json](../industry.json). Download the file and place it in `App/options`. +3. Set up the values in the code list for **Years in work force** as an [open dynamic code list](/altinn-studio/guides/development/options/sources/dynamic) in `App/options` (follow the directions in the documentation). Options: Label | Data value --------------|---------- @@ -62,8 +62,8 @@ The municipality of Sogndal wishes to collect information on the newcomers emplo ### Useful documentation -- [Static code lists](/altinn-studio/guides/development/options/static-codelists) -- [Dynamic code lists](/altinn-studio/guides/development/options/dynamic-codelists) +- [Static code lists](/altinn-studio/guides/development/options/sources/static) +- [Dynamic code lists](/altinn-studio/guides/development/options/sources/dynamic) ### Knowledge check {{% expandsmall id="m4t1q1" header="What is the difference between static and dynamic options?" %}} @@ -94,12 +94,12 @@ We want the user to be presented with a different set of options for the industr ### Tasks -1. [Send a dynamic query parameter](/altinn-studio/guides/development/options/#pass-query-parameters-when-fetching-options) with the Industry component based on the Sector. +1. [Send a dynamic query parameter](/altinn-studio/guides/development/options/sources/dynamic#query-parameters) with the Industry component based on the Sector. 2. Create a dynamic code list for _Industry_ with logic based on the value of the query parameter (hint: you can read the industry list from the JSON file). ### Useful documentation -- [How to pass query parameters when fetching options](/altinn-studio/guides/development/options/#pass-query-parameters-when-fetching-options) -- [How to configure dynamic code lists](/altinn-studio/guides/development/options/dynamic-codelists) +- [How to pass query parameters when fetching options](/altinn-studio/guides/development/options/sources/dynamic/#query-parameters) +- [How to configure dynamic code lists](/altinn-studio/guides/development/options/sources/dynamic) ### Knowledge check {{% expandsmall id="m4t2q1" header="If a code list is configured with a mapping to the data model, what happens when the relevant field changes value?" %}} diff --git a/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.nb.md b/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.nb.md index e83df7cf94c..9b19b834083 100644 --- a/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.nb.md +++ b/content/altinn-studio/getting-started/app-dev-course-old/modul4/_index.nb.md @@ -47,8 +47,8 @@ Sogndal kommune ønsker å samle inn opplysninger om tilflytterens arbeidsituasj ### I lokalt utviklingsmiljø 1. Opprett mappen `App/options` hvis den ikke eksisterer. -2. Sogndal kommune har opprettet en [statisk kodeliste](/nb/altinn-studio/guides/development/options/static-codelists) for **bransjer**: [industry.json](../industry.json). Last ned filen og plasser den i `App/options`. -3. Sett opp alternativene for **År i arbeidslivet** som en [åpen dynamisk kodeliste](/nb/altinn-studio/guides/development/options/dynamic-codelists#åpne-dynamiske-kodelister) i mappen `App/options` (følg anvisning i dokumentasjonen). +2. Sogndal kommune har opprettet en [statisk kodeliste](/nb/altinn-studio/guides/development/options/sources/static) for **bransjer**: [industry.json](../industry.json). Last ned filen og plasser den i `App/options`. +3. Sett opp alternativene for **År i arbeidslivet** som en [åpen dynamisk kodeliste](/nb/altinn-studio/guides/development/options/sources/dynamic#åpne-kodelister) i mappen `App/options` (følg anvisning i dokumentasjonen). Svaralternativer: Label | Dataverdi -----------|---------- @@ -60,8 +60,8 @@ Sogndal kommune ønsker å samle inn opplysninger om tilflytterens arbeidsituasj ### Nyttig dokumentasjon -- [Statiske kodelister](/nb/altinn-studio/guides/development/options/static-codelists) -- [Dynamiske kodelister](/nb/altinn-studio/guides/development/options/dynamic-codelists) +- [Statiske kodelister](/nb/altinn-studio/guides/development/options/sources/static) +- [Dynamiske kodelister](/nb/altinn-studio/guides/development/options/sources/dynamic) ### Forståelsessjekk {{% expandsmall id="m4t1q1" header="Hva er forskjellen på statiske og dynamiske svaralternativer?" %}} @@ -95,12 +95,12 @@ basert på hvilken sektor de har krysset av for. ### Oppgaver -1. [Send en dynamisk query-parameter](/nb/altinn-studio/guides/development/options/#sende-med-spørringsparametere-ved-henting-av-en-kodeliste) med Bransje-komponenten basert på Sektor. +1. [Send en dynamisk query-parameter](/nb/altinn-studio/guides/development/options/sources/dynamic#spørringsparametre) med Bransje-komponenten basert på Sektor. 2. Lag en dynamisk kodeliste for _Bransje_ med logikk basert på verdien til query-parameteren (hint: du kan lese inn bransjelisten fra json-filen). ### Nyttig dokumentasjon -- [Hvordan sende med spørringsparametre ved henting av kodelister](/nb/altinn-studio/guides/development/options/#sende-med-spørringsparametere-ved-henting-av-en-kodeliste) -- [Hvordan sette opp dynamiske kodelister](/nb/altinn-studio/guides/development/options/dynamic-codelists) +- [Hvordan sende med spørringsparametre ved henting av kodelister](/nb/altinn-studio/guides/development/options/sources/dynamic#spørringsparametre) +- [Hvordan sette opp dynamiske kodelister](/nb/altinn-studio/guides/development/options/sources/dynamic) ### Forståelsessjekk {{% expandsmall id="m4t2q1" header="Om en kodeliste er satt opp med en mapping mot datamodellen, hva skjer når det aktuelle feltet endrer verdi?" %}} diff --git a/content/altinn-studio/guides/development/options/_index.en.md b/content/altinn-studio/guides/development/options/_index.en.md index b574a6457ee..ef782fb7540 100644 --- a/content/altinn-studio/guides/development/options/_index.en.md +++ b/content/altinn-studio/guides/development/options/_index.en.md @@ -1,6 +1,6 @@ --- -title: Code lists (options) -linktitle: Code lists +title: Options (code lists) +linktitle: Options description: How to configure Options / Code lists for an app? toc: true weight: 40 @@ -9,255 +9,20 @@ aliases: - /altinn-studio/reference/data/options --- -Altinn offers two different ways an application can use code lists - static and dynamic. Both is primarily exposed -through the options api from application, and are available at `{org}/{app}/api/options/{optionsId}`. -Checkbox, Dropdown, and RadioButton components will automatically be able to fetch such lists if you connect the -component to the option id in question. Not all dynamic code list have to be fetched from the options api - we can also -have code lists based on the values from a repeating structure in the datamodel. +Several of the form components in Altinn 3 use options. The concept referred to as options is sometimes also called _code lists_. -## Connect the component to options (code list) +The following components support options: -This is done by adding the optionId you would like to refer to either through the component UI in Designer or directly -in `FormLayout.json` as shown below: +| Component | Type | Use case | +|-------------------------------------------------------------------------|-----------------------|----------------------------------------------------------------------------------------------------| +| [Dropdown](../../../reference/ux/components/dropdown) | Single choice | Used to select a single option from a dropdown list | +| [RadioButtons](../../../reference/ux/components/radiobuttons) | Single choice | Used to select a single option from a list of radio buttons | +| [List](../../../reference/ux/components/listcomponent) | Single choice | Used to select a single option from a list/table (with one radio button per row) | +| [Likert](../../../reference/ux/components/likert) | Single choice per row | Used to select a single option per row in a table, displayed as a scale. Commonly used in surveys. | +| [Checkboxes](../../../reference/ux/components/checkboxes) | Multiple choice | Used to select one or more options from a list of checkboxes | +| [MultipleSelect](../../../reference/ux/components/multipleselect) | Multiple choice | Used to select one or more options from a dropdown list | +| [FileUploadWithTag](../../../reference/ux/components/fileuploadwithtag) | Single choice | Used to upload a file and tag it with an option | -```json -{ - "id": "some-dropdown-component", - "type": "Dropdown", - "textResourceBindings": {}, - "dataModelBindings": {}, - "optionsId": "countries" -} -``` - -### Save label value in the datamodel -Sometimes you might wish to save the displayed value in the users chosen language to simplify creation of simple presentations of data without addtional lookups or a requirement to remember the label the user actually picked in case it have changed over time. - -This is performed by having a separate ``dataModelBindings`` with the key ``"label":`` in addition to ``"simpleBinding":`` - -```json -{ - "id": "dropdown-component", - "type": "Dropdown", - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype", - "label":"soknad.nyGaranti.loyvetypeLabel" - }, - "optionsId": "biler" -} -``` - - -## Pass query parameters when fetching options - -Options supports query parameters when making the api call, the parameter `language` is added automatically. - -### Pass static query parameters - -You can add static query parameters by setting the `queryParameters` property on the component: - -```json -{ - "id": "some-dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype" - }, - "required": true, - "optionsId": "loyvetyper", - "queryParameters": { - "loyvetype": "garanti" - } -}, -``` - -In the example above the parameter `?loyvetype=garanti` will be added to the request. - -### Pass dynamic query parameters based on the data model - -You can add dynamic parameters by setting the `mapping` property on the component: - -```json -{ - "id": "some-dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype" - }, - "required": true, - "optionsId": "loyvetyper", - "mapping": { - "soknad.transportorOrgnummer": "orgnummer" - } -}, -``` - -In the example above, the query parameter `orgnummer={nr}`, where `{nr}` is the value of `soknad.transportorOrgnummer` -will be set. -If an option is setup with mapping and the given data field changes app-frontend will refetch the option. This can be -used to dynamically decide which choices are available based on information given by the end user. - -Passing query parameters from repeating groups is also supported by adding an index indicator for the relevant indexes. -Example for a group: - -```json -{ - "id": "dropdown-group", - "type": "Dropdown", - "textResourceBindings": { - "title": "Select city" - }, - "dataModelBindings": { - "simpleBinding": "Group.City" - }, - "required": true, - "optionsId": "cities", - "mapping": { - "Group[{0}].Country": "country" - } -}, -``` - -For nested groups follows the same pattern but with an additional index indicator for the nested group: - -```json -{ - "id": "dropdown-nested-group", - "type": "Dropdown", - "textResourceBindings": { - "title": "Select city" - }, - "dataModelBindings": { - "simpleBinding": "Group.SubGroup.City" - }, - "required": true, - "optionsId": "cities", - "mapping": { - "Group[{0}].SubGroup[{1}].Country": "country" - } -}, -``` - -For a complete example of how this is setup see our [demo app.](https://altinn.studio/repos/ttd/dynamic-options-rep) - -{{%notice warning%}} - -**Applies to applications using version 7.4.0 or older of the nuget packages** - https://github.com/Altinn/app-lib-dotnet/release - -
- -During PDF-generation the app will try to call the same option endpoint as app-frontend does. -We currently have a weakness where mapping parameters not are included in this request, see issue [#7903.](https://github.com/Altinn/altinn-studio/issues/7903) - -A possible workaround here is to return an empty array when the PDF-generator asks for options with empty query params, example: - -```c# -string someArg = keyValuePairs.GetValueOrDefault("someArg"); -string someOtherArg = keyValuePairs.GetValueOrDefault("someOtherArg"); - -if (string.IsNullOrEmpty(someArg) || string.IsNullOrEmpty(someOtherArg)) { - return await Task.FromResult(new List()); -} -``` - -Notice that this wil result in the option value and not the label being present as the end users answer. -{{% /notice%}} - -### Store metadata for the parameters used to retrieve options in tha datamodel - -You can store metadata for the parameters used to retrieve options in the datamodel by setting the `metadata` property -on the components `dataModelBinding` property: - -```json -{ - "id": "some-dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype", - "metadata": "soknad.transportorOrgnummer" - }, - "required": true, - "optionsId": "loyvetyper", - "mapping": { - "soknad.transportorOrgnummer": "orgnummer" - } -}, -``` - -This configuration will store the metadata of the retrieved options as a comma separated string in the -field `soknad.transportorOrgnummer` in the datamodel. - -## Description and HelpText - -`description` and `helpText` is supported by options in apps that use version v7.8.0 or higher. `description` and -`helpText` can be displayed by the components `RadioButtons` and `Checkboxes` by providing the option list with the -mentioned properties. - -Descriptions and HelpTexts can be provided to `options` in the same way that a `label` is provided, in either static or -dynamic code lists. One can also use them in options based on repeating groups in the `source` attribute. - -```json -[ - { - "value": "norway", - "label": "Norge", - "description": "This is a description", - "helpText": "This is a help text" - }, - { - "value": "denmark", - "label": "Danmark" - } -] -``` - -```cs -var options = new AppOptions -{ - Options = new List - { - new AppOption - { - Label = "Ole", - Value = "1", - Description = "This is a description", - HelpText = "This is a help text" - }, - new AppOption - { - Label = "Dole", - Value = "2" - } - } -}; -``` - -Descriptions and help texts used in options based on repeating groups can be set up with dynamic text-resources in the -same way as labels, described in -[options based on repeating groups](repeating-group-codelists). - -```json -{ - "id": "checkboxes-component-id", - "type": "Checkboxes", - ... - "source": { - "group": "some.group", - "label": "checkboxes.label", - "description": "checkboxes.descripiton", - "helpText": "checkboxes.helpText", - "value": "some.group[{0}].someField" - } -}, -``` +In the categories below, you can learn more about how to produce a list of options, configure that option list to be used in a component, as well as common functionality supported across these components. {{}} diff --git a/content/altinn-studio/guides/development/options/_index.nb.md b/content/altinn-studio/guides/development/options/_index.nb.md index bfc8d7834e9..91ba0f4a324 100644 --- a/content/altinn-studio/guides/development/options/_index.nb.md +++ b/content/altinn-studio/guides/development/options/_index.nb.md @@ -1,7 +1,7 @@ --- -title: Kodelister (svaralternativer) -linktitle: Kodelister -description: Hvordan konfigurere svaralternativer/kodelister for en app? +title: Svaralternativer +linktitle: Svaralternativer +description: Hvordan konfigurere svaralternativer for en app toc: true weight: 40 aliases: @@ -9,247 +9,20 @@ aliases: - /nb/altinn-studio/reference/data/options --- -Altinn tilbyr to ulike måter en app kan eksponere kodelister på: Statisk og dynamisk. Disse eksponeres primært fra endepunktet som er tilgjengelig på `{org}/{app}/api/options/{optionsId}`, hvor `optionsId` er ID-en til listen. -Komponenter som avkrysningsbokser, radioknapper og nedtrekkslister vil automatisk kunne hente ut en slik liste om man kobler dem til en kodeliste-ID. Men ikke alle dynamiske kodelister må gå via API'et – vi har også dynamiske kodelister som baserer seg på verdiene fra en repeterende struktur i datamodellen. +Flere av skjemakomponentene i Altinn 3 bruker svaralternativer. Konseptet omtalt som svaralternativer kalles av og til også for _kodelister_ (eller _code lists_ og _options_ på engelsk). -## Koble en komponent til en kodeliste +Følgende komponenter støtter svaralternativer: -En komponent kobles til en kodeliste ved å legge til feltet `optionsId`, som refererer til kodelistens ID. Eksempel: +| Komponent | Type | Bruksområde | +|-------------------------------------------------------------------------|------------------|---------------------------------------------------------------------------------------------------------| +| [Dropdown](../../../reference/ux/components/dropdown) | Ett valg | Brukes for å velge ett alternativ fra en nedtrekksliste | +| [RadioButtons](../../../reference/ux/components/radiobuttons) | Ett valg | Brukes for å velge ett alternativ fra en liste med radioknapper | +| [List](../../../reference/ux/components/listcomponent) | Ett valg | Brukes for å velge ett alternativ fra en liste/tabell (med en radioknapp per rad) | +| [Likert](../../../reference/ux/components/likert) | Ett valg per rad | Brukes for å velge ett alternativ per rad i en tabell, vist som en skala. Vanlig i spørreundersøkelser. | +| [Checkboxes](../../../reference/ux/components/checkboxes) | Flere valg | Brukes for å velge ett eller flere alternativer fra en liste med avkrysningsbokser | +| [MultipleSelect](../../../reference/ux/components/multipleselect) | Flere valg | Brukes for å velge ett eller flere alternativer fra en nedtrekksliste | +| [FileUploadWithTag](../../../reference/ux/components/fileuploadwithtag) | Ett valg | Brukes for å laste opp en fil og knytte den til en 'tag'/merkelapp | -```json -{ - "id": "dropdown-komponent", - "type": "Dropdown", - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype" - }, - "optionsId": "biler" -} -``` - -### Lagre visningsverdi i datamodellen -Noen ganger ønsker man å lagre den viste verdien på brukerens språk i datamodellen for enklere å kunne bruke de lagrede dataene til å lagre enkle visninger uten å være avhengig av å gjøre et nytt oppslag for å få en visningsvennlig verdi. Det kan også brukes for å huske hva brukeren faktisk har sett når han valgte i tilfelle man endrer ordlyd for en verdi og vil ha logg for hva brukeren har sett. - -Dette gjøres ved å ha en egen ``dataModelBindings`` med navnet ``"label":`` i tillegg til en ``"simpleBinding":``. - -```json -{ - "id": "dropdown-komponent", - "type": "Dropdown", - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype", - "label":"soknad.nyGaranti.loyvetypeLabel" - }, - "optionsId": "biler" -} -``` - -## Sende med spørringsparametere ved henting av en kodeliste - -Kodelisteendepunktet støtter spørringsparametre. Parameteren `language` sendes med automatisk. - -### Sende med statiske parametre - -Man kan legge til statiske parametre ved å sette opp `queryParameters` på den aktuelle komponenten: - -```json -{ - "id": "dropdown-komponent", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype" - }, - "required": true, - "optionsId": "loyvetyper", - "queryParameters": { - "loyvetype": "garanti" - } -} -``` - -I eksempelet over vil parameteren `?loyvetype=garanti` bli sendt med i kallet. - -### Sende med dynamiske parametre basert på datamodellen - -Man kan legge til dynamiske parametre ved å sette opp `mapping` på den aktuelle komponenten: - -```json -{ - "id": "dropdown-komponent", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype" - }, - "required": true, - "optionsId": "loyvetyper", - "mapping": { - "soknad.transportorOrgnummer": "orgnummer" - } -} -``` - -I eksempelet over vil parameteren `orgnummer={nr}` bli sendt med. `{nr}` er verdien på feltet `soknad.transportorOrgnummer`. -Om man setter opp en kobling til et datafelt og dette feltet endrer seg, så vil appen hente kodelisten på nytt. På denne måten kan man dynamisk styre hvilke valg som vises basert på informasjon gitt av sluttbruker. - -Å sende med parametere fra repeterende grupper gjøres ved å legge ved en indeks-indikator for de relevante gruppene. Eksempel: - -```json -{ - "id": "dropdown-group", - "type": "Dropdown", - "textResourceBindings": { - "title": "Select city" - }, - "dataModelBindings": { - "simpleBinding": "Group.City" - }, - "required": true, - "optionsId": "cities", - "mapping": { - "Group[{0}].Country": "country" - } -} -``` - -For nøstede repeterende grupper vil man følge det samme mønsteret, men med en ekstra indikator for den nøstede gruppa: - -```json -{ - "id": "dropdown-nested-group", - "type": "Dropdown", - "textResourceBindings": { - "title": "Select city" - }, - "dataModelBindings": { - "simpleBinding": "Group.SubGroup.City" - }, - "required": true, - "optionsId": "cities", - "mapping": { - "Group[{0}].SubGroup[{1}].Country": "country" - } -} -``` - -For et komplett eksempel kan du se vår [demo app.](https://altinn.studio/repos/ttd/dynamic-options-rep) - -{{%notice warning%}} -**Gjelder applikasjoner som benytter versjon 7.4.0 eller eldre av nuget pakkene** - se https://github.com/Altinn/app-lib-dotnet/release - -
- -Under PDF-generering vil appen prøve å kalle det samme options-endepunktet som app-frontend gjør. -Vi har foreløpig en svakhet ved at eventuelle `mapping`-parametere ikke blir inkludert i denne forespørselen, se [sak #7903](https://github.com/Altinn/altinn-studio/issues/7903). - -En mulig workaround her er å returnere en tom array i det PDF-generatoren spør om options med tomme query-parametere, eksempel: - -```c# -string someArg = keyValuePairs.GetValueOrDefault("someArg"); -string someOtherArg = keyValuePairs.GetValueOrDefault("someOtherArg"); - -if (string.IsNullOrEmpty(someArg) || string.IsNullOrEmpty(someOtherArg)) { - return await Task.FromResult(new List()); -} -``` - -Merk at dette vil resultere i at PDF-filen vil vise kodeverdien og ikke visningsverdien som sluttbrukers svar. -{{% /notice%}} - -### Lagre metadata for parametrene som ble brukt til å hente options - -Du kan lagre metadata for parameterene som ble brukt til å hente kodeliste i datamodellen ved å sette egenskapen `metadata` -på komponentens `dataModelBinding`-egenskap: - -```json -{ - "id": "some-dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "NyGarantiLoyvetype" - }, - "dataModelBindings": { - "simpleBinding": "soknad.nyGaranti.loyvetype", - "metadata": "soknad.transportorOrgnummer" - }, - "required": true, - "optionsId": "loyvetyper", - "mapping": { - "soknad.transportorOrgnummer": "orgnummer" - } -} -``` - -Denne konfigurasjonen vil lagre metadata for parameterene som ble brukt til å hente kodelisten som en kommaseparert -streng i feltet `soknad.transportorOrgnummer` i datamodellen. - -## Beskrivelse og hjelpetekst - -`description` og `helpText` støttes av kodelister i apper som bruker versjon 7.8.0 eller høyere. Beskrivelse og -hjelpetekst kan vises av komponentene `RadioButtons` og `Checkboxes` ved å sette attributtene i en `option` som -brukes av komponenten. - -Beskrivelser og hjelpetekster kan gis til `options` på samme måte som en `label` er gitt, enten i statiske eller -dynamiske kodelister. Man kan også bruke dem i kodelister basert på repeterende grupper i `source`-attributten. - -```json -[ - { - "value": "norway", - "label": "Norge", - "description": "This is a description", - "helpText": "This is a help text" - }, - { - "value": "denmark", - "label": "Danmark" - } -] -``` - -```cs -var options = new AppOptions -{ - Options = new List - { - new AppOption - { - Label = "Ole", - Value = "1", - Description = "This is a description", - HelpText = "This is a help text" - }, - new AppOption - { - Label = "Dole", - Value = "2" - } - } -}; -``` - -Beskrivelser og hjelpetekster som brukes i kodelister basert på repeterende grupper kan settes opp med dynamiske -tekstressurser på samme måte som `label`, som beskrevet i -[kodelister fra repeterende grupper](repeating-group-codelists). - -```json -{ - "id": "checkboxes-component-id", - "type": "Checkboxes", - ... - "source": { - "group": "some.group", - "label": "checkboxes.label", - "description": "checkboxes.descripiton", - "helpText": "checkboxes.helpText", - "value": "some.group[{0}].someField" - } -} -``` +I kategoriene under kan du lære mer om hvordan du produserer en liste med svaralternativer, kobler den til en komponent, samt om felles funkjsonalitet som kan brukes på tvers av disse komponentene. {{}} diff --git a/content/altinn-studio/guides/development/options/dynamic-codelists/_index.en.md b/content/altinn-studio/guides/development/options/dynamic-codelists/_index.en.md deleted file mode 100644 index f3e25663d8b..00000000000 --- a/content/altinn-studio/guides/development/options/dynamic-codelists/_index.en.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Dynamic code lists generated runtime -linktitle: Dynamic code lists -description: How to create dynamic code lists created during runtime execution of the application? -toc: false -weight: 100 ---- - -As an alternative to the static files you can have code that determines what the lists should be during runtime. This makes it possible to expose dynamic values that for instance are filtered or looked up in external sources. Dynamic code lists can either be open and accessible to all or secured and limited to those with read access to the instance. - -In versions prior to 4.24.0 this was done by overriding the `GetOptions` method in `App.cs`. This method is now deprecated and is replaced by putting the option code in separate classes implementing an interface and registering the implementation in the application dependency injection container. This allows for better separation, inject dependencies into the constructor, pass in language and other query parameters and generally handle all aspects of the implementation as you see fit. - -For code lists that are open you implement the `IAppOptionsProvider` interface and for code lists that should be secured you implement the `IInstanceAppOptionsProvider` interface. The pattern is the same for both, and the models returned is the same, but the implementation is kept separate to avoid exposing data that should be secured. - -### Open dynamic code lists - -Below you find an example of how to implement a open custom options provider. The url will will still be exposed from the same endpoint as before `{org}/{app}/api/options/countires`. - -```C# -using Altinn.App.Common.Models; -using Altinn.App.PlatformServices.Options; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Altinn.App.Core -{ - public class CountryAppOptionsProvider : IAppOptionsProvider - { - public string Id { get; set; } = "countries"; - - public Task GetAppOptionsAsync(string language, Dictionary keyValuePairs) - { - var options = new AppOptions - { - Options = new List - { - new AppOption - { - Label = "Norway", - Value = "47" - }, - new AppOption - { - Label = "Sweden", - Value = "46" - } - } - }; - - return Task.FromResult(options); - } - } -} - -``` - -For your implementation to be picked up you need to add the following line in your `Startup.cs` (or `Program.cs` in .NET 6): - -```csharp -services.AddTransient(); -``` - -Note that you can have multiple registrations of this interface. The correct implementation is resolved by finding the one with the correct id. - -The interface has a property `Id`, which should be set to the optionId, and a method `GetAppOptionsAsync` for resolving the options. This method accepts a language code and a dictionary of key/value pairs. Both parameters will typically be query parameters picked up from the controller and passed in. Although language could be put in the dictionary as well it's decided to be explicit on this particular parameter. - -> Language codes should be based on ISO 639-1 or the W3C IANA Language Subtag Registry. The latter is built upon the ISO 639-1 standard but is guaranties uniques of the codes, where as ISO 639-1 have conflicting usage for some codes. -> - -### Secured dynamic options - -{{%notice warning%}} - -**NOTICE:** to use this functionality the app must use version >= 4.27.0 of the nuget packages `Altinn.App.PlatformServices`, `Altinn.App.Common` and `Altinn.App.Api`. - -{{%/notice%}} - -If you want to expose options that are sensitive you can use `IInstanceAppOptionsProvider`, which will validate that the user has read rights defined in the authorization policy defined in the app's `policy.xml`-file. -Below you find an example of how to implement a secured custom options provider. The `IInstanceAppOptionsProvider` interface must be implemented, and a `secure`-prop must be added to the component. -The following option will be exposed at `/{org}/{app}/instances/{instanceOwnerId}/{instanceGUID}/options/children`. - -```C# -using System.Collections.Generic; -using System.Threading.Tasks; -using Altinn.App.Common.Models; -using Altinn.App.PlatformServices.Models; - -namespace Altinn.App.Core -{ - public class ChildrenAppOptionsProvider : IInstanceAppOptionsProvider - { - public string Id { get; set; } = "children"; - - public Task GetInstanceAppOptionsAsync(InstanceIdentifier instanceIdentifier, string language, Dictionary keyValuePairs) - { - // ... - // Some custom code to get the list of children from the instance owner - // ... - - var options = new AppOptions - { - Options = new List - { - new AppOption - { - Label = "Ole", - Value = "1" - }, - new AppOption - { - Label = "Dole", - Value = "2" - }, - new AppOption - { - Label = "Doffen", - Value = "3" - } - } - }; - - return Task.FromResult(options); - } - } -} - -``` - -For your implementation to be picked up you need to add the following line in your `Startup.cs` (or `Program.cs` in .NET 6): - -```csharp -services.AddTransient(); -``` - -Note that you can have multiple registrations of this interface. The correct implementation is resolved by finding the one with the correct id. - -The interface has a property `Id`, which should be set to the optionId, and a method `GetInstanceAppOptionsAsync` for resolving the options. This method accepts a language code and a dictionary of key/value pairs. Both parameters will typically be query parameters picked up from the controller and passed in. Although language could be put in the dictionary as well it's decided to be explicit on this particular parameter. These parameters are the same as for the open variant of options, in addition the instance id (which identifies both the instance owner and the instance itself) will be passed in. - -The final configuration needed is the `secure`-boolean on the component. Example: - -```json {hl_lines=[13]} - { - "id": "dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "Some title", - "description": "Some description" - }, - "dataModelBindings": { - "simpleBinding": "some.field" - }, - "required": true, - "optionsId": "children", - "secure": true - } -``` \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/dynamic-codelists/_index.nb.md b/content/altinn-studio/guides/development/options/dynamic-codelists/_index.nb.md deleted file mode 100644 index a2dc1c4cccc..00000000000 --- a/content/altinn-studio/guides/development/options/dynamic-codelists/_index.nb.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Dynamiske kodelister generert under kjøring av applikasjonen -linktitle: Dynamiske kodelister -description: Hvordan lage dynamiske kodelister som bygges opp når applikasjonen kjører? -toc: false -weight: 100 ---- - -I app-templaten har man også mulighet til å ha dynamisk kodelister som bestemmes under kjøringen av appen. Dette muligjør det å eksponere dynamiske verdier som kan filtreres eller hentes fra andre kilder. Dynamiske kodelister kan enten være åpne, dvs. alle brukere når de, eller de kan være sikret gjennom at du må ha tilgang til instansen for å se de. - -I versjoner eldre enn 4.24.0 ble dette gjort ved å legge til kode i metoden `GetOptions` i `App.cs`. Denne metoden er nå erstattet ved at man legger til egne klasser for hver kodeliste som implementerer et interface og at man registrerer denne i applikasjonen sin 'dependency injection cointainer'. Dette gir bedre skille mellom de ulike kodelistene, muliggjør å sende avhengigheter inn i konstruktøren til klassen, sende inn språk og andre parametere og generelt håndtere alle aspekter av implementeringen slik du selv ønsker det. - -For kodelister som er åpne implementerer man `IAppOptionsProvider` interfacet, mens for kodelister som skal være sikret implementerer man `IInstanceAppOptionsProvider`. Fremgangsmåten er den samme for begge to og modellen som returneres er lik. Men implementeringen holdes adskilt for ikke å eksponere verdier som skulle vært sikret. - -### Åpne dynamiske kodelister - -Under finner du et eksempel på hvordan dette kan settes opp for en åpen kodeliste. Her vil man få ut den oppsatte kodelisten i det appen får et kall mot `{org}/{app}/api/options/countries`. - -```C# -using Altinn.App.Common.Models; -using Altinn.App.PlatformServices.Options; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Altinn.App.Core -{ - public class CountryAppOptionsProvider : IAppOptionsProvider - { - public string Id { get; set; } = "countries"; - - public Task GetAppOptionsAsync(string language, Dictionary keyValuePairs) - { - var options = new AppOptions - { - Options = new List - { - new AppOption - { - Label = "Norway", - Value = "47" - }, - new AppOption - { - Label = "Sweden", - Value = "46" - } - } - }; - - return Task.FromResult(options); - } - } -} - -``` - -For at denne implementasjonen skal plukkes opp av applikasjonen må den registreres i `Startup.cs` (eller `Program.cs` i .NET 6): - -```csharp -services.AddTransient(); -``` - -Legg merke til at du kan ha mange implementasjoner av dette interfacet. Den rette implementasjonen finnes gjennom å se på hvilken kodeliste id det spørres etter. - -Interfacene har en egenskap `Id`, som skal settes til til den id'en man skal spørre etter, og en metode `GetAppOptionsAsync` som returnerer selve kodelisten. Denne metoden tar i mot språk og en liste med key/value par som typisk er query parametre som plukkes opp av kontrolleren og sendes inn. Selv om språk kunne vært et key/value par og sånn sett hvert i listen, så er denne lagt utenfor for å være eksplisitt på språk. - -> Språkkoder bør baseres på ISO 639-1 standarden eller W3C IANA Language Subtag Registry standarden. Sistnevnte bygger på ISO 639-1 standarden men garanterer at alle kodene er unike, noe ISO 639-1 ikke gjør. -> - -### Sikrede dynamiske kodelister - -{{%notice warning%}} - -**MERK:** for å benytte denne funksjonaliteten må man versjon >= 4.27.0 av nugetpakkene `Altinn.App.PlatformServices`, `Altinn.App.Common` og `Altinn.App.Api`. - -{{%/notice%}} - -Om du ønsker å eksponere kodelister som inneholder sensitive data som man ikke ønsker skal være tilgjengelige i et åpent API kan man benytte `IInstanceAppOptionsProvider`. Disse kodelistene validerer at brukeren har lesetilgang definert i applikasjonens `policy.xaml`-fil. -Under finner du et eksempel på man setter opp en sikret kodeliste. Interfacet `IInstanceAppOptionsProvider` må implementeres og en `secure` boolean må legges på komponenten. -Her vil man få ut den oppsatte kodelisten i det appen får et kall mot `/{org}/{app}/instances/{instanceOwnerId}/{instanceGUID}/options/children`. - -```C# -using System.Collections.Generic; -using System.Threading.Tasks; -using Altinn.App.Common.Models; -using Altinn.App.PlatformServices.Models; - -namespace Altinn.App.Core -{ - public class ChildrenAppOptionsProvider : IInstanceAppOptionsProvider - { - public string Id { get; set; } = "children"; - - public Task GetInstanceAppOptionsAsync(InstanceIdentifier instanceIdentifier, string language, Dictionary keyValuePairs) - { - // ... - // Some custom code to get the list of children from the instance owner - // ... - - var options = new AppOptions - { - Options = new List - { - new AppOption - { - Label = "Ole", - Value = "1" - }, - new AppOption - { - Label = "Dole", - Value = "2" - }, - new AppOption - { - Label = "Doffen", - Value = "3" - } - } - }; - - return Task.FromResult(options); - } - } -} - -``` - -For at denne implementasjonen skal plukkes opp av applikasjonen må den registreres i `Startup.cs` (eller `Program.cs` i .NET 6): - -```csharp -services.AddTransient(); -``` - -Legg merke til at du kan ha mange implementasjoner av dette interfacet. Den rette implementasjonen finnes gjennom å se på hvilken kodeliste id det spørres etter. - -Interfacene har en egenskap `Id`, som skal settes til til den id'en man skal spørre etter, og en metode `GetAppOptionsAsync` som returnerer selve kodelisten. Denne metoden tar i mot språk og en liste med key/value par som typisk er query parametre som plukkes opp av kontrolleren og sendes inn. Selv om språk kunne vært et key/value par og sånn sett hvert i listen, så er denne lagt utenfor for å være eksplisitt på språk. - -Siste konfigurasjon som trengs er å legge til `secure`-boolean på den aktuelle komponenten. Eksempel: - -```json {hl_lines=[13]} - { - "id": "dropdown-component", - "type": "Dropdown", - "textResourceBindings": { - "title": "Some title", - "description": "Some description" - }, - "dataModelBindings": { - "simpleBinding": "some.field" - }, - "required": true, - "optionsId": "children", - "secure": true - } -``` diff --git a/content/altinn-studio/guides/development/options/functionality/_index.en.md b/content/altinn-studio/guides/development/options/functionality/_index.en.md new file mode 100644 index 00000000000..970f7b9edc2 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/_index.en.md @@ -0,0 +1,7 @@ +--- +title: Functionality +description: Common functionality for all option sources +weight: 20 +--- + +{{}} diff --git a/content/altinn-studio/guides/development/options/functionality/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/_index.nb.md new file mode 100644 index 00000000000..51e195d667f --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/_index.nb.md @@ -0,0 +1,7 @@ +--- +title: Funksjonalitet +description: Felles funksjonalitet for alle kildene til kodelister +weight: 20 +--- + +{{}} diff --git a/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.en.md b/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.en.md new file mode 100644 index 00000000000..cd5d05520d5 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.en.md @@ -0,0 +1,39 @@ +--- +title: Automatic cleanup +description: How unknown options are automatically removed from the data model +weight: 100 +--- + +Some options for components may be dynamic. Either directly via [dynamic options](../../sources/dynamic), +[options derived from a changing data model](../../sources/from-data-model), or by [static options](../../sources/static) +where some values may be [filtered](../filtering) out. + +When the options are dynamic, the data model may contain values that are no longer valid. This can happen if the user +(or prefill) has selected an option that is no longer available. In such cases, to avoid the data model from containing +invalid values, unknown options are automatically removed from the data model. + +## How it works + +When the form is loaded, the options for all components are fetched and compared to their values in the data model. Even +if a component is not visible (i.e. when on a page that is not currently shown), the app-frontend will still +check the options for that component and remove any values that are not in the options list. + +This has some implications that you should be aware of: +- If you configure multiple components pointing to the same field in the data model, the options for all those components + should be the same. If they are not, the data model will be cleaned up to only contain the options that are valid for + all components. +- If the component is not marked as `required`, the user can submit the form even if the value is removed from the data + model. If you want to ensure that the user selects a valid option, you should mark the component as required. + +## When it _doesn't_ happen + +- The automatic cleanup of unknown options currently only happens when the form is loaded via the app-frontend, + i.e. when the user opens the form in a browser. +- Automatic cleanup does not happen when the component is marked as `hidden`. For this reason you can also have + multiple components pointing to the same field in the data model, where some are hidden and some are visible. In such + cases, only the visible components will have their options checked and cleaned up. This is also the case if a component + is hidden due to being on a hidden page, or inside another hidden component. The term 'hidden' in this sense refers to + [dynamic expressions](../../../dynamics) used to hide components, not which components are currently visible on + the page. +- Removal of unknown values does not happen for the `FileUploadWithTag` component. +- Removal of unknown values does not happen for components configured with `renderAsSummary` set to `true`. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.nb.md new file mode 100644 index 00000000000..d90519997ca --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/automatic-cleanup/_index.nb.md @@ -0,0 +1,39 @@ +--- +title: Automatisk opprydding +description: Hvordan ukjente svaralternativer automatisk fjernes fra datamodellen +weight: 100 +--- + +Noen svaralternativer for komponenter kan være dynamiske. Enten direkte via [dynamiske svaralternativer](../../sources/dynamic), +[svaralternativer hentet fra en endrende datamodell](../../sources/from-data-model), eller ved [statiske svaralternativer](../../sources/static) +hvor noen verdier kan være [filtrert](../filtering) bort. + +Når svaralternativene er dynamiske, kan datamodellen inneholde verdier som ikke lenger er gyldige. Dette kan skje hvis +brukeren (eller forhåndsutfyllingen) har valgt et alternativ som ikke lenger er tilgjengelig. I slike tilfeller, for å +unngå at datamodellen inneholder ugyldige verdier, fjernes ukjente svaralternativer automatisk fra datamodellen. + +## Hvordan det fungerer + +Når skjemaet lastes, hentes svaralternativene for alle komponenter og sammenlignes med verdiene i datamodellen. Selv om +en komponent ikke er synlig (dvs. når på en side som for øyeblikket ikke vises), vil app-frontend fortsatt +sjekke svaralternativene for den komponenten og fjerne eventuelle verdier som ikke er i svaralternativlisten. + +Dette har noen implikasjoner som du bør være klar over: +- Hvis du konfigurerer flere komponenter som peker på det samme feltet i datamodellen, bør svaralternativene for alle + disse komponentene være de samme. Hvis de ikke er det, vil datamodellen ryddes opp for å kun inneholde de + svaralternativene som er gyldige for alle komponentene. +- Hvis komponenten ikke er merket som `required`, kan brukeren sende inn skjemaet selv om verdien fjernes fra + datamodellen. Hvis du vil sikre at brukeren velger et gyldig alternativ, bør du merke komponenten som påkrevd. + +## Når det _ikke_ skjer + +- Den automatiske oppryddingen av ukjente svaralternativer skjer for øyeblikket bare når skjemaet lastes via app-frontend, + dvs. når brukeren åpner skjemaet i en nettleser. +- Automatisk opprydding skjer ikke når komponenten er merket som `hidden`. Derfor kan du også ha flere + komponenter som peker på det samme feltet i datamodellen, hvor noen er skjulte og noen er synlige. I slike + tilfeller vil bare de synlige komponentene ha svaralternativene sjekket og ryddet opp. Dette er også tilfelle + hvis en komponent er skjult fordi den er på en skjult side, eller inne i en annen skjult komponent. + Begrepet 'skjult' i denne sammenhengen refererer til [dynamiske uttrykk](../../../dynamics) brukt for å skjule + komponenter, ikke hvilke komponenter som for øyeblikket er synlige på siden. +- Fjerning av ukjente verdier skjer ikke for komponenten `FileUploadWithTag`. +- Fjerning av ukjente verdier skjer ikke for komponenter konfigurert med `renderAsSummary` satt til `true`. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/data-binding/_index.en.md b/content/altinn-studio/guides/development/options/functionality/data-binding/_index.en.md new file mode 100644 index 00000000000..0c6d1cf410a --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/data-binding/_index.en.md @@ -0,0 +1,119 @@ +--- +title: Data binding +description: What can be stored in the data model +weight: 50 +--- + +### Storing the chosen option + +{{}} +Below are some examples of setting up data binding for components using options. For some components, the setup will be +different, and it is recommended to look at the specific documentation for the component for more information. +{{}} + +Components using options must be set up to store the selected options in the data model. Usually, the component will +store the value of the selected option in the data model against a field of type `string`, set up in the component +configuration with the key `simpleBinding`: + +```json {hl_lines=["8"]} +{ + "id": "single-choice-component", + "type": "RadioButtons", + "textResourceBindings": { + "title": "Do you own a cat?" + }, + "dataModelBindings": { + "simpleBinding": "Submitter.HasCat" + }, + "options": [ + { "value": "y", "label": "Yes" }, + { "value": "n", "label": "No" } + ] +} +``` + +In the example above, the component will store the choice of whether the user owns a cat in the field `Submitter.HasCat` +in the data model. This field will get the value `y` if the user chooses "Yes" and `n` if the user chooses "No". + +For multi-choice components such as [`Checkboxes`](../../../../../reference/ux/components/checkboxes) and +[`MultipleSelect`](../../../../../reference/ux/components/multipleselect), the component will store a comma-separated +list of selected values in the data model. + +```json +{ + "id": "multi-choice-component", + "type": "Checkboxes", + "textResourceBindings": { + "title": "Which pets do you have?" + }, + "dataModelBindings": { + "simpleBinding": "Submitter.Pets" + }, + "options": [ + { "value": "cat", "label": "Cat" }, + { "value": "dog", "label": "Dog" }, + { "value": "fish", "label": "Fish" } + ] +} +``` + +In the example above, the component will store a comma-separated list of selected pets in the field `Submitter.Pets` in +the data model. If you choose "Cat" and "Fish", the field will get the value `"cat,fish"`. The order of the choices is +not guaranteed to be the same as the order of the options, nor the order the user chose them in. + +{{}} +Note that the value for each option must be unique, and if you use multi-choice components, none of the values can +contain a comma. +{{}} + +### Storing the label + +Components using options will usually only store the value of the selected option in the data model. While this is +usually sufficient, there are cases where you might want to store the label of the selected option as well. This can +be useful for displaying the selected option in a simple presentation of the data without additional lookups or if +you have a requirement to remember the label the user actually picked in case it has changed over time. When storing +the label in the data model, it will respect the users chosen language, look up the actual text from the text resources +and store the final value in the data model. + +This is configured by having a separate binding with the key `label`. The `label` binding should point to a field in the +data model of type `string`: + +```json {hl_lines=["6"]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "dataModelBindings": { + "simpleBinding": "municipality.value", + "label": "municipality.label" + }, + "optionsId": "municipalities" +} +``` + +### Storing metadata + +When fetching options, especially [shared common code lists](../../sources/shared), you might want to store some +metadata describing how the options were fetched. This can be useful for reconstructing the options after the form +has been submitted, as well as for logging purposes. + +This can be configured by setting the `metadata` property on the components `dataModelBinding` property to a field +in the data model containing a `string` value: + +```json {hl_lines=["9"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype", + "metadata": "soknad.nyGaranti.loyvetypeMetadata" + }, + "required": true, + "optionsId": "loyvetyper", + "mapping": { + "soknad.transportorOrgnummer": "orgnummer" + } +} +``` + +This configuration will store the metadata of the retrieved options as a comma separated string in the +field `soknad.nyGaranti.loyvetypeMetadata` in the data model. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/data-binding/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/data-binding/_index.nb.md new file mode 100644 index 00000000000..417794e6c1d --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/data-binding/_index.nb.md @@ -0,0 +1,120 @@ +--- +title: Dataknytning +description: Hva kan lagres i datamodellen +weight: 50 +--- + +### Lagring av valgt alternativ + +{{}} +Under følger noen eksempler på oppsett av dataknytning for komponenter som bruker svaralternativer. For noen komponenter +vil oppsettet være annerledes, og det anbefales å se på komponentens spesifikke dokumentasjon for mer informasjon. +{{}} + +Komponenter som bruker svaralternativer må settes opp til å lagre valgte alternativer i datamodellen. Vanligvis vil +komponenten lagre verdien av det valgte alternativet i datamodellen mot et felt av typen `string`, satt opp i +komponentens konfigurasjon med nøkkelen `simpleBinding`: + +```json {hl_lines=["8"]} +{ + "id": "single-choice-component", + "type": "RadioButtons", + "textResourceBindings": { + "title": "Eier du en katt?" + }, + "dataModelBindings": { + "simpleBinding": "Submitter.HasCat" + }, + "options": [ + { "value": "y", "label": "Ja" }, + { "value": "n", "label": "Nei" } + ] +} +``` + +I eksempelet over vil komponenten lagre valget av om brukeren eier en katt i feltet `Submitter.HasCat` i datamodellen. +Dette feltet får verdien `y` om brukeren velger "Ja" og `n` om brukeren velger "Nei". + +For flervalgskomponenter som f.eks. [`Checkboxes`](../../../../../reference/ux/components/checkboxes) og +[`MultipleSelect`](../../../../../reference/ux/components/multipleselect), vil komponenten lagre en kommaseparert +liste av valgte verdier i datamodellen. + +```json +{ + "id": "multi-choice-component", + "type": "Checkboxes", + "textResourceBindings": { + "title": "Hvilke kjæledyr har du?" + }, + "dataModelBindings": { + "simpleBinding": "Submitter.Pets" + }, + "options": [ + { "value": "cat", "label": "Katt" }, + { "value": "dog", "label": "Hund" }, + { "value": "fish", "label": "Fisk" } + ] +} +``` + +I eksempelet over vil komponenten lagre en kommaseparert liste av valgte kjæledyr i feltet `Submitter.Pets` i +datamodellen. Hvis du velger "Katt" og "Fisk", vil feltet få verdien `"cat,fish"`. Rekkefølgen på valgene er ikke +garantert å være den samme som rekkefølgen på svaralternativene, ei heller den rekkefølgen brukeren valgte dem i. + +{{}} +Legg merke til at verdien for hvert svaralternativ må være unik, og om man bruker flervalgskomponenter kan ingen +av verdiene inneholde et komma. +{{}} + +### Lagring av ledetekst / visningsverdi + +Komponenter som bruker svaralternativer vil vanligvis bare lagre verdien av det valgte alternativet i datamodellen. +Dette er ofte tilstrekkelig, men i noen tilfeller kan det være nyttig å lagre ledeteksten til det valgte alternativet +også. For eksempel kan dette være nyttig om man trenger å vise det valgte alternativet i en enkel tekstvisning senere, +uten å måtte gjøre ytterligere oppslag. Det kan også være nyttig å huske hvilken ledetekst brukeren faktisk valgte i +tilfelle den endres over tid. Når ledeteksten lagres i datamodellen, vil den følge brukerens valgte språk, slå opp +teksten i tekstressursene og lagre den endelige verdien i datamodellen. + +Dette konfigureres ved å ha en separat binding med nøkkelen `label`. Denne bindingen må peke på et felt i +datamodellen av typen `string`: + +```json {hl_lines=["6"]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "dataModelBindings": { + "simpleBinding": "kommune.value", + "label": "kommune.label" + }, + "optionsId": "kommuner" +} +``` + +### Lagring av metadata + +Når appen henter svaralternativer, spesielt [felles kodelister](../../sources/shared), kan det være nyttig å lagre +noen metadata som beskriver hvordan svaralternativene ble hentet. Dette kan være nyttig for å rekonstruere +svaralternativene etter at skjemaet er sendt inn, samt for logging. + +Dette kan konfigureres ved å sette `metadata`-egenskapen på komponentens `dataModelBinding`-egenskap til et felt i +datamodellen som inneholder en `string`-verdi: + +```json {hl_lines=["9"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype", + "metadata": "soknad.nyGaranti.loyvetypeMetadata" + }, + "required": true, + "optionsId": "loyvetyper", + "mapping": { + "soknad.transportorOrgnummer": "orgnummer" + } +} +``` + +Denne konfigurasjonen vil nå lagre metadataen til de hentede svaralternativene som en kommaseparert streng i +feltet `soknad.nyGaranti.loyvetypeMetadata` i datamodellen. +` \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/filtering/_index.en.md b/content/altinn-studio/guides/development/options/functionality/filtering/_index.en.md new file mode 100644 index 00000000000..b7551f9187b --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/filtering/_index.en.md @@ -0,0 +1,109 @@ +--- +title: Filtering +description: Removing some options from the list +weight: 200 +--- + +Filtering is a way to remove some of the options from the list. This can be useful if you want to limit which +options the user can choose from. + +Keep in mind that there are different approaches to making options dynamic: + +1. You can use [dynamics](../../../dynamics) to hide and show entirely different components based on a condition. These + components could be bound to the same location in the data model, but have different options. Note that + [automatic cleanup](../automatic-cleanup) may unexpectedly remove values from the data model when using this approach. + Test thoroughly to ensure form data is not lost when using this method. +2. By using [dynamic options](../../sources/dynamic) and passing query parameters to the backend, you can write custom code + to generate a different set of options based on those query parameters. This may be useful, but can cause lots of traffic + to the backend if the options are frequently changing. +3. Using [options from the data model](../../sources/from-data-model) you can set up options based on a repeating structure in + the data model. In combination with data processing on the backend, this can be a powerful way to create custom options + even when dynamic options based on query parameters would be problematic. + +Filtering options via the `optionFilter` property works with all of the above, and with +plain [static options](../../sources/static) as well. It makes it possible to use a [dynamic expression](../../../dynamics) +to filter out options based on the current state of the form. + +### Configuration + +In the example below, the `optionFilter` property is set to a dynamic expression that filters out the +option `should-be-removed`. Note that the `optionFilter` property accepts an expression for which options to keep, +so you should invert the logic to remove an option. + +The expression is evaluated for each option, and if it returns `true`, the option is _kept_. All other options are _removed_. + +```json {hl_lines=["10"]} +{ + "id": "dropdown-component-id", + "type": "Dropdown", + ... + "options": [ + { "value": "should-be-removed", "label": "Should be removed" }, + { "value": "red", "label": "Red" }, + { "value": "blue", "label": "Blue" } + ], + "optionFilter": ["notEquals", ["value"], "should-be-removed"] +} +``` + +The result of the above configuration will be a dropdown with two options: "Red" and "Blue". + +### The `value` function + +In the example above, the `value` function is used to access the value of the current option. This function can be used +with arguments to access other values in the option as well. + +- `["value"]` and `["value", "value"]` are equivalent, and will return the value of the current option. +- `["value", "label"]` will return the label of the current option. This label is the text given in the `label` property + of the option, before any text resources are looked up. +- `["value", "description"]` will return the [description of the current option](../texts), if set. +- `["value", "helpText"]` will return the [help text of the current option](../texts), if set. + + +### Example: Filtering duplicate options in a repeating group + +In the following animation, a `RepeatingGroup` component has been set up with a `Dropdown` component inside. +The `Dropdown` component has a `optionFilter` property that removes options that have already been used in +other rows in the repeating group. + +![Filtering options in a repeating group](filtering.gif) + +The configuration for this example is as follows: + +```json {hl_lines=["11-17"]} +{ + "id": "ingredientType", + "type": "Dropdown", + "textResourceBindings": { + "title": "Ingrediens" + }, + "dataModelBindings": { + "simpleBinding": "Ingredients.Type" + }, + "optionsId": "foods", + "optionFilter": [ + "or", + // Remove those that have been used elsewhere + ["not", ["commaContains", ["dataModel", "UsedTypes"], ["value"]]], + // But not if it's the currently chosen ingredient here + ["equals", ["component", "ingredientType"], ["value"]] + ] +} +``` + +A few things to note about the configuration: + +1. The already used types are stored in a comma-separated list in the `UsedTypes` field in the data model. This field + is updated using a [data processor](../../../../../reference/logic/dataprocessing) that finds all the unique types + in the `Ingredients` array. +2. If we just checked the `UsedTypes` field against the `value` of the current `Dropdown` component, as soon as an + ingredient was chosen, it would be removed from the list of options and + would [be automatically cleaned up](../automatic-cleanup). For this reason, we also check that the `value` is not + equal to the data set in the current `Dropdown` component. + +{{% notice warning %}} +The example above relies on saving the form data to the backend and running data processing to update +the `UsedTypes` field. For this reason, it is still fully possible to select the same ingredient in multiple rows +in the repeating group if you're fast enough. When using a method like this you should +also [implement validation](../../../../../reference/logic/validation) to catch any duplicates that might slip through. +{{% /notice %}} diff --git a/content/altinn-studio/guides/development/options/functionality/filtering/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/filtering/_index.nb.md new file mode 100644 index 00000000000..df209ffc1df --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/filtering/_index.nb.md @@ -0,0 +1,108 @@ +--- +title: Filtrering +description: Fjerne noen alternativer fra listen +weight: 200 +--- + +Filtrering gjør det mulig å fjerne noen av svaralternativene fra listen. Dette kan være nyttig hvis du vil begrense hvilke +alternativer brukeren kan velge mellom. + +Legg merke til at det allerede finnes flere måter å gjøre svaralternativene dynamiske på: + +1. Du kan bruke [dynamikk](../../../dynamics) for å skjule og vise helt forskjellige komponenter basert på en betingelse. Disse + komponentene kan være bundet til samme sted i datamodellen, men ha forskjellige svaralternativer. Merk at + [automatisk opprydding](../automatic-cleanup) kan fjerne verdier fra datamodellen når du bruker denne metoden. +2. Ved å bruke [dynamiske alternativer](../../sources/dynamic) og sende spørringsparametre til backenden, kan du skrive + kode for å generere et annet sett med alternativer basert på disse spørringsparametrene. Dette kan være nyttig, + men kan føre til mye nettverkstrafikk hvis alternativene og spørringsparametrene endres ofte. +3. Ved å bruke [svaralternativer fra en repeterende struktur i datamodellen](../../sources/from-data-model). I kombinasjon + med data prosessering på backenden, kan dette være en kraftig måte å lage egendefinerte svaralternativer på, selv når dynamiske + alternativer basert på spørringsparametre ville være problematisk. + +Filtrering av svaralternativer via `optionFilter`-egenskapen fungerer med alle de nevnte metodene, og med +vanlige [statiske svaralternativer](../../sources/static) også. +Dette gjør det mulig å bruke et [dynamisk uttrykk](../../../dynamics) for å filtrere ut svaralternativer basert +på den nåværende tilstanden i skjemaet. + +### Konfigurasjon + +I eksempelet under er `optionFilter`-egenskapen satt til et dynamisk uttrykk som filtrerer ut alternativet +`should-be-removed`. Merk at `optionFilter`-egenskapen settes opp med et uttrykk for hvilke alternativer som skal beholdes, +så du må snu om logikken for å fjerne et alternativ. + +Uttrykket evalueres for hvert svaralternativ, og hvis det returnerer `true`, beholdes alternativet. Alle andre alternativer fjernes. + +```json {hl_lines=["10"]} +{ + "id": "dropdown-component-id", + "type": "Dropdown", + ... + "options": [ + { "value": "should-be-removed", "label": "Denne blir fjernet" }, + { "value": "red", "label": "Rød" }, + { "value": "blue", "label": "Blå" } + ], + "optionFilter": ["notEquals", ["value"], "should-be-removed"] +} +``` + +Resultatet av konfigurasjonen over vil være en nedtrekksliste med to alternativer: "Rød" og "Blå". + +### `value`-funksjonen + +I eksempelet over brukes `value`-funksjonen til å få tilgang til verdien av det nåværende alternativet. +Denne funksjonen kan brukes med argumenter for å få tilgang til andre verdier i svaralternativet også. + +- `["value"]` og `["value", "value"]` er like, og vil returnere verdien av det nåværende alternativet. +- `["value", "label"]` vil returnere ledeteksten til det nåværende alternativet. Denne teksten er teksten som er gitt i + `label`-egenskapen til alternativet, før noen teksteressurser slås opp. +- `["value", "description"]` vil returnere [beskrivelsen av det nåværende alternativet](../texts), hvis satt. +- `["value", "helpText"]` vil returnere [hjelpeteksten til det nåværende alternativet](../texts), hvis satt. + +### Eksempel: Filtrere duplikate alternativer i en repeterende gruppe + +I animasjonen under er en `RepeatingGroup`-komponent satt opp med en `Dropdown`-komponent inni. Denne +`Dropdown`-komponenten har en `optionFilter`-egenskap som fjerner svaralternativer som allerede er brukt i andre +rader i den repeterende gruppen. + +![Filtrering av alternativer i en repeterende gruppe](filtering.gif) + +Konfigurasjonen for dette eksempelet er som følger: + +```json {hl_lines=["11-17"]} +{ + "id": "ingredientType", + "type": "Dropdown", + "textResourceBindings": { + "title": "Ingrediens" + }, + "dataModelBindings": { + "simpleBinding": "Ingredients.Type" + }, + "optionsId": "foods", + "optionFilter": [ + "or", + // Fjern de som har blitt brukt andre steder + ["not", ["commaContains", ["dataModel", "UsedTypes"], ["value"]]], + // Men ikke hvis det er den valgte ingrediensen her + ["equals", ["component", "ingredientType"], ["value"]] + ] +} +``` + +Noen ting å merke seg om konfigurasjonen: + +1. De allerede brukte ingrediens-typene lagres i en komma-separert liste i feltet `UsedTypes` i datamodellen. Dette feltet + oppdateres ved hjelp av [dataprosessering](../../../../../reference/logic/dataprocessing) som finner alle unike + ingredienstyper i `Ingredients`-lista. +2. Hvis vi bare sjekket `UsedTypes`-feltet mot `value`-verdien til den nåværende `Dropdown`-komponenten, ville alternativet + bli fjernet fra listen med en gang en ingrediens ble valgt, og verdien i datamodellen + ville da [blitt automatisk ryddet opp](../automatic-cleanup). Av denne grunn sjekker vi også at `value` ikke er lik + verdien til den nåværende `Dropdown`-komponenten. + +{{% notice warning %}} +Eksempelet ovenfor er avhengig av å lagre skjemadata til backenden og kjøre dataprosessering for å oppdatere +`UsedTypes`-feltet. Av denne grunn er det fortsatt fullt mulig å velge den samme ingrediensen i flere rader +i den repeterende gruppen hvis du er rask nok. Når du bruker en metode som dette bør du +også [implementere validering](../../../../../reference/logic/validation) for å fange opp eventuelle duplikate verdier. +{{% /notice %}} diff --git a/content/altinn-studio/guides/development/options/functionality/filtering/filtering.gif b/content/altinn-studio/guides/development/options/functionality/filtering/filtering.gif new file mode 100644 index 00000000000..ae2234a9c33 Binary files /dev/null and b/content/altinn-studio/guides/development/options/functionality/filtering/filtering.gif differ diff --git a/content/altinn-studio/guides/development/options/functionality/preselection/_index.en.md b/content/altinn-studio/guides/development/options/functionality/preselection/_index.en.md new file mode 100644 index 00000000000..a04e66517cb --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/preselection/_index.en.md @@ -0,0 +1,72 @@ +--- +title: Pre-selection +description: Making one option selected by default +weight: 250 +--- + +In some cases it is desirable that one of the options is pre-selected. There are different ways to achieve this. + +1. In the data model, you can [prefill the field](../../../prefill) with the desired value. Note that + the value must also belong to one of the valid options for components linked to this field in the data model, + otherwise the [value will be automatically cleaned up](../automatic-cleanup). +2. While filling out the form, you can also use [data processing](../../../../../reference/logic/dataprocessing) + to set the field to a desired pre-selected value. Beware that in some cases it is important to allow the user + to _not choose an option_. If the field is simply overwritten when it lacks a value, the user will not be able + to remove a pre-selected option using this approach. +3. Use the `preselectedOptionIndex` property, as described here. This allows the component itself to automatically choose + a numbered position in the list of options as pre-selected. + +### The `preselectedOptionIndex` property + +{{% notice info %}} +This property is available for most components support options, with the notable exception of the `FileUploadWithTag` +component. Only one option can be pre-selected at this time, and it is not possible to choose which option should +be pre-selected based on the value property of the option. +{{% /notice %}} + +This property allows you to choose an option as pre-selected. It takes an integer as an argument, which is the index +of the option that should be pre-selected. The index starts at 0 for the first option, 1 for the second option, etc. + +```json +{ + "id": "my-component-id", + "type": "Checkboxes", + ... + "options": [ + { "value": "red", "label": "Red" }, + { "value": "blue", "label": "Blue" }, + { "value": "green", "label": "Green" } + ], + "preselectedOptionIndex": 1 +} +``` + +In the configuration above, "Blue" will be pre-selected when the component is displayed. The user can still choose +another option, and since this is a multi-select component, the user can also remove the pre-selection by clicking +the pre-selected option again. + +This functionality follows a set of rules: + +1. If the data model already has a value for the field, the pre-selection will not be set. +2. If a pre-selected value has already been set earlier (and e.g. deselected), this will not happen again as long as + the app is open in the browser. If the user reloads the page, the pre-selection may be set again. +3. The pre-selection is set as soon as the page has loaded and the components are ready, regardless of whether the + component is visible on the screen or not. +4. Pre-selected values are not set for components that are hidden using [dynamics](../../../dynamics). If + the component is shown again later, the pre-selection may be set. +5. The preselected option is determined before [sorting](../sorting) is applied, but after [filtering](../filtering). + +{{% notice warning %}} +There are situations where pre-selection with this property is not optimal, and can lead to situations that may be +perceived as erroneous: + +1. If an option is selected, the user deselects it, and reloads the form later, the pre-selection will be set again - + even if the component is on a page the user has already filled out and won't see again. +2. When the form is submitted via the API, pre-selections set with this property will have no effect. This property + requires that the form is open in the browser to work. +3. If the form is in a state where the data model is not writable or locked (e.g. in the PDF generator), + setting pre-selected values can lead to error messages and failed submissions. + +For these reasons, it is recommended to use this property with caution, and to consider one of the other alternative +methods for pre-selection described above. +{{% /notice %}} diff --git a/content/altinn-studio/guides/development/options/functionality/preselection/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/preselection/_index.nb.md new file mode 100644 index 00000000000..164db237c83 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/preselection/_index.nb.md @@ -0,0 +1,75 @@ +--- +title: Forhåndsvalg +description: Gjør et av alternativene forhåndsvalgt +weight: 250 +--- + +Noen ganger er det ønskelig at et av svaralternativene er forhåndsvalgt. Det finnes ulike måter å oppnå dette på. + +1. I datamodellen kan du [forhåndsutfylle feltet](../../../prefill) med verdien som ønskes. Legg merke til at + verdien også må tilhøre en av de gyldige svaralternativene for komponenter knyttet mot dette feltet i datamodellen, + ellers vil [verdien bli ryddet bort automatisk](../automatic-cleanup). +2. Underveis i utfyllingen av et skjema kan man også bruke [dataprosessering](../../../../../reference/logic/dataprocessing) + for å sette feltet til en ønsket forhåndsvalgt verdi. I noen tilfeller er det riktignok viktig å tillate brukeren + å _ikke velge et alternativ_. Hvis feltet bare skrives over om det mangler en verdi, vil brukeren ikke kunne + fjerne et forhåndsvalgt alternativ. +3. Bruk av `preselectedOptionIndex`-egenskapen, som beskrevet her. Denne lar komponenten selv automatisk velge e + nummerert posisjon i listen av svaralternativer som forhåndsvalgt. + +### `preselectedOptionIndex`-egenskapen + +{{% notice info %}} +Denne egenskapen er tilgjengelig for de fleste komponenter som støtter svaralternativer, med unntak av `FileUploadWithTag` +komponenten. Kun ett alternativ kan være forhåndsvalgt til enhver tid, og det er ikke mulig å velge hvilket alternativ +som skal være forhåndsvalgt basert på `value`-egenskapen til alternativet. +{{% /notice %}} + +Denne egenskapen lar deg velge et svaralternativ som forhåndsvalgt. Den tar et heltall som argument, som er indeksen +til svaralternativet som skal være forhåndsvalgt. Indeksen starter på 0 for det første svaralternativet, 1 for det andre +osv. + +```json +{ + "id": "my-component-id", + "type": "Checkboxes", + ... + "options": [ + { "value": "red", "label": "Rød" }, + { "value": "blue", "label": "Blå" }, + { "value": "green", "label": "Grønn" } + ], + "preselectedOptionIndex": 1 +} +``` + +I konfigurasjonen over vil "Blå" være forhåndsvalgt når komponenten vises. Brukeren kan fortsatt velge et annet +alternativ, og ettersom dette er en flervalgskomponent, kan brukeren også fjerne forhåndsvalget ved å klikke på +det forhåndsvalgte alternativet igjen. + +Denne funksjonaliteten følger et sett med regler: + +1. Dersom datamodellen allerede har en verdi for feltet, vil ikke forhåndsvalget bli satt. +2. Dersom en forhåndsvalgt verdi allerede har blitt satt tidligere (og f.eks. valgt bort), vil dette ikke skje igjen så + lenge appen er åpen i nettleseren. Dersom brukeren laster siden på nytt, vil forhåndsvalget kunne bli satt igjen. +3. Forhåndsvalget settes med en gang siden har lastet og komponentene er klare, uavhengig av om komponenten vises + på skjermen eller ikke. +4. Forhåndsvalgte verdier blir ikke satt for komponenter som er skjult ved hjelp av [dynamikk](../../../dynamics). Om + komponenten senere blir vist igjen, vil forhåndsvalget kunne bli satt. +5. Det forhåndsvalgte alternativet blir bestemt før [sortering](../sorting) blir utført, men etter [filtrering](../filtering). + + +{{% notice warning %}} +Det finnes situasjoner hvor forhåndsvalg med denne egenskapen ikke er optimalt, og kan føre til +situasjoner som kan oppleves som feil: + +1. Dersom et alternativ blir valgt, brukeren velger det bort igjen, og laster skjemaet på nytt senere vil forhåndsvalget + bli satt igjen - selv om komponenten er på en side lenge før den brukeren ser på, og ikke vil se igjen. +2. Når skjemaet sendes inn via API-et vil forhåndsvalg satt med denne egenskapen ikke ha noen effekt. Denne egenskapen + krever at skjemaet er åpent i nettleseren for å fungere. +3. Om skjemaet er i en tilstand hvor datamodellen ikke er skrivbar (f.eks. i PDF-generatoren), vil potensielt + setting av forhåndsvalgte verdier kunne føre til feilmeldinger og mislykket innsending dersom datamodellen ikke + allerede hadde en verdi. + +Av disse grunnene anbefales det å bruke denne egenskapen med forsiktighet, og å vurdere et av de andre alternativene +for forhåndsvalg som er beskrevet over. +{{% /notice %}} \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/sorting/_index.en.md b/content/altinn-studio/guides/development/options/functionality/sorting/_index.en.md new file mode 100644 index 00000000000..c8710df0bbe --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/sorting/_index.en.md @@ -0,0 +1,42 @@ +--- +title: Sorting +description: Sorting the options in the list +weight: 300 +--- + +Options are usually displayed in the order they are defined in, but it is also possible to sort them alphabetically by +label. This can be useful to make it easier for the user to find the option they are looking for when the list does not +need to be in a specific order. + +Worth knowing: + +1. The sorting can change along the way if the user changes the language in the form. +2. The actual sorting is done after the [preselected value](../preselection) has been found. This means that + the sorting order should not affect which option is preselected. + +### Configuration + +The `sortOrder` property is optional, and can be set to one of the following values: + +- `asc` (ascending) - sorts in ascending order. Texts are sorted alphabetically from A to Z. +- `desc` (descending) - sorts in descending order. Texts are sorted alphabetically from Z to A. + +Example configuration: + +```json {hl_lines=[10]} +{ + "id": "sort-example", + "type": "RadioButtons", + "options": [ + { "value": "1", "label": "Cow" }, + { "value": "2", "label": "Alligator" }, + { "value": "3", "label": "Cat" }, + { "value": "4", "label": "Dog" } + ], + "sortOrder": "asc", + "preselectedOptionIndex": 0 +} +``` + +In the configuration above, the options will be sorted alphabetically in ascending order, and "Cow" will be preselected +even if it is not the first option displayed for the user. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/sorting/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/sorting/_index.nb.md new file mode 100644 index 00000000000..e51c860da86 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/sorting/_index.nb.md @@ -0,0 +1,42 @@ +--- +title: Sortering +description: Sortere valgene i listen +weight: 300 +--- + +Svaralternativer vises vanligvis i rekkefølgen de er definert i, men det er også mulig å sortere dem alfabetisk etter +ledetekst (`label`). Dette kan være nyttig for å gjøre det enklere for brukeren å finne det alternativet de leter etter +når listen ikke må være i en spesifikk rekkefølge. + +Verdt å vite: + +1. Sorteringen kan endre seg underveis dersom brukeren endrer språk i skjemaet. +2. Selve sorteringen gjøres etter [forhåndsvalgt verdi](../preselection) har blitt funnet. Det betyr at + sorteringsrekkefølgen ikke skal påvirke hvilket svaralternativ som er forhåndsvalgt. + +### Konfigurasjon + +Egenskapen `sortOrder` er valgfri, og kan settes til en av følgende verdier: + +- `asc` (ascending) - sorterer i stigende rekkefølge. Tekster sorteres alfabetisk fra A til Å. +- `desc` (descending) - sorterer i synkende rekkefølge. Tekster sorteres alfabetisk fra Å til A. + +Eksempel-konfigurasjon: + +```json {hl_lines=[10]} +{ + "id": "sort-example", + "type": "RadioButtons", + "options": [ + { "value": "1", "label": "Ku" }, + { "value": "2", "label": "Hest" }, + { "value": "3", "label": "Katt" }, + { "value": "4", "label": "Hund" } + ], + "sortOrder": "asc", + "preselectedOptionIndex": 0 +} +``` + +I konfigurasjonen over vil svaralternativene sorteres alfabetisk i stigende rekkefølge, og "Ku" vil være forhåndsvalgt +selv om det ikke er det første alternativet som vises for brukeren. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/texts/_index.en.md b/content/altinn-studio/guides/development/options/functionality/texts/_index.en.md new file mode 100644 index 00000000000..e580221c9c7 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/texts/_index.en.md @@ -0,0 +1,99 @@ +--- +title: Texts +description: The different text properties that can be used for options +weight: 150 +--- + +## Label + +The most common text property for options is the `label`. The `label` is the text that is displayed to the user in the +UI (in contrast to the `value`, which is the value [stored in the data model](../data-binding)). +Both `label` and `value` are required properties for an option. + +```json +[ + { "value": "norway", "label": "Norge" }, + { "value": "denmark", "label": "text.key.for.denmark" } +] +``` + +Labels, as with all texts, can either be plain text or a key pointing to a text resource. If the label is a key pointing +to a text resource, the text can change according to the selected language. + +The final text displayed to the user can [also be stored in the data model](../data-binding/#storing-the-label) if needed. + +## Description and help text + +If you need to provide additional information about an option, you can use the `description` and `helpText` properties. +`description` and `helpText` can be displayed by the components `RadioButtons` and `Checkboxes`. + +Descriptions and help texts can be provided to `options` in the same way that a `label` is provided, via +[static](../../sources/static), [dynamic](../../sources/dynamic) +and [options from the data model](../../sources/from-data-model). + +Click on the headers below to expand the examples. + +{{% expandlarge id="static" header="Static JSON file or component configuration" %}} +```json +[ + { + "value": "norway", + "label": "Norge", + "description": "This is a description", + "helpText": "This is a help text" + }, + { + "value": "denmark", + "label": "Danmark" + } +] +``` +{{% /expandlarge %}} + +{{% expandlarge id="dynamic" header="Dynamic options via C# code" %}} +```cs +var options = new AppOptions +{ + Options = new List + { + new AppOption + { + Label = "Ole", + Value = "1", + Description = "This is a description", + HelpText = "This is a help text" + }, + new AppOption + { + Label = "Dole", + Value = "2" + } + } +}; +``` +{{% /expandlarge %}} + +{{% expandlarge id="from-data-model" header="Options based on repeating structures in the data model" %}} +Note that the properties `label`, `description`, and `helpText` can also be [dynamic expressions](../../../dynamics) +this mode. + +```json +{ + "id": "checkboxes-component-id", + "type": "Checkboxes", + ... + "source": { + "group": "some.group", + "label": "checkboxes.label", + "description": "checkboxes.description", + "helpText": [ + "if", ["equals", ["dataModel.someField"], "someValue"], + "checkboxes.helpText1", + "else", + "checkboxes.helpText2" + ], + "value": "some.group[{0}].someField" + } +} +``` +{{% /expandlarge %}} \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/functionality/texts/_index.nb.md b/content/altinn-studio/guides/development/options/functionality/texts/_index.nb.md new file mode 100644 index 00000000000..8c1bf2c5b23 --- /dev/null +++ b/content/altinn-studio/guides/development/options/functionality/texts/_index.nb.md @@ -0,0 +1,100 @@ +--- +title: Tekster +description: De ulike tekstegenskapene som kan brukes for svaralternativer +weight: 150 +--- + +## Ledetekst + +Den vanligste tekstegenskapen for svaralternativer er `label`. `label` er teksten som vises for brukeren i +brukergrensesnittet (i motsetning til `value`, som er [verdien som lagres i datamodellen](../data-binding)). +Både `label` og `value` er påkrevde egenskaper for et svaralternativ. + +```json +[ + { "value": "norway", "label": "Norge" }, + { "value": "denmark", "label": "text.key.for.denmark" } +] +``` + +Ledetekster, som alle tekster, kan være enten ren tekst eller en nøkkel som peker til en tekstressurs. Hvis +`label` er en nøkkel som peker til en tekstressurs, kan teksten endres i henhold til brukerens valgte språk. + +Den endelige teksten som vises for brukeren kan [også lagres i datamodellen](../data-binding/#lagring-av-ledetekst--visningsverdi) hvis +det er nødvendig. + +## Beskrivelse og hjelpetekst + +Hvis du trenger å gi ytterligere informasjon om et alternativ, kan du bruke egenskapene `description` og `helpText`. +`description` og `helpText` kan vises av komponentene `RadioButtons` og `Checkboxes`. + +Beskrivelser og hjelpetekster kan spesifiseres på samme måte som en ledetekst (`label`) er gitt, enten i +[statiske](../../sources/static), [dynamiske](../../sources/dynamic) eller +[svaralternativer fra datamodellen](../../sources/from-data-model). + +Klikk på overskriftene nedenfor for å utvide eksemplene. + +{{% expandlarge id="static" header="Statisk JSON-fil eller komponentkonfigurasjon" %}} +```json +[ + { + "value": "norway", + "label": "Norge", + "description": "Dette er en beskrivelse", + "helpText": "Dette er en hjelpetekst" + }, + { + "value": "denmark", + "label": "Danmark" + } +] +``` +{{% /expandlarge %}} + +{{% expandlarge id="dynamic" header="Dynamiske svaralternativer via C#-kode" %}} +```cs +var options = new AppOptions +{ + Options = new List + { + new AppOption + { + Label = "Ole", + Value = "1", + Description = "This is a description", + HelpText = "This is a help text" + }, + new AppOption + { + Label = "Dole", + Value = "2" + } + } +}; +``` +{{% /expandlarge %}} + +{{% expandlarge id="from-data-model" header="Svaralternativer basert på repeterende strukturer i datamodellen" %}} +Legg merke til at egenskapene `label`, `description` og `helpText` også kan være [dynamiske uttrykk](../../../dynamics) +i denne modusen. + +```json +{ + "id": "checkboxes-component-id", + "type": "Checkboxes", + ... + "source": { + "group": "some.group", + "label": "checkboxes.label", + "description": "checkboxes.description", + "helpText": [ + "if", ["equals", ["dataModel.someField"], "someValue"], + "checkboxes.helpText1", + "else", + "checkboxes.helpText2" + ], + "value": "some.group[{0}].someField" + } +} +``` +{{% /expandlarge %}} \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.en.md b/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.en.md deleted file mode 100644 index 82d2790239c..00000000000 --- a/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.en.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Code lists based on repeating groups from the data model -linktitle: From repeating groups -description: How to configure code lists that gets it's values from a repeating group from the datamodel? -weight: 150 ---- - -Traditional options are based on resources fetched from the backend. -This approach differs a bit from this, as it enables setting up a direct connection from the options to the form data that is stored in app frontend. -A use case here would typically be if the user fills out a repeating list of data that should later be selected in a dropdown/checkbox/radiobutton. - -### Configuration - -To set up options from the data model we have set up a new property on `RadioButtons`, `Checkboxes`, and `Dropdown`-components called `source`. -This property contains the fields `group`, `label`, and `value`. Example: - -```json {hl_lines=["5-9"]} - { - "id": "dropdown-component-id", - "type": "Dropdown", - ... - "source": { - "group": "some.group", - "label": "dropdown.label", - "value": "some.group[{0}].someField" - } - }, -``` - -Explanation: - -- **group** - the group field in the data model to base the options on -- **label** - a reference to a text id to be used as the label for each iteration of the group, see more below. -- **value** - a reference to a field in the group that should be used as the option value. Notice that we set up this `[{0}]` syntax. Here the `{0}` will be replaced by each index of the group. - -Notice that the **value** field must be unique for each element. If the repeating group does not contain a field which is unique for each item it is recommended to add a field to the data model that can be used as identifier, for instance a GUID. - -As for the **label** property, we have to define a text resource that can be used as a label for each repetition of the group. -This follows similar syntax as the **value**, and will also be familiar if you have used [variables in text](/nb/altinn-studio/reference/ux/texts). - -Example text resource connected: - -```json -{ - "language": "nb", - "resources": [ - { - "id": "dropdown.label", - "value": "Person: {0}, Age: {1}", - "variables": [ - { - "key": "some.group[{0}].name", - "dataSource": "dataModel.default" - }, - { - "key": "some.group[{0}].age", - "dataSource": "dataModel.default" - } - ] - } - ] -} -``` - -In the example above we have two parameters in the text which is referencing fields in the group. -We also recognize the `[{0}]` syntax in the `key` prop which enables the usage of this label for each index in the group. diff --git a/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.nb.md b/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.nb.md deleted file mode 100644 index 2b9e7de9423..00000000000 --- a/content/altinn-studio/guides/development/options/repeating-group-codelists/_index.nb.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Kodelister fra repeterede grupper i datamodellen -linktitle: Fra repeterende grupper -description: Hvordan konfigurere kodelister som får verdiene sine basert på verdier hentet fra en repeterende gruppe i datamodellen? -weight: 150 ---- - -Tradisjonelle options baserer seg på ressurser hentet fra backend. -Denne måten å gjøre ting på endrer seg litt på dette, da det muliggjør å sette opp en direkte kobling fra komponent til skjemadata som ligger lagret i app frontend. -Et typisk bruksområde for dette er om brukeren fyller ut en liste med data som man senere i skjema ønsker å kunne velge mellom i en nedtrekksliste eller liknende. - -### Konfigurasjon - -For å sette opp options fra datamodellen har vi laget en nytt objekt som kan brukes på komponentene `RadioButtons`, `Checkboxes` og `Dropdown` som vi har kalt `source`. -Dette nye objektet inneholder feltene `group`, `label` og `value`. Eksempel: - -```json {hl_lines=["5-9"]} - { - "id": "dropdown-component-id", - "type": "Dropdown", - ... - "source": { - "group": "some.group", - "label": "dropdown.label", - "value": "some.group[{0}].someField" - } - }, -``` - -Forklaring: - -- **group** - gruppen i datamodellen man baserer options på. -- **label** - en referanse til en text id som brukes som label for hver iterasjon av gruppen. Se mer under. -- **value** - en referanse til det feltet i gruppen som skal bruke som option verdi. Legg merke til `[{0}]` syntaxen. Her vil `{0}` bli erstattet med den aktuelle indeksen for hvert element i gruppen. - -Merk at **value** feltet må være unikt for hvert element. Om man ikke har et felt som er unik anbefales det å legge på et ekstra felt i datamodellen som kan benyttes som identifikator f.eks en GUID eller liknende. - -For **label** feltet må vi definere en tekst ressurs som kan bli brukt som label for hver repetisjon av gruppen. -Dette følger samme syntax som **value**, og vil være kjent for deg om du har brukt [variabler i tekst](/nb/altinn-studio/reference/ux/texts). - -Eksempel: - -```json -{ - "language": "nb", - "resources": [ - { - "id": "dropdown.label", - "value": "Person: {0}, Age: {1}", - "variables": [ - { - "key": "some.group[{0}].name", - "dataSource": "dataModel.default" - }, - { - "key": "some.group[{0}].age", - "dataSource": "dataModel.default" - } - ] - } - ] -} -``` - -I dette eksempelet har vi satt opp to parametere i teksten som refererer til felter i gruppen. -Vi kjenner også igjen `[{0}]` syntaksen i `key` feltet som muliggjør gjenbruk av labelen for hver index i gruppen. diff --git a/content/altinn-studio/guides/development/options/sources/_index.en.md b/content/altinn-studio/guides/development/options/sources/_index.en.md new file mode 100644 index 00000000000..8b8b8349fcf --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/_index.en.md @@ -0,0 +1,18 @@ +--- +title: Sources +description: The different sources for options in Altinn Studio +weight: 10 +--- + +When you set up an options-enabled component in Altinn Studio, it needs to be connected to a source of options. There are three different properties in the component configuration that can be used for this, depending on the use case: + + +| Property | Usage | +|-------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `options` | [Static options defined per-component](./static/#in-component-configuration) | +| `optionsId` | Either [static options from on json files](./static/#from-json-files), [dynamic options](./dynamic) or [shared options](./shared) | +| `source` | [Options from the data model](./from-data-model) | + +At least one such property has to be set in the component configuration. If multiple are set, the configuration precedence will be the opposite of the table above, so `source` will take precedence over `optionsId`, which will take precedence over `options`. + +{{}} diff --git a/content/altinn-studio/guides/development/options/sources/_index.nb.md b/content/altinn-studio/guides/development/options/sources/_index.nb.md new file mode 100644 index 00000000000..92476c98f87 --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/_index.nb.md @@ -0,0 +1,17 @@ +--- +title: Kilder +description: De ulike kildene Altinn Studio kan bruke for svaralternativer +weight: 10 +--- + +Når du setter opp en komponent i Altinn Studio som skal ha svaralternativer, må den kobles til en kilde for svaralternativer. Det er tre forskjellige egenskaper i komponentkonfigurasjonen som kan brukes til dette, avhengig av bruksområdet: + +| Egenskap | Bruksområde | +|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `options` | [Statiske svaralternativer definert per komponent](./static/#i-komponentkonfigurasjonen) | +| `optionsId` | Enten [statiske svaralternativer fra json-filer](./static/#fra-json-filer), [dynamiske svaralternativer](./dynamic) eller [delte svaralternativer](./shared) | +| `source` | [Svaralternativer fra datamodellen](./from-data-model) | + +Minst en slik egenskap må settes i komponentkonfigurasjonen. Hvis flere er satt, vil konfigurasjonsprioriteten være motsatt av tabellen ovenfor, slik at `source` vil ha forrang over `optionsId`, som vil ha forrang over `options`. + +{{}} diff --git a/content/altinn-studio/guides/development/options/altinn2-codelists/_index.en.md b/content/altinn-studio/guides/development/options/sources/altinn2/_index.en.md similarity index 93% rename from content/altinn-studio/guides/development/options/altinn2-codelists/_index.en.md rename to content/altinn-studio/guides/development/options/sources/altinn2/_index.en.md index 59966480786..cf8f107e3da 100644 --- a/content/altinn-studio/guides/development/options/altinn2-codelists/_index.en.md +++ b/content/altinn-studio/guides/development/options/sources/altinn2/_index.en.md @@ -1,9 +1,11 @@ --- -title: Re-use code lists from Altinn 2 -linktitle: Code lists from Altinn 2 -description: How to configure code lists that exists in Altinn 2 in an Altinn 3 app? +title: Re-use options from Altinn 2 +linktitle: From Altinn 2 +description: Options fetched from Altinn 2 toc: false weight: 300 +aliases: + - /altinn-studio/guides/development/options/altinn2-codelists --- {{}} **Deprecated feature** diff --git a/content/altinn-studio/guides/development/options/altinn2-codelists/_index.nb.md b/content/altinn-studio/guides/development/options/sources/altinn2/_index.nb.md similarity index 93% rename from content/altinn-studio/guides/development/options/altinn2-codelists/_index.nb.md rename to content/altinn-studio/guides/development/options/sources/altinn2/_index.nb.md index dbd4557d6c6..b3d75d10a31 100644 --- a/content/altinn-studio/guides/development/options/altinn2-codelists/_index.nb.md +++ b/content/altinn-studio/guides/development/options/sources/altinn2/_index.nb.md @@ -1,9 +1,11 @@ --- -title: Gjenbruk av kodelister fra Altinn 2 -linktitle: Kodelister fra Altinn 2 -description: Hvordan konfigurere kodelister fra Altinn 2 i en Altinn 3 applikasjon? +title: Gjenbruk av svaralternativer fra Altinn 2 +linktitle: Fra Altinn 2 +description: Svaralternativer hentet fra Altinn 2 toc: false weight: 300 +aliases: + - /nb/altinn-studio/guides/development/options/altinn2-codelists --- {{}} **Utgående funksjonalitet** diff --git a/content/altinn-studio/guides/development/options/sources/dynamic/_index.en.md b/content/altinn-studio/guides/development/options/sources/dynamic/_index.en.md new file mode 100644 index 00000000000..8c7856917c6 --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/dynamic/_index.en.md @@ -0,0 +1,273 @@ +--- +title: Dynamic options +linktitle: Dynamic +description: Generated runtime by custom C# code +toc: false +weight: 100 +aliases: + - /altinn-studio/guides/development/options/dynamic-codelists +--- + +In an Altinn 3 app you can also have dynamic code lists that are generated runtime. This makes it possible to have dynamic lists that are filtered or looked up in external sources. Dynamic code lists can either be public (accessible to all, without authentication), or secured and limited to those with read access to the instance. + +For public code lists, implement the `IAppOptionsProvider` interface, while for secured code lists, implement the `IInstanceAppOptionsProvider`. The approach is the same for both, and the model returned is the same. The implementation is kept separate to avoid exposing values that should be secured. + +### Public code lists + +Below you find an example of how to implement a public options provider. + +```C# +using Altinn.App.Common.Models; +using Altinn.App.PlatformServices.Options; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Altinn.App.Core +{ + public class CountryAppOptionsProvider : IAppOptionsProvider + { + public string Id { get; set; } = "countries"; + + public Task GetAppOptionsAsync(string language, Dictionary keyValuePairs) + { + var options = new AppOptions + { + Options = new List + { + new AppOption + { + Label = "Norway", + Value = "47" + }, + new AppOption + { + Label = "Sweden", + Value = "46" + } + } + }; + + return Task.FromResult(options); + } + } +} +``` + +For your implementation to be picked up you need to add the following line in your `Program.cs`: + +```C# +services.AddTransient(); +``` + +The result of this implementation will be available at the endpoint `{org}/{app}/api/options/countries`. The identifier can be used in components, so to use the code list in a Dropdown component you can set `optionsId` as in the following example: + +```json {hl_lines=[10]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "Some title" + }, + "dataModelBindings": { + "simpleBinding": "some.field" + }, + "optionsId": "countries" +} +``` + +### Secured options + +If you want to produce lists of options that are sensitive you can implement `IInstanceAppOptionsProvider`, which will validate that the user has read rights as defined in the authorization policy from the `policy.xml`-file. +Below you'll find an example of how to implement a secured options provider. + +```C# +using System.Collections.Generic; +using System.Threading.Tasks; +using Altinn.App.Common.Models; +using Altinn.App.PlatformServices.Models; + +namespace Altinn.App.Core +{ + public class ChildrenAppOptionsProvider : IInstanceAppOptionsProvider + { + public string Id { get; set; } = "children"; + + public Task GetInstanceAppOptionsAsync(InstanceIdentifier instanceIdentifier, string language, Dictionary keyValuePairs) + { + // ... + // Some custom code to get the list of children from the instance owner + // ... + + var options = new AppOptions + { + Options = new List + { + new AppOption + { + Label = "Ole", + Value = "1" + }, + new AppOption + { + Label = "Dole", + Value = "2" + }, + new AppOption + { + Label = "Doffen", + Value = "3" + } + } + }; + + return Task.FromResult(options); + } + } +} + +``` + +For your implementation to be picked up you need to add the following line in your `Program.cs`: + +```csharp +services.AddTransient(); +``` + +The result of this implementation will be available at the endpoint `{org}/{app}/instances/{instanceOwnerId}/{instanceGUID}/options/children`. The identifier can be used in components, so to use the code list in a Dropdown component you can set `optionsId` as in the following example. It is also important to set the `secure` property to `true` to indicate that this is a secured code list. + +```json {hl_lines=["10-11"]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "Some title" + }, + "dataModelBindings": { + "simpleBinding": "some.field" + }, + "optionsId": "children", + "secure": true +} +``` + +## Query parameters + +The options endpoint you create supports query parameters. The `language` parameter is sent automatically, and other parameters can be sent from the component configuration. These can be read from the `keyValuePairs` parameter in the implementation. This can be useful to filter the code list based on data in the data model, or in other ways vary the code list based on context. + +As an example, consider a form with two `Dropdown` components that are linked together. The first lets the user choose a county, and the second lets the user choose a municipality. The municipalities shown in the second component should be filtered based on the county selected in the first component. This can be solved by sending the county as a query parameter to the code list for municipalities (and then filter the list based on this parameter in your implementation). + +### Based on expressions + +{{%notice info%}} +Query parameters based on expressions are available from app-frontend version 4.9.0 or higher. If your app is using the rolling release of major version 4, this is already available. +{{% /notice%}} + +You can add both static and dynamic parameters by setting up `queryParameters` on the component: + +```json {hl_lines=["11-15"]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "Some title" + }, + "dataModelBindings": { + "simpleBinding": "some.field" + }, + "optionsId": "countries", + "queryParameters": { + "country": "norway", + "orgnumber": ["dataModel", "some.orgnumber"], + "adult": ["greaterThanEq", ["dataModel", "some.age"], 18] + } +} +``` + +In the example above the parameter `country=norway` will always be added to the request (this is entirely static and will not change). The parameter `orgnumber={nr}` will be added, where `{nr}` is the value of the field `some.orgnumber` in the data model. The parameter `adult={bool}` will be added, where `{bool}` will be either `true` or `false` based on whether the value of the field `some.age` is greater than or equal to 18. + +More examples of expressions can be found in the [dynamics documentation](../../../dynamics), and the full list of available functions can be found in the [expression reference overview](../../../../../reference/logic/expressions). + +### Based on the data model + +{{%notice warning%}} +This approach is discouraged. From app-frontend version 4.9.0, it is possible to use the `queryParameters` property instead. As described above, this property allows you to add both static and dynamic query parameters using expressions, making them more flexible than `mapping`. + +At some point, the `mapping` property will be removed, but when that happens tools will be provided to migrate existing configurations to use `queryParameters` instead. +{{% /notice%}} + +You can add dynamic parameters by setting the `mapping` property on the component: + +```json {hl_lines=["12-14"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "NyGarantiLoyvetype" + }, + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype" + }, + "required": true, + "optionsId": "loyvetyper", + "mapping": { + "soknad.transportorOrgnummer": "orgnummer" + } +} +``` + +In the example above, the query parameter `orgnummer={nr}`, where `{nr}` is the value of `soknad.transportorOrgnummer` +will be set. +If an option is setup with mapping and the given data field changes app-frontend will refetch the option. This can be +used to dynamically decide which choices are available based on information given by the end user. + +Passing query parameters from repeating groups is also supported by adding an index indicator for the relevant indexes. +Example for a group: + +```json {hl_lines=[13]} +{ + "id": "dropdown-group", + "type": "Dropdown", + "textResourceBindings": { + "title": "Select city" + }, + "dataModelBindings": { + "simpleBinding": "Group.City" + }, + "required": true, + "optionsId": "cities", + "mapping": { + "Group[{0}].Country": "country" + } +} +``` + +For nested groups follows the same pattern but with an additional index indicator for the nested group: + +```json {hl_lines=[13]} +{ + "id": "dropdown-nested-group", + "type": "Dropdown", + "textResourceBindings": { + "title": "Select city" + }, + "dataModelBindings": { + "simpleBinding": "Group.SubGroup.City" + }, + "required": true, + "optionsId": "cities", + "mapping": { + "Group[{0}].SubGroup[{1}].Country": "country" + } +} +``` + +For a complete example of how this is setup see our [demo app.](https://altinn.studio/repos/ttd/dynamic-options-rep) + +## Things to consider + +- The method `GetAppOptionsAsync` receives a language code in the `language` parameter. Language codes are based on ISO 639-1 or the W3C IANA Language Subtag Registry. The latter is built upon the ISO 639-1 standard but is guaranties uniques of the codes, whereas ISO 639-1 have conflicting usage for some codes. +- An app can have many implementations of these interfaces, one for each option list. The correct implementation is found by looking at the code list identifier that is requested, and comparing it to the `Id` property in the implementation. This is also the identifier used in the `optionsId` property in the component configuration. Therefore, the `Id` property in the implementation must be unique per app. +- It may be tempting to implement a dynamic option list that fetches data from the data model and produces the option list based on this. This is not recommended, as the app frontend only fetches the option list once for each unique set of query parameters. This means that the user interface showing the option list will not update in line with changes in the data model. + - An alternative is to use the functionality for [dynamic code lists based on the data model](../from-data-model), in some cases together with corresponding code in [DataProcessor](../../../../../reference/logic/dataprocessing). + - Another alternative may be to use query parameters, as described above. +- If you use query parameters, it may be wise to consider how many unique combinations of parameters will typically be used in the app. If there are many, consider using a different approach such as fetching all data and filtering valid values in the frontend using [`optionFilter`](../../functionality/filtering). Many different combinations of query parameters can lead to the app having to do a lot of unnecessary work to fetch new option lists every time the user makes a change in the form. diff --git a/content/altinn-studio/guides/development/options/sources/dynamic/_index.nb.md b/content/altinn-studio/guides/development/options/sources/dynamic/_index.nb.md new file mode 100644 index 00000000000..cb0a919b456 --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/dynamic/_index.nb.md @@ -0,0 +1,272 @@ +--- +title: Dynamiske svaralternativer +linktitle: Dynamisk +description: Generert ved kjøring fra C#-kode +toc: false +weight: 100 +aliases: + - /nb/altinn-studio/guides/development/options/dynamic-codelists +--- + +I en Altinn 3 app har man også mulighet til å ha dynamisk kodelister som produseres dynamisk ved kjøring av appen. Dette gjør det mulig å lage dynamiske verdier, for eksempel ved å hente og filtrere verdier fra andre kilder. Dynamiske kodelister kan enten være åpne (tilgjengelig for alle, uten autentisering), eller sikret slik at brukeren må ha tilgang til instansen for å hente kodelisten. + +For åpne kodelister implementerer man `IAppOptionsProvider` interfacet, mens for sikrede kodelister implementerer man `IInstanceAppOptionsProvider`. Fremgangsmåten er den samme for begge og modellen som returneres er lik. Implementeringen holdes adskilt for ikke å eksponere verdier som skulle vært sikret. + +## Åpne kodelister + +Under finner du et eksempel på hvordan dette kan settes opp for en åpen kodeliste. + +```C# +using Altinn.App.Common.Models; +using Altinn.App.PlatformServices.Options; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Altinn.App.Core +{ + public class CountryAppOptionsProvider : IAppOptionsProvider + { + public string Id { get; set; } = "countries"; + + public Task GetAppOptionsAsync(string language, Dictionary keyValuePairs) + { + var options = new AppOptions + { + Options = new List + { + new AppOption + { + Label = "Norway", + Value = "47" + }, + new AppOption + { + Label = "Sweden", + Value = "46" + } + } + }; + + return Task.FromResult(options); + } + } +} +``` + +For at denne implementasjonen skal plukkes opp av applikasjonen må den registreres i `Program.cs`: + +```C# +services.AddTransient(); +``` + +Resultatet av denne implementasjonen vil bli tilgjengeleg på endepunktet `{org}/{app}/api/options/countries`. Identifikatoren kan brukes i komponenter, så for å bruke kodelisten i en Dropdown-komponent kan man sette `optionsId` som i følgende eksempel: + +```json {hl_lines=[10]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "Some title" + }, + "dataModelBindings": { + "simpleBinding": "some.field" + }, + "optionsId": "countries" +} +``` + +## Sikrede kodelister + +Om du ønsker å produsere kodelister som inneholder sensitive data som ikke skal være tilgjengelige i et åpent API kan man implementere `IInstanceAppOptionsProvider`. Slike sikrede kodelister kontrollerer at brukeren har lesetilgang som definert i applikasjonens `policy.xaml`-fil før brukeren kan hente innholdet i kodelisten. +Under finner du et eksempel på hvordan man setter opp en sikret kodeliste. + +```C# +using System.Collections.Generic; +using System.Threading.Tasks; +using Altinn.App.Common.Models; +using Altinn.App.PlatformServices.Models; + +namespace Altinn.App.Core +{ + public class ChildrenAppOptionsProvider : IInstanceAppOptionsProvider + { + public string Id { get; set; } = "children"; + + public Task GetInstanceAppOptionsAsync(InstanceIdentifier instanceIdentifier, string language, Dictionary keyValuePairs) + { + // ... + // Some custom code to get the list of children from the instance owner + // ... + + var options = new AppOptions + { + Options = new List + { + new AppOption + { + Label = "Ole", + Value = "1" + }, + new AppOption + { + Label = "Dole", + Value = "2" + }, + new AppOption + { + Label = "Doffen", + Value = "3" + } + } + }; + + return Task.FromResult(options); + } + } +} + +``` + +For at denne implementasjonen skal plukkes opp av applikasjonen må den registreres i `Program.cs`: + +```C# +services.AddTransient(); +``` + +Resultatet av denne implementasjonen vil bli tilgjengeleg på endepunktet `{org}/{app}/instances/{instanceOwnerId}/{instanceGUID}/options/children`. Identifikatoren kan brukes i komponenter, så for å bruke kodelisten i en Dropdown-komponent kan man sette `optionsId` som i følgende eksempel. Det er også viktig å sette `secure`-egenskapen til `true` for å indikere at dette er en sikret kodeliste. + +```json {hl_lines=["10-11"]} +{ + "id": "dropdown-component", + "type": "Dropdown", + "textResourceBindings": { + "title": "Some title" + }, + "dataModelBindings": { + "simpleBinding": "some.field" + }, + "optionsId": "children", + "secure": true +} +``` + +## Spørringsparametre + +Kodeliste-endepunktet du lager støtter spørringsparametre. Parameteren `language` sendes med automatisk, og andre parametre kan sendes med fra komponentkonfigurasjonen. Disse kan du lese ut fra `keyValuePairs`-parameteren i implementasjonen. Dette kan være nyttig for å filtrere kodelisten basert på data i datamodellen, eller på andre måter variere kodelisten basert på kontekst. + +Som et eksempel kan vi se for oss et skjema med to `Dropdown`-komponenter som er knyttet sammen. Den første lar brukeren velge et fylke, og den andre lar brukeren velge en kommune. Kommunene som vises i den andre komponenten skal være filtrert basert på fylket som er valgt i den første komponenten. Dette kan løses ved å sende med fylket som et spørringsparameter til kodelisten for kommuner. + +### Basert på uttrykk + +{{%notice info%}} +Dynamiske parametre basert på uttrykk er tilgjengelig fra app-frontend versjon 4.9.0 eller høyere. Hvis appen din bruker den rullerende utgivelsen av hovedversjon 4, er dette allerede tilgjengelig. +{{% /notice%}} + +Man kan legge til både statiske og dynamiske parametre ved å sette opp `queryParameters` på den aktuelle komponenten: + +```json {hl_lines=["12-16"]} +{ + "id": "dropdown-komponent", + "type": "Dropdown", + "textResourceBindings": { + "title": "NyGarantiLoyvetype" + }, + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype" + }, + "required": true, + "optionsId": "loyvetyper", + "queryParameters": { + "loyvetype": "garanti", + "orgnummer": ["dataModel", "soknad.transportorOrgnummer"], + "myndig": ["greaterThanEq", ["dataModel", "soknad.alder"], 18] + } +} +``` + +I eksempelet over vil parameteret `loyvetype=garanti` alltid bli sendt med (dette er helt statisk og vil ikke endre seg). Parameteret `orgnummer={nr}` vil bli sendt med, hvor `{nr}` er verdien på feltet `soknad.transportorOrgnummer` i datamodellen. Parameteret `myndig={bool}` vil bli sendt med, hvor `{bool}` blir enten `true` eller `false` basert på om verdien på feltet `soknad.alder` er større enn eller lik 18. + +Flere eksempler på uttrykk finner du i [dokumentasjonen for dynamikk](../../../dynamics), og den fullstendige oversikten over tilgjengelige funksjoner finner du i [referanseoversikten over uttrykk](../../../../../reference/logic/expressions). + +### Basert på datamodellen + +{{%notice warning%}} +Denne tilnærmingen frarådes. Fra og med app-frontend versjon 4.9.0 er det mulig å bruke `queryParameters`-egenskapen i stedet. Som beskrevet ovenfor, tillater denne egenskapen deg å legge til både statiske og dynamiske spørringsparametre ved hjelp av uttrykk - noe som gjør dem mer fleksible enn `mapping`. + +På et tidspunkt vil `mapping`-egenskapen bli fjernet, men når det skjer vil verktøy bli gitt for å migrere eksisterende konfigurasjoner til å bruke `queryParameters` i stedet. +{{% /notice%}} + +Man kan legge til dynamiske parametre ved å sette opp `mapping` på den aktuelle komponenten: + +```json {hl_lines=["12-14"]} +{ + "id": "dropdown-komponent", + "type": "Dropdown", + "textResourceBindings": { + "title": "NyGarantiLoyvetype" + }, + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype" + }, + "required": true, + "optionsId": "loyvetyper", + "mapping": { + "soknad.transportorOrgnummer": "orgnummer" + } +} +``` + +I eksempelet over vil parameteren `orgnummer={nr}` bli sendt med. `{nr}` er verdien på feltet `soknad.transportorOrgnummer`. +Om man setter opp en kobling til et datafelt og dette feltet endrer seg, så vil appen hente kodelisten på nytt. På denne måten kan man dynamisk styre hvilke valg som vises basert på informasjon gitt av sluttbruker. + +Å sende med parametre fra repeterende grupper gjøres ved å legge ved en indeks-indikator for de relevante gruppene. Eksempel: + +```json {hl_lines=[13]} +{ + "id": "dropdown-group", + "type": "Dropdown", + "textResourceBindings": { + "title": "Select city" + }, + "dataModelBindings": { + "simpleBinding": "Group.City" + }, + "required": true, + "optionsId": "cities", + "mapping": { + "Group[{0}].Country": "country" + } +} +``` + +For nøstede repeterende grupper vil man følge det samme mønsteret, men med en ekstra indikator for den nøstede gruppa: + +```json {hl_lines=[13]} +{ + "id": "dropdown-nested-group", + "type": "Dropdown", + "textResourceBindings": { + "title": "Select city" + }, + "dataModelBindings": { + "simpleBinding": "Group.SubGroup.City" + }, + "required": true, + "optionsId": "cities", + "mapping": { + "Group[{0}].SubGroup[{1}].Country": "country" + } +} +``` + +For et komplett eksempel kan du se vår [demo app.](https://altinn.studio/repos/ttd/dynamic-options-rep) + + +## Lurt å tenke på + +- Metoden `GetAppOptionsAsync` får inn en språkkode i parameteren `language`. Språkkoder bør baseres på ISO 639-1 standarden eller W3C IANA Language Subtag Registry standarden. Sistnevnte bygger på ISO 639-1 standarden men garanterer at alle kodene er unike, noe ISO 639-1 ikke gjør. +- En app kan ha mange implementasjoner av disse interfacene, en for hver kodeliste. Den rette implementasjonen finnes gjennom å se på hvilken kodeliste-identifikator det spørres etter, og sammenlignes med `Id`-egenskapen i implementasjonen. Dette er også identifikatoren som brukes i `optionsId`-egenskapen i komponentkonfigurasjonen. Dermed må også `Id`-egenskapen i implementasjonen være unik per app. +- Det kan være fristende å sette opp en dynamisk og sikret kodeliste hvor man henter ut data fra datamodellen og produserer kodelisten basert på dette. Dette er ikke anbefalt, da appens frontend bare henter kodelisten en gang hvor hvert unike sett med spørringsparametre. Det betyr at visningen av kodelisten ikke vil oppdatere seg i tråd med endringene i datamodellen. + - Et alternativ er å bruke funksjonaliteten for [dynamiske kodelister basert på datamodell](../from-data-model), i noen tilfeller sammen med tilsvarende kode i [DataProcessor](../../../../../reference/logic/dataprocessing). + - Et annet alternativ kan være å bruke spørringsparametre, som beskrevet over. +- Dersom man bruker spørringsparametre, kan det være lurt å tenke gjennom hvor mange unike kombinasjoner av parametre som typisk vil bli brukt i appen. Hvis det er mange, kan det være lurt å vurdere å bruke en annen tilnærming, som for eksempel å hente ut all data og filtrere gyldige verdier i frontend ved hjelp av [`optionFilter`](../../functionality/filtering). Mange ulike kombinasjoner av spørringsparametre kan før til at appen må gjøre mye unødvendig arbeid for å hente nye kodeliste-verdier hver gang brukeren gjør en endring i skjemaet. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/sources/from-data-model/_index.en.md b/content/altinn-studio/guides/development/options/sources/from-data-model/_index.en.md new file mode 100644 index 00000000000..4a0f480632f --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/from-data-model/_index.en.md @@ -0,0 +1,92 @@ +--- +title: Options from repeating structures +linktitle: From data model +description: Options made from a repeating structure in the data model +weight: 150 +aliases: + - /altinn-studio/guides/development/options/repeating-group-codelists +--- + +In the previous section about [dynamic options](../dynamic) we covered how to write code on the backend to generate dynamic options for a component. You could use pass certain values from the data model to the backend to generate those options (via [query parameters](../dynamic#query-parameters)), but that approach scales poorly when the query parameters would end up changing the options frequently, i.e. when the options are functionally unique for some set of data in the data model. + +Another approach is to set up options based on a 'repeating group' in the data model. Such a repeating object in the data model could also represent a list of options for a dropdown, radio buttons, or checkboxes. This is especially useful combined with the [RepeatingGroup](../../../../../reference/ux/fields/grouping/repeating) component, as it allows the user to add and remove items from the list, and the options will automatically update. + +This functionality does not require the use of any `RepeatingGroup` component in the form layout, but it does require that the data model contains a repeating structure. + +### Configuration + +To set up options derived from the data model, use the `source` property in your component configuration. +This property contains the fields `group`, `label`, and `value`. Example: + +```json {hl_lines=["5-9"]} +{ + "id": "dropdown-component-id", + "type": "Dropdown", + ... + "source": { + "group": "some.group", + "label": "dropdown.label", + "value": "some.group[{0}].someField" + } +} +``` + +Explanation: + +- **group** - the repeating group field in the data model to base the options on +- **label** - a reference to a text id to be used as the label for each option, see more below. +- **value** - a reference to a field in the group that should be used as the option value. Notice that we set up a placeholder `[{0}]` that will be replaced with the index of the repeating element. + +The **value** field must be unique for each element. If the repeating group does not contain a field which is unique for each item it is recommended to add a field to the data model that can be used as identifier, for instance a GUID. Non-unique values will be filtered out from all option lists, so not choosing a unique value can make it seem like the options are not being correctly populated. + +As for the **label** property, you have to define a text resource that can be used as a label for each option. +In the example below, other values from the repeating structure is used in the label via [variables in text](/altinn-studio/reference/ux/texts): + +```json +{ + "language": "nb", + "resources": [ + { + "id": "dropdown.label", + "value": "Person: {0}, Age: {1}", + "variables": [ + { + "key": "some.group[{0}].name", + "dataSource": "dataModel.default" + }, + { + "key": "some.group[{0}].age", + "dataSource": "dataModel.default" + } + ] + } + ] +} +``` + +### Expression support + +The properties `label`, `description`, and `helpText` can also be [dynamic expressions](../../../dynamics) in this mode. + +```json {hl_lines=["9-14"]} +{ + "id": "checkboxes-component-id", + "type": "Checkboxes", + ... + "source": { + "group": "some.group", + "label": "checkboxes.label", + "description": "checkboxes.description", + "helpText": [ + "if", ["equals", ["dataModel.someField"], "someValue"], + "checkboxes.helpText1", + "else", + "checkboxes.helpText2" + ], + "value": "some.group[{0}].someField" + } +``` + +In the example above, the `helpText` property is set up to show different help texts based on the value +of `someField` in the data model. If `someField` is equal to `someValue`, the help text will +be `checkboxes.helpText1`, otherwise it will be `checkboxes.helpText2`. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/sources/from-data-model/_index.nb.md b/content/altinn-studio/guides/development/options/sources/from-data-model/_index.nb.md new file mode 100644 index 00000000000..d25de60ecd5 --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/from-data-model/_index.nb.md @@ -0,0 +1,92 @@ +--- +title: Svaralternativer fra repeterede strukturer +linktitle: Fra datamodellen +description: Svaralternativer hentet fra en repeterende struktur i datamodellen +weight: 150 +aliases: + - /nb/altinn-studio/guides/development/options/repeating-group-codelists +--- + +I den forrige seksjonen om [dynamiske svaralternativer](../dynamic) beskrev vi hvordan man kan skrive kode på backend for å generere dynamiske svaralternativer for en komponent. Du kunne også sende visse verdier fra datamodellen til backend for å generere disse alternativene (via [spørringsparametre](../dynamic#spørringsparametre)). Denne fremgangsmåten skalerer dårlig når spørringsparametrene ender opp med å endre alternativene ofte, dvs. når alternativene er funksjonelt unike for en del av dataene i datamodellen. + +En annen tilnærming er å sette opp svaralternativer basert på en 'repeterende gruppe' i datamodellen. En slik repeterende struktur i datamodellen kan også representere en liste over alternativer for en nedtrekksliste, radioknapper eller avmerkingsbokser. Dette er spesielt nyttig i kombinasjon med [RepeatingGroup](../../../../../reference/ux/fields/grouping/repeating) komponenten, da det lar brukeren legge til og fjerne elementer fra listen, og alternativene vil automatisk oppdateres. + +Denne funksjonaliteten krever ikke bruk av noen `RepeatingGroup` komponent i skjemalayout, men det krever at datamodellen inneholder en repeterende struktur. + +### Konfigurasjon + +For å sette opp svaralternativer som hentes ut fra datamodellen brukes egenskapen `source`. +I dette objektet definerer man feltene `group`, `label` og `value`. Eksempel: + +```json {hl_lines=["5-9"]} +{ + "id": "dropdown-component-id", + "type": "Dropdown", + ... + "source": { + "group": "some.group", + "label": "dropdown.label", + "value": "some.group[{0}].someField" + } +} +``` + +Forklaring: + +- **group** - den repeterende strukturen i datamodellen man baserer svaralternativene på. +- **label** - en referanse til en tekstnøkkel som brukes som ledetekst for hvet svaralternativ. Se mer under. +- **value** - en referanse til det feltet i den repeterende strukturen som skal bruke som verdi, og dermed lagres når brukeren gjør et valg. Legg merke til at vi har fyllt inn `[{0}]` som vil bli erstattet med indeksen til det repeterende elementet. + + +Verdien hentet ut fra **value** må være unikt for hvert repeterende element. Om man ikke har et felt som er unikt per rad, anbefales det å legge på et ekstra felt i datamodellen som kan benyttes som identifikator f.eks en GUID eller liknende. Dersom verdien ikke er unik vil den bli filtrert bort fra alle svaralternativlister, og antallet svaralternativer tilgjengelige for brukeren kan da være noe lavere enn forventet ut fra datamodellen. + +For **label** feltet må vi definere en tekstressurs som kan bli brukt som ledetekst for hvert svaralternativ. +I eksempelet under, brukes andre verdier fra den repeterende strukturen i ledeteksten via [variabler i tekst](/nb/altinn-studio/reference/ux/texts): + +```json +{ + "language": "nb", + "resources": [ + { + "id": "dropdown.label", + "value": "Person: {0}, Age: {1}", + "variables": [ + { + "key": "some.group[{0}].name", + "dataSource": "dataModel.default" + }, + { + "key": "some.group[{0}].age", + "dataSource": "dataModel.default" + } + ] + } + ] +} +``` + +### Støtte for uttrykk + +Egenskapene `label`, `description` og `helpText` støtter også [dynamiske uttrykk](../../../dynamics) i denne modusen. + +```json {hl_lines=["9-14"]} +{ + "id": "checkboxes-component-id", + "type": "Checkboxes", + ... + "source": { + "group": "some.group", + "label": "checkboxes.label", + "description": "checkboxes.description", + "helpText": [ + "if", ["equals", ["dataModel.someField"], "someValue"], + "checkboxes.helpText1", + "else", + "checkboxes.helpText2" + ], + "value": "some.group[{0}].someField" + } +``` + +I eksempelet over er `helpText` satt opp til å vise forskjellige hjelpetekster basert på verdien av `someField` i datamodellen. +Hvis `someField` er lik `someValue`, vil hjelpeteksten være `checkboxes.helpText1`, ellers vil den være `checkboxes.helpText2`. diff --git a/content/altinn-studio/guides/development/options/common-codelists/_index.en.md b/content/altinn-studio/guides/development/options/sources/shared/_index.en.md similarity index 91% rename from content/altinn-studio/guides/development/options/common-codelists/_index.en.md rename to content/altinn-studio/guides/development/options/sources/shared/_index.en.md index abfc8352c67..28f9bff468b 100644 --- a/content/altinn-studio/guides/development/options/common-codelists/_index.en.md +++ b/content/altinn-studio/guides/development/options/sources/shared/_index.en.md @@ -1,15 +1,13 @@ --- -title: Common shared code lists -linktitle: Common shared code list -description: How to re-use common code lists shared across applications? +title: Common shared options +linktitle: Shared +description: Options that are shared across applications toc: false weight: 200 +aliases: + - /altinn-studio/guides/development/options/common-codelists --- -{{%notice info%}} -This functionality requires that the application uses at least [version 7.8.0](https://github.com/Altinn/app-lib-dotnet/releases/tag/v7.8.0) of the Altinn.App.Core NuGet package. -{{% /notice%}} - ## What are common standard code lists? Common standard code lists are lists such as countries, counties, municipalities, genders, marital statuses, etc. that can be used in an application without the need to maintain these code lists yourself. See the [complete list](https://github.com/Altinn/codelists-lib-dotnet#available-codelists) of available code lists. @@ -42,7 +40,7 @@ These code lists are created as a separate [NuGet package](https://www.nuget.org You can do this either using [Altinn Studio](https://altinn.studio) and configure the *code list ID* of your component in the user interface. - Or you can configure the component by editing the `optionsId` property in FormLayout.json according to the [documentation](/altinn-studio/guides/development/options/#connect-the-component-to-options-code-list). + Or you can configure the component by editing the `optionsId` property for the component in the layout file. ## Custom Configuration While the configuration mentioned above, where you use the method `services.AddAltinnCodelists();`, will register all available code lists with default values, there may be cases where you want to customize the configuration of a code list. The examples below will vary slightly depending on the source of the code list, as different sources offer different options. diff --git a/content/altinn-studio/guides/development/options/common-codelists/_index.nb.md b/content/altinn-studio/guides/development/options/sources/shared/_index.nb.md similarity index 89% rename from content/altinn-studio/guides/development/options/common-codelists/_index.nb.md rename to content/altinn-studio/guides/development/options/sources/shared/_index.nb.md index 59e550a02fb..29faa34945d 100644 --- a/content/altinn-studio/guides/development/options/common-codelists/_index.nb.md +++ b/content/altinn-studio/guides/development/options/sources/shared/_index.nb.md @@ -1,15 +1,13 @@ --- -title: Felles standard kodelister -linktitle: Felles standard kodelister -description: Hvordan bruke felles standard kodelister på tvers av applikasjoner i Altinn 3? +title: Delte standard kodelister +linktitle: Delte +description: Delte standard kodelister som kan brukes i flere applikasjoner toc: false weight: 200 +aliases: + - /nb/altinn-studio/guides/development/options/common-codelists --- -{{%notice info%}} -Denne funksjonaliteten krever at applikasjonen benytter minimum [versjon 7.8.0](https://github.com/Altinn/app-lib-dotnet/releases/tag/v7.8.0) av Altinn.App.Core nuget pakken. -{{% /notice%}} - ## Hva er felles standard kodelister? Felles standard kodelister er lister som land, fylker, kommuner, kjønn, sivilstatus etc som man kan benytte i sin applikasjon uten at man selv trenger å vedlikeholde disse kodelistene selv. Se [komplett liste](https://github.com/Altinn/codelists-lib-dotnet#available-codelists) over tilgjengelige kodelister. @@ -39,11 +37,11 @@ Denne funksjonaliteten krever at applikasjonen benytter minimum [versjon 7.8.0]( Ved å kalle denne metoden vil du registrere alle kodelistene på tvers av alle kilder. Du kan også registrere kodelistene én og én hvis du vil ha kontroll på hvilke kodelister som er tatt i bruk eller konfigurere og tilpasse oppsettet av kodelisten. ### 3. Koble applikasjonen din til kodeverket du ønsker å bruke - Se (dokuemntasjon)[https://github.com/Altinn/codelists-lib-dotnet#available-codelists] nedenfor for tilgjengelige kodelister. + Se [dokuemntasjon](https://github.com/Altinn/codelists-lib-dotnet#available-codelists) nedenfor for tilgjengelige kodelister. Du kan gjøre dette enten ved hjelp av [Altinn Studio](https://altinn.studio) og konfigurere *Kodeliste-ID* for komponenten din i brukergrensesnittet. - Eller du kan konfigurere komponenten ved å redigere egenskapen `optionsId` i FormLayout.json i henhold til [dokumentasjonen](/altinn-studio/guides/development/options/#connect-the-component-to-options-code-list). + Eller du kan konfigurere komponenten ved å redigere egenskapen `optionsId` form komponenten i layout-filen. ## Tilpasset konfigurasjon Mens konfigurasjonen nevnt ovenfor der du kaller `services.AddAltinnCodelists();` vil legge til alle tilgjengelige kodelister med standardverdier, kan det være tilfeller der du ønsker å tilpasse konfigurasjonen av en kodeliste. Eksemplene under vil variere noe avhengig av kilden til kodelisten siden de ulike kildene tilbyr ulike muligheter. diff --git a/content/altinn-studio/guides/development/options/sources/static/_index.en.md b/content/altinn-studio/guides/development/options/sources/static/_index.en.md new file mode 100644 index 00000000000..d9ea6041d48 --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/static/_index.en.md @@ -0,0 +1,88 @@ +--- +title: Static options +linktitle: Static +description: Lists of options that does not change, but can be filtered +toc: false +weight: 50 +aliases: + - /altinn-studio/guides/development/options/static-codelists +--- + +For simpler use-cases, a static code list is easy to configure. These can either be set directly in the component +configuration or in a json file in the application repository. Which method to use depends on the re-usability of +the code list. If multiple components should use the same code list, it is recommended to use the [json file method](#from-json-files). + +Note that even though a static code list can be completely static, it is also possible to make it (a bit more) dynamic +by [filtering the options](../../functionality/filtering) using expressions. If you want even more flexibility, +you can also [create your own code-based code list](../dynamic). + +## In component configuration + +In the example configuration, a Dropdown component is set up with a static code list. The `options` property is an +array of objects, where each object represents a code list item. The `value` property is the value that will be +saved in the data model when the user selects the item. The `label` property is the text that will be displayed to +the user. + +```json {hl_lines=["8-21"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "textResourceBindings": {}, + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype" + }, + "options": [ + { + "value": "1", + "label": "Type 1" + }, + { + "value": "2", + "label": "Type 2" + }, + { + "value": "3", + "label": "Type 3" + } + ] +} +``` + +## From JSON files + +By adding json based option files in the application repository, the application will automatically read the file and expose it through the options api. For this to work, the files must be placed in the `App/options/` folder and be named according to the following conventions `{optionId}.json` for the application to recognize them. + +For example if you have a list of countries in a file named `countries.json`, the optionId would be `countries`, and would be exposed through the api at `{org}/{app}/api/options/countries`. The static code lists should be in a special format as shown below: + +```json +[ + { + "value": "norway", + "label": "Norge" + }, + { + "value": "denmark", + "label": "Danmark" + }, + { + "value": "sweden", + "label": "country.label.sweden" + } +] +``` + +Note that the `label` field can be a key to a text resource (as shown above for sweden) or plain text. + +In order to reference this code list in a component, you can use the `optionsId` property in the component configuration: + +```json {hl_lines=["8"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "textResourceBindings": {}, + "dataModelBindings": { + "simpleBinding": "soknad.opphavsland" + }, + "optionsId": "countries" +} +``` \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/sources/static/_index.nb.md b/content/altinn-studio/guides/development/options/sources/static/_index.nb.md new file mode 100644 index 00000000000..bf8375fc79f --- /dev/null +++ b/content/altinn-studio/guides/development/options/sources/static/_index.nb.md @@ -0,0 +1,88 @@ +--- +title: Statiske svaralternativer +linktitle: Statisk +description: Svaralternativer som ikke endrer seg, men som kan filtreres +toc: false +weight: 50 +aliases: + - /nb/altinn-studio/guides/development/options/static-codelists +--- + +For enklere brukstilfeller er statiske kodelister det letteste å sette opp. Disse kan enten settes direkte +i komponentkonfigurasjonen eller i en json-fil i app-repositoriet. Hvilken metode som bør brukes avhenger av +gjenbruksbehovet til kodelisten. Hvis flere komponenter skal bruke samme kodeliste, anbefales det å +[putte kodelisten i en json-fil](#fra-json-filer). + +Legg merke til at selv om en slik kodeliste kan være helt statisk, er det også mulig å gjøre den (litt mer) dynamisk +ved å [filtrere svaralternativene](../../functionality/filtering) ved hjelp av e uttrykk. Ønsker du enda mer +fleksiilitet, kan du også [lage en egen kodebasert kodeliste](../dynamic). + +## I komponentkonfigurasjonen + +I denne eksempelkonfigurasjonen er en Dropdown-komponent satt opp med en statisk kodeliste. Egenskapen `options` er en +array av objekter, hvor hvert objekt representerer et kodelisteelement. Egenskapen `value` er verdien som vil bli +lagret i datamodellen når brukeren velger elementet. Egenskapen `label` er teksten som vil bli vist til brukeren. + +```json {hl_lines=["8-21"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "textResourceBindings": {}, + "dataModelBindings": { + "simpleBinding": "soknad.nyGaranti.loyvetype" + }, + "options": [ + { + "value": "1", + "label": "Type 1" + }, + { + "value": "2", + "label": "Type 2" + }, + { + "value": "3", + "label": "Type 3" + } + ] +} +``` + +## Fra JSON-filer + +Ved å legge json-lister i options mappen i app repo vil appen automatisk lese denne filen og eksponere det gjennom options-apiet. +Options filene må ligge under `App/options/` og vil bli differensiert ved hjelp av navngivningen på json-filen. F.eks `land.json`. Her vil da optionsId være `land`, og vil være eksponert gjennom endepunktet `{org}/{app}/api/options/land`. +Kodelistene må være på et spesifikt format. Eksempel på en kodeliste som inneholder land (`App/options/land.json`): + +```json +[ + { + "value": "norway", + "label": "Norge" + }, + { + "value": "denmark", + "label": "Danmark" + }, + { + "value": "sweden", + "label": "country.label.sweden" + } +] +``` + +`label` feltet kan inneholde en tekstnøkkel til teskstressursene eller ren tekst. + +For å referere til denne kodelisten i en komponent, kan du bruke egenskapen `optionsId` i komponentkonfigurasjonen: + +```json {hl_lines=["8"]} +{ + "id": "some-dropdown-component", + "type": "Dropdown", + "textResourceBindings": {}, + "dataModelBindings": { + "simpleBinding": "soknad.opphavsland" + }, + "optionsId": "land" +} +``` \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/static-codelists/_index.en.md b/content/altinn-studio/guides/development/options/static-codelists/_index.en.md deleted file mode 100644 index cdc2c69842e..00000000000 --- a/content/altinn-studio/guides/development/options/static-codelists/_index.en.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: Static code lists from the application repository -linktitle: Static code lists -description: How to configure static code lists from the application repository. -toc: false -weight: 50 ---- - -By adding json based option files in the application repository, the application will automatically read the file and expose it through the options api. For this to work, the files must be placed in the `App/options/` folder and be named according to the following conventions `{optionId}.json` for the application to recognize them. - -For example if you have a list of countries in a file named `countries.json`, the optionId would be `countries`, and would be exposed through the api at `{org}/{app}/api/options/countries`. The static code lists should be in a special format as shown below: - -```json -[ - { - "value": "norway", - "label": "Norge" - }, - { - "value": "denmark", - "label": "Danmark" - }, - { - "value": "sweden", - "label": "country.label.sweden" - } -] -``` - -Note that the `label` field can be a key to a text resource (as shown above for sweden) or plain text. \ No newline at end of file diff --git a/content/altinn-studio/guides/development/options/static-codelists/_index.nb.md b/content/altinn-studio/guides/development/options/static-codelists/_index.nb.md deleted file mode 100644 index 2f95579bee1..00000000000 --- a/content/altinn-studio/guides/development/options/static-codelists/_index.nb.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: Statiske kodelister fra applikasjon -linktitle: Statiske kodelister -description: Hvordan konfigurere statiske kodelister levert fra applikasjonen? -toc: false -weight: 50 ---- - -Ved å legge json-lister i options mappen i app repo vil appen automatisk lese denne filen og eksponere det gjennom options-apiet. -Options filene må ligge under `App/options/` og vil bli differensiert ved hjelp av navngivningen på json-filen. F.eks `land.json`. Her vil da optionsId være `land`, og vil være eksponert gjennom endepunktet `{org}/{app}/api/options/land`. -Kodelistene må være på et spesifikt format. Eksempel på en kodeliste som inneholder land (`App/options/land.json`): - -```json -[ - { - "value": "norway", - "label": "Norge" - }, - { - "value": "denmark", - "label": "Danmark" - }, - { - "value": "sweden", - "label": "country.label.sweden" - } -] -``` - -`label` feltet kan inneholde en tekstnøkkel til teskstressursene eller ren tekst. diff --git a/content/altinn-studio/reference/logic/expressions/_index.en.md b/content/altinn-studio/reference/logic/expressions/_index.en.md index 786daa74815..c620df63ae1 100644 --- a/content/altinn-studio/reference/logic/expressions/_index.en.md +++ b/content/altinn-studio/reference/logic/expressions/_index.en.md @@ -146,22 +146,23 @@ And for a person who is 15 years old (or younger, such as a 4-year-old), the tex Dynamic expressions are currently available for use in these properties, as defined in [layout files](../../ux/pages). -| Components | Property | Expected Value | Frontend | Backend | -| -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | -------------------------- | -------- | ------- | -| [Pages/layouts](#showhide-entire-pages) | `hidden` | [Boolean](#boolean-values) | ✅ | ✅ | -| All | `hidden` | [Boolean](#boolean-values) | ✅ | ✅ | -| Form components | `required` | [Boolean](#boolean-values) | ✅ | ✅ | -| Form components | `readOnly` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `hiddenRow` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.addButton` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.saveButton` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.deleteButton` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.alertOnDelete` | [Boolean](#boolean-values) | ✅ | ❌ | -| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.saveAndNextButton` | [Boolean](#boolean-values) | ✅ | ❌ | -| [RadioButtons](../../ux/components/radiobuttons), [Checkboxes](../../ux/components/checkbox), [Dropdown](../../ux/components/dropdown) | `source.label` | [String](#strings) | ✅ | ❌ | -| [RadioButtons](../../ux/components/radiobuttons), [Checkboxes](../../ux/components/checkbox), [Dropdown](../../ux/components/dropdown) | `source.description` | [String](#strings) | ✅ | ❌ | -| [RadioButtons](../../ux/components/radiobuttons), [Checkboxes](../../ux/components/checkbox), [Dropdown](../../ux/components/dropdown) | `source.helpText` | [String](#strings) | ✅ | ❌ | -| All | `textResourceBindings.[*]` \* | [String](#strings) | ✅ | ❌ | +| Components | Property | Expected Value | Frontend | Backend | +|------------------------------------------------------------------------------------------------------------|-------------------------------|----------------------------|----------|---------| +| [Pages/layouts](#showhide-entire-pages) | `hidden` | [Boolean](#boolean-values) | ✅ | ✅ | +| All | `hidden` | [Boolean](#boolean-values) | ✅ | ✅ | +| Form components | `required` | [Boolean](#boolean-values) | ✅ | ✅ | +| Form components | `readOnly` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `hiddenRow` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.addButton` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.saveButton` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.deleteButton` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.alertOnDelete` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Repeating groups](../../ux/fields/grouping/repeating) | `edit.saveAndNextButton` | [Boolean](#boolean-values) | ✅ | ❌ | +| [Option based components](../../../guides/development/options/sources/from-data-model/#expression-support) | `source.label` | [String](#strings) | ✅ | ❌ | +| [Option based components](../../../guides/development/options/sources/from-data-model/#expression-support) | `source.description` | [String](#strings) | ✅ | ❌ | +| [Option based components](../../../guides/development/options/sources/from-data-model/#expression-support) | `source.helpText` | [String](#strings) | ✅ | ❌ | +| [Option based components](../../../guides/development/options/sources/dynamic/#based-on-expressions) | `queryParameters.[*]` | [String](#strings) | ✅ | ❌ | +| All | `textResourceBindings.[*]` \* | [String](#strings) | ✅ | ❌ | \* = The values that can be overridden with textResourceBindings vary from component to component, but will work wherever used. For repeating groups, you can find [more information here](../../ux/fields/grouping/repeating#textresourcebindings) @@ -260,38 +261,40 @@ Here we find the closest `age` component `age-1`, which is _36_, Kari's age. These functions are available for use in expressions: -| Function Name | Parameters | Return Value | Frontend | Backend | -|----------------------------------------------|--------------------------------------------------------------------------------------------------| ------------------------------------ | -------- | ------- | -| [`equals`](#func-equals) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`notEquals`](#func-equals) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`not`](#func-not) | [Boolean](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`greaterThan`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`greaterThanEq`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`lessThan`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`lessThanEq`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`concat`](#func-concat) | None or multiple [strings](#strings) | [String](#strings) | ✅ | ✅ | -| [`and`](#func-and) | One or more [boolean values](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`or`](#func-and) | One or more [boolean values](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`if`](#func-if) | [See detailed description](#func-if) | [See detailed description](#func-if) | ✅ | ✅ | -| [`contains`](#func-contains-not-contains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`notContains`](#func-contains-not-contains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`commaContains`](#func-commaContains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`startsWith`](#func-starts-ends-with) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`endsWith`](#func-starts-ends-with) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | -| [`lowerCase`](#func-lowerCase-upperCase) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`upperCase`](#func-lowerCase-upperCase) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`stringLength`](#func-stringLength) | [String](#strings) | [Number](#numbers) | ✅ | ✅ | -| [`text`](#func-text) | [String](#strings) | [String](#strings) | ✅ | ❌ | -| [`language`](#func-language) | None | [String](#strings) | ✅ | ❌ | -| [`displayValue`](#func-displayValue) | [String](#strings) | [String](#strings) | ✅ | ❌ | -| [`round`](#func-round) | [Number](#numbers), optional [Number](#numbers) | [String](#strings) | ✅ | ✅ | -| [`instanceContext`](#func-instancecontext) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`frontendSettings`](#func-frontendsettings) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`dataModel`](#func-datamodel) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`component`](#func-component) | [String](#strings) | [String](#strings) | ✅ | ✅ | -| [`formatDate`](#func-formatDate) | [String](#strings), optional [String](#strings) | [String](#strings) | ✅ | ❌ | -| [`linkToPage`](#func-linkToPage) | [String](#strings), [String](#strings) | [String](#strings) | ✅ | ❌ | -| [`linkToComponent`](#func-linkToComponent) | [String](#strings), [String](#strings) | [String](#strings) | ✅ | ❌ | +| Function Name | Parameters | Return Value | Frontend | Backend | +|----------------------------------------------|-------------------------------------------------|--------------------------------------|----------|---------| +| [`equals`](#func-equals) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`notEquals`](#func-equals) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`not`](#func-not) | [Boolean](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`greaterThan`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`greaterThanEq`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`lessThan`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`lessThanEq`](#func-gt) | [Number](#numbers), [Number](#numbers) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`concat`](#func-concat) | None or multiple [strings](#strings) | [String](#strings) | ✅ | ✅ | +| [`and`](#func-and) | One or more [boolean values](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`or`](#func-and) | One or more [boolean values](#boolean-values) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`if`](#func-if) | [See detailed description](#func-if) | [See detailed description](#func-if) | ✅ | ✅ | +| [`contains`](#func-contains-not-contains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`notContains`](#func-contains-not-contains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`commaContains`](#func-commaContains) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`startsWith`](#func-starts-ends-with) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`endsWith`](#func-starts-ends-with) | [String](#strings), [String](#strings) | [Boolean](#boolean-values) | ✅ | ✅ | +| [`lowerCase`](#func-lowerCase-upperCase) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`upperCase`](#func-lowerCase-upperCase) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`stringLength`](#func-stringLength) | [String](#strings) | [Number](#numbers) | ✅ | ✅ | +| [`text`](#func-text) | [String](#strings) | [String](#strings) | ✅ | ❌ | +| [`language`](#func-language) | None | [String](#strings) | ✅ | ❌ | +| [`displayValue`](#func-displayValue) | [String](#strings) | [String](#strings) | ✅ | ❌ | +| [`round`](#func-round) | [Number](#numbers), optional [Number](#numbers) | [String](#strings) | ✅ | ✅ | +| [`instanceContext`](#func-instancecontext) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`frontendSettings`](#func-frontendsettings) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`dataModel`](#func-datamodel) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`component`](#func-component) | [String](#strings) | [String](#strings) | ✅ | ✅ | +| [`formatDate`](#func-formatDate) | [String](#strings), optional [String](#strings) | [String](#strings) | ✅ | ❌ | +| [`linkToPage`](#func-linkToPage) | [String](#strings), [String](#strings) | [String](#strings) | ✅ | ❌ | +| [`linkToComponent`](#func-linkToComponent) | [String](#strings), [String](#strings) | [String](#strings) | ✅ | ❌ | +| [`argv`](#func-argv) | [Number](#numbers) | [String](#strings) | ✅ | ✅ | +| [`value`](#func-value) | optional [String](#strings) | [String](#strings) | ✅ | ❌ | Detailed descriptions and examples @@ -833,6 +836,38 @@ When clicked, this link will take the user to the page the component is on and f {{% /expandlarge %}} +{{% expandlarge id="func-argv" header="argv" %}} +The `argv` function can be used to retrieve arguments sent to the expression. This is currently only available for +[expression-based validation](../validation/expression-validation). + +The function takes 1 argument, which is the index of the argument you want to retrieve. The index starts at 0. + +```json +["argv", 0] +``` +{{% /expandlarge %}} + +{{% expandlarge id="func-value" header="value" %}} +Like `argv`, the `value` function is also a method for retrieving arguments sent to the expression. The function can be used +without arguments to retrieve a value, or with an argument to retrieve other types of values. This is currently available +for [filtering of options](../../../guides/development/options/functionality/filtering) and will soon be available +as an alternative to `argv` in [expression-based validation](../validation/expression-validation). + +```json +["value"] +``` + +This expression retrieves the value of the option (if used in filtering options). In other contexts, this expression currently +returns an error message. + +```json +["value", "label"] +``` + +The expression above retrieves the text of the option (if used in filtering options). In other contexts, this expression +returns an error message. +{{% /expandlarge %}} + ## Data Types Expressions in the functions expect that the arguments sent in have a specific type. If an argument sent in has a diff --git a/content/altinn-studio/reference/logic/expressions/_index.nb.md b/content/altinn-studio/reference/logic/expressions/_index.nb.md index 58442a76a6e..7fe66286e69 100644 --- a/content/altinn-studio/reference/logic/expressions/_index.nb.md +++ b/content/altinn-studio/reference/logic/expressions/_index.nb.md @@ -141,22 +141,23 @@ Og for en person som er 15 år (eller yngre, som f.eks. en 4-åring), returneres Dynamiske uttrykk er foreløpig tilgjengelig for bruk i disse egenskapene, som definert i [layout-filer](../../ux/pages). -| Komponenter | Egenskap | Forventet verdi | Frontend | Backend | -| --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | -------------------------- | -------- | ------- | -| [Sider/layouts](#viseskjule-hele-sider) | `hidden` | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| Alle | `hidden` | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| Skjemakomponenter | `required` | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| Skjemakomponenter | `readOnly` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `hiddenRow` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.addButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.saveButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.deleteButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.alertOnDelete` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.saveAndNextButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | -| [Radioknapper](../../ux/components/radiobuttons), [Avkrysningsbokser](../../ux/components/checkbox), [Nedtrekksliste](../../ux/components/dropdown) | `source.label` | [Streng](#strenger) | ✅ | ❌ | -| [Radioknapper](../../ux/components/radiobuttons), [Avkrysningsbokser](../../ux/components/checkbox), [Nedtrekksliste](../../ux/components/dropdown) | `source.description` | [Streng](#strenger) | ✅ | ❌ | -| [Radioknapper](../../ux/components/radiobuttons), [Avkrysningsbokser](../../ux/components/checkbox), [Nedtrekksliste](../../ux/components/dropdown) | `source.helpText` | [Streng](#strenger) | ✅ | ❌ | -| Alle | `textResourceBindings.[*]` \* | [Streng](#strenger) | ✅ | ❌ | +| Komponenter | Egenskap | Forventet verdi | Frontend | Backend | +|-----------------------------------------------------------------------------------------------------------------|-------------------------------|----------------------------|----------|---------| +| [Sider/layouts](#viseskjule-hele-sider) | `hidden` | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| Alle | `hidden` | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| Skjemakomponenter | `required` | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| Skjemakomponenter | `readOnly` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `hiddenRow` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.addButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.saveButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.deleteButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.alertOnDelete` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Repeterende grupper](../../ux/fields/grouping/repeating) | `edit.saveAndNextButton` | [Boolsk](#boolske-verdier) | ✅ | ❌ | +| [Kodelistebaserte komponenter](../../../guides/development/options/sources/from-data-model/#støtte-for-uttrykk) | `source.label` | [Streng](#strenger) | ✅ | ❌ | +| [Kodelistebaserte komponenter](../../../guides/development/options/sources/from-data-model/#støtte-for-uttrykk) | `source.description` | [Streng](#strenger) | ✅ | ❌ | +| [Kodelistebaserte komponenter](../../../guides/development/options/sources/from-data-model/#støtte-for-uttrykk) | `source.helpText` | [Streng](#strenger) | ✅ | ❌ | +| [Kodelistebaserte komponenter](../../../guides/development/options/sources/dynamic/#basert-på-uttrykk) | `queryParameters.[*]` | [Streng](#strenger) | ✅ | ❌ | +| Alle | `textResourceBindings.[*]` \* | [Streng](#strenger) | ✅ | ❌ | \* = Hvilke verdier man kan overstyre med textResourceBindings varierer fra komponent til komponent, men vil fungere på alle steder der det brukes. TextResourceBindings for repeterende grupper finner @@ -258,37 +259,39 @@ Hva vil resultatet bli i de forskjellige eksemplene? Her er svarene: Disse funksjonene er tilgjengelige for bruk i uttrykk: | Funksjonsnavn | Parametre | Returverdi | Frontend | Backend | -| -------------------------------------------- | -------------------------------------------------- | ------------------------------------ | -------- | ------- | -| [`equals`](#func-equals) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`notEquals`](#func-equals) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`not`](#func-not) | [Boolsk](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`greaterThan`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`greaterThanEq`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`lessThan`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`lessThanEq`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`concat`](#func-concat) | Ingen eller flere [strenger](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`and`](#func-and) | En eller flere [boolske verdier](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`or`](#func-and) | En eller flere [boolske verdier](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`if`](#func-if) | [Se detaljert beskrivelse](#func-if) | [Se detaljert beskrivelse](#func-if) | ✅ | ✅ | -| [`contains`](#func-contains-not-contains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`notContains`](#func-contains-not-contains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`commaContains`](#func-commaContains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`startsWith`](#func-starts-ends-with) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`endsWith`](#func-starts-ends-with) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | -| [`lowerCase`](#func-lowerCase-upperCase) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`upperCase`](#func-lowerCase-upperCase) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`stringLength`](#func-stringLength) | [Streng](#strenger) | [Tall](#tall) | ✅ | ✅ | -| [`text`](#func-text) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | -| [`language`](#func-language) | Ingenting | [Streng](#strenger) | ✅ | ❌ | -| [`displayValue`](#func-displayValue) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | -| [`round`](#func-round) | [Tall](#tall), valgfritt [Tall](#tall) | [Streng](#strenger) | ✅ | ✅ | -| [`instanceContext`](#func-instancecontext) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`frontendSettings`](#func-frontendsettings) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`dataModel`](#func-datamodel) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`component`](#func-component) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | -| [`formatDate`](#func-formatDate) | [Streng](#strenger), valgfri [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | -| [`linkToPage`](#func-linkToPage) | [Streng](#strenger), [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | -| [`linkToComponent`](#func-linkToComponent) | [Streng](#strenger), [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +|----------------------------------------------|----------------------------------------------------|--------------------------------------|----------|---------| +| [`equals`](#func-equals) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`notEquals`](#func-equals) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`not`](#func-not) | [Boolsk](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`greaterThan`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`greaterThanEq`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`lessThan`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`lessThanEq`](#func-gt) | [Tall](#tall), [Tall](#tall) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`concat`](#func-concat) | Ingen eller flere [strenger](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`and`](#func-and) | En eller flere [boolske verdier](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`or`](#func-and) | En eller flere [boolske verdier](#boolske-verdier) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`if`](#func-if) | [Se detaljert beskrivelse](#func-if) | [Se detaljert beskrivelse](#func-if) | ✅ | ✅ | +| [`contains`](#func-contains-not-contains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`notContains`](#func-contains-not-contains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`commaContains`](#func-commaContains) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`startsWith`](#func-starts-ends-with) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`endsWith`](#func-starts-ends-with) | [Streng](#strenger), [Streng](#strenger) | [Boolsk](#boolske-verdier) | ✅ | ✅ | +| [`lowerCase`](#func-lowerCase-upperCase) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`upperCase`](#func-lowerCase-upperCase) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`stringLength`](#func-stringLength) | [Streng](#strenger) | [Tall](#tall) | ✅ | ✅ | +| [`text`](#func-text) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +| [`language`](#func-language) | Ingenting | [Streng](#strenger) | ✅ | ❌ | +| [`displayValue`](#func-displayValue) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +| [`round`](#func-round) | [Tall](#tall), valgfritt [Tall](#tall) | [Streng](#strenger) | ✅ | ✅ | +| [`instanceContext`](#func-instancecontext) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`frontendSettings`](#func-frontendsettings) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`dataModel`](#func-datamodel) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`component`](#func-component) | [Streng](#strenger) | [Streng](#strenger) | ✅ | ✅ | +| [`formatDate`](#func-formatDate) | [Streng](#strenger), valgfri [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +| [`linkToPage`](#func-linkToPage) | [Streng](#strenger), [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +| [`linkToComponent`](#func-linkToComponent) | [Streng](#strenger), [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | +| [`argv`](#func-argv) | [Tall](#tall) | [Streng](#strenger) | ✅ | ✅ | +| [`value`](#func-value) | valgfri [Streng](#strenger) | [Streng](#strenger) | ✅ | ❌ | Detaljerte beskrivelser og eksempler @@ -825,6 +828,39 @@ Resultatet vil bli `}} **Documentation for code lists** -- [Link a Component to a Code List](/altinn-studio/guides/development/options/#connect-the-component-to-options-code-list) -- [Static Code Lists](/altinn-studio/guides/development/options/static-codelists/) -- [Dynamic Code Lists](/altinn-studio/guides/development/options/dynamic-codelists/) -- [Code lists based on repeating groups from the data model](/altinn-studio/guides/development/options/repeating-group-codelists/) \ No newline at end of file +- [Link a Component to a Code List](/altinn-studio/guides/development/options/) +- [Static Code Lists](/altinn-studio/guides/development/options/sources/static/) +- [Dynamic Code Lists](/altinn-studio/guides/development/options/sources/dynamic/) +- [Code lists based on repeating groups from the data model](/altinn-studio/guides/development/options/sources/from-data-model/) \ No newline at end of file diff --git a/content/altinn-studio/reference/ux/components/_common-props-content/options-page.nb.md b/content/altinn-studio/reference/ux/components/_common-props-content/options-page.nb.md index 6a2597ad86c..cfc0ce57964 100644 --- a/content/altinn-studio/reference/ux/components/_common-props-content/options-page.nb.md +++ b/content/altinn-studio/reference/ux/components/_common-props-content/options-page.nb.md @@ -13,7 +13,7 @@ Alternativer (options) kan legges til manuelt eller ved hjelp av [kodelister](/n {{< property-docs prop="source" >}} **Dokumentasjon for kodelister** -- [Koble en komponent til kodeliste](/nb/altinn-studio/guides/development/options/#koble-en-komponent-til-en-kodeliste) -- [Statiske kodelister](/nb/altinn-studio/guides/development/options/static-codelists/) -- [Dynamiske kodelister](/nb/altinn-studio/guides/development/options/dynamic-codelists/) -- [Dynamiske kodelister fra repeterede grupper i datamodellen](/nb/altinn-studio/guides/development/options/repeating-group-codelists/) \ No newline at end of file +- [Koble en komponent til kodeliste](/nb/altinn-studio/guides/development/options/) +- [Statiske kodelister](/nb/altinn-studio/guides/development/options/sources/static/) +- [Dynamiske kodelister](/nb/altinn-studio/guides/development/options/sources/dynamic/) +- [Dynamiske kodelister fra repeterede grupper i datamodellen](/nb/altinn-studio/guides/development/options/sources/from-data-model/) \ No newline at end of file diff --git a/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.en.md b/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.en.md index 22d0093a3ae..24933ddd915 100644 --- a/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.en.md +++ b/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.en.md @@ -19,7 +19,7 @@ To add options from a code list, select "Kodeliste" and enter a code list ID.
-If you wish to [secure dynamic code lists](/altinn-studio/guides/development/options/dynamic-codelists/#secured-dynamic-options), you can check this option: +If you wish to [secure dynamic code lists](/altinn-studio/guides/development/options/sources/dynamic/#secured-options), you can check this option: {{% image file="component-settings/secure.png" %}} diff --git a/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.nb.md b/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.nb.md index bced1ee81c8..2e048584fbc 100644 --- a/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.nb.md +++ b/content/altinn-studio/reference/ux/components/_common-props-content/optionsId.nb.md @@ -19,7 +19,7 @@ For å legge til alternativer fra en kodeliste, velg 'Kodeliste' og angi en kode
-Om du ønsker å [sikre dynamiske kodelister](/nb/altinn-studio/guides/development/options/dynamic-codelists/#sikrede-dynamiske-kodelister) kan du huke av for dette: +Om du ønsker å [sikre dynamiske kodelister](/nb/altinn-studio/guides/development/options/sources/dynamic/#sikrede-kodelister) kan du huke av for dette: {{% image file="component-settings/secure.png" %}}