Modul 6
Utvidelse av skjema med repeterende gruppe
I denne modulen skal du utvide applikasjonen du har laget i foregående moduler for å støtte mer av funksjonaliteten som Sogndal kommune ønsker.
Temaer som dekkes i denne modulen:
- Repeterende grupper
- Validering
- Dataprosessering
Oppgaver
Krav fra kommunen
For å kunne skreddersy et best mulig tilbud til nye innflyttere ønsker vi oss en oversikt over tidligere bosteder til innflytteren.
På datasiden ønsker vi at det legges opp til at brukeren kan fylle inn tidligere bosteder. Tidligere bosteder skal inneholde følgende felter:
- Gateadresse
- Postnummer
- Poststed
Det skal være mulig å legge inn opptil 10 tidligere bosteder.
Oppgaver
- Legg til en gruppekomponent på siden som samler inn personalia.
- Legg til en adressekomponent under gruppekomponenten.
- For begge komponentene, legg til passende ledetekst og knytt de til relevante felter i datamodellen.
Merk at “Maksimalt antall repetisjoner” må endres lokalt.
Nyttig dokumentasjon
Forståelsessjekk
Krav fra kommunen
Dersom innflytter fyller inn postnummer 1337
som et av tidligere bosteder må vedkommende
bekrefte sin uovertruffenhet ved å legge til et symbol i adressefeltet før de kan gå videre.
Vi ønsker derfor at det skal dukke opp en feilmelding på det aktuelle feltet med følgende tekst:
Vi er beæret over å motta en '1337' innbygger til Sogndal kommune!
Du må imidlertid bekrefte din uovertruffenhet ved å legge til en 🌟 i adressefeltet for å gå videre.
Oppgaver
- Legg til en validering på feltet
Postnr
for tidligere bostedsadresser.
Nyttig dokumentasjon
Forståelsessjekk
Krav fra kommunen
En av kommunens databehandlere har sett seg lei av å manuelt rette opp i en gateadresse som ofte blir skrevet feil av innflyttere. Vi ønsker derfor å programmatisk fikse opp i dette under utfyllingen av appen.
Om sluttbruker fyller inn Sesame Street 1
i feltet Innflytter.Adresse.Gateadresse
skal dette automatisk rettes til Sesamsgate 1
.
I alle andre tilfeller skal feltet forbli urørt.
Oppgaver
- Opprett en fil for dataprosessering.
- Legg til prosessering av adressefeltet som beskrevet over.
Husk å implementere løsningen i Program.cs
som tidligere.
Nyttig dokumentasjon
Forståelsessjekk
ProcessDataWrite
kjøres i det brukeren skriver data, altså når brukeren har fylt inn et felt eller oppdaterer en eksisterende verdi.
ProcessDataRead
kjøres i det brukeren leser data fra databasen, f.eks. når man navigerer seg til en tidligere instans av applikasjonen og henter opp tidligere utfylt data.Oppsummering
I denne modulen har du sett på repeterende grupper og hvordan dette konfigureres som en del av brukergrensesnittet. Vi har også sett på hvordan man setter opp egendefinerte valideringer i backend for tilfeller som ikke lar seg definere som en del av restriksjoner i datamodellen. Til slutt har vi sett på hvordan man kan sette opp dataprosessering som muliggjør manipulering av data ved kjøretid.
Løsningsforslag
Vi har lagt til en komponent for repeterende gruppe i Altinn Studio Designer med en adressekomponent som “child”.
Gruppekomponenten er knyttet til datamodell-feltet Innflytter.TidligereBosteder
og adressekomponenten er knyttet til feltene Innflytter.TidligereBosteder.Gateadresse
,
Innflytter.TidligereBosteder.Postnr
og Innflytter.TidligereBosteder.Poststed
.
Antall tillatte repeterende grupper er bestemt av maxOccurs
for feltet i datamodellen.
Vi må også sette maxCount
til 10
på gruppekomponenten for å hindre brukeren i å (visuelt) opprette flere grupper enn tillatt.
Foreløpig må dette gjøres lokalt i sidens layout-fil (se under).
Vi har i tillegg lagt til en overskrift som tydeliggjør skillet mellom tidligere og nåværende adresser.
App/ui/layouts/innflytterPersonalia.json
{
"$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4//schemas/json/layout/layout.schema.v1.json",
"data": {
"layout": [
{
"id": "tidligere-bosteder-overskrift",
"type": "Header",
"size": "M",
"textResourceBindings": {
"title": "innflytterPersonalia.tidligere-bosteder-overskrift.title"
}
},
{
"id": "Group-tidligere-bosteder",
"type": "RepeatingGroup",
"maxCount": 10,
"dataModelBindings": {
"group": "Innflytter.TidligereBosteder"
},
"textResourceBindings": {
"add_button": "innflytterPersonalia.Address-adresse"
},
"children": [
"Address-tidligere-bosted"
]
},
{
"id": "Address-tidligere-bosted",
"type": "Address",
"dataModelBindings": {
"address": "Innflytter.TidligereBosteder.Gateadresse",
"zipCode": "Innflytter.TidligereBosteder.Postnr",
"postPlace": "Innflytter.TidligereBosteder.Poststed"
},
"simplified": true,
"required": true,
"textResourceBindings": {
"title": "innflytterPersonalia.Address-tidligere-bosted.title"
}
}
]
}
}
Følgende tekstressurser er lagt til:
App/config/texts/resources.nb.json
{
"$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/text-resources/text-resources.schema.v1.json",
"language": "nb",
"resources": [
{
"id": "innflytterPersonalia.Address-adresse",
"value": "adresse"
},
{
"id": "innflytterPersonalia.Address-tidligere-bosted.title",
"value": "Tidligere bosted"
},
{
"id": "innflytterPersonalia.tidligere-bosteder-overskrift.title",
"value": "Tidligere bosteder"
}
]
}
- Legg til valideringslogikk i metoden
ValidateData
iInstanceValidation.cs
:
App/logic/Validation/InstanceValidation.cs
...
public async Task ValidateData(object data, ModelStateDictionary validationResults)
{
if (data.GetType() == typeof(Skjema))
{
Skjema skjema = (Skjema)data;
string elitePostalCode = "1337";
string eliteSymbol = "🌟";
if (skjema?.Innflytter.TidligereBosteder != null)
{
List<Adresse> tidligereBosteder = skjema.Innflytter.TidligereBosteder;
int i = 0;
foreach (Adresse adresse in tidligereBosteder)
{
if (adresse.Postnr == elitePostalCode && !adresse.Gateadresse.Contains(eliteSymbol))
{
validationResults.AddModelError("Innflytter.TidligereBosteder[" + i + "].Postnr", "Innflytter.TidligereBosteder.validation_message");
}
i++;
}
}
}
await Task.CompletedTask;
}
...
- Legg til tekstressurs for feilmelding:
App/config/texts/resources.nb.json
{
"$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/text-resources/text-resources.schema.v1.json",
"language": "nb",
"resources": [
...,
{
"id": "Innflytter.TidligereBosteder.validation_message",
"value": "Vi er beæret over å motta en '1337' innbygger til Sogndal kommune! Du må imidlertid bekrefte din uovertruffenhet ved å legge til en 🌟 i adressefeltet for å gå videre."
}
]
}
Ekstra utfordring
Denne løsningen endrer bare adressen for tidligere bosteder. Oppdater koden slik at valideringen også omfatter nåværende adresse.
- Opprett en klasse som implementerer
IdataProcessor
som beskrevet i dataprosessering og legg til logikk for dataprosessering:
App/logic/DataProcessing/DataProcessor.cs
...
namespace Altinn.App.AppLogic.DataProcessing;
public class DataProcessor : IDataProcessor {
public async Task<bool> ProcessDataRead(Instance instance, Guid? dataId, object data)
{
return await Task.FromResult(false);
}
public async Task<bool> ProcessDataWrite(Instance instance, Guid? dataId, object data)
{
bool edited = false;
if (data.GetType() == typeof(Skjema)) {
Skjema skjema = (Skjema)data;
if (skjema?.Innflytter.TidligereBosteder != null) {
List<Adresse> tidligereBosteder = skjema.Innflytter.TidligereBosteder;
int i = 0;
foreach (Adresse adresse in tidligereBosteder) {
if (adresse.Gateadresse == "Sesame Street 1") {
adresse.Gateadresse = "Sesamgate 1";
edited = true;
}
i++;
}
}
}
return await Task.FromResult(edited);
}
}
- Registrer implementeringen i
Program.cs
App/Program.cs
...
{
// Register your apps custom service implementations here.
...
services.AddTransient<IInstanceValidator, InstanceValidator>();
services.AddTransient<IDataProcessor, DataProcessor>();
}
...
Ekstra utfordring
Denne løsningen endrer bare adressen for tidligere bosteder og kun for Sesame Street 1
.
Oppdater koden slik at:
- Prosesseringen også omfatter nåværende adresse.
- Endringen skjer for alle gatenumre.