3. FormData converteren
3. FormData converteren
Een FormData object kan eenvoudig omgevormd worden naar een object via onderstaand fragment:
const convertedData = Object.fromEntries(formData.entries())Dit is echter niet voldoende als het FormData object geneste data bevat. Beschouw volgende keys:
- array.0.name
- array.0.type
- array.1.name
- array.1.type
- property.subObject.name
Elk punt in de key duidt op een geneste property. Deze properties kunnen omgevormd worden door de key op te splitsen en dan per element in de opgesplitste array te kijken of het om het laatste element gaat.
Als het element het laatste deel van de key is, gaat het om de naam van de property in de diepst geneste object. Als het niet het laatste element is, moeten we naar het volgende element kijken en onderscheiden we twee situaties. Als het deel van de key dat volgt op het huidige deel een nummer is, dan weten we dat het om een array gaat. Als het volgende deel geen cijfer is, dan weten we dat het om een object gaat.
We komen zo tot onderstaande functie.
export function convertFormData<T>(data: FormData): T {
const result: Record<string, unknown> = {}
// Voeg alle top-level keys toe aan het resultaat.
Array.from(data.keys())
.filter(key => !key.includes('.'))
.forEach(key => (result[key] = data.get(key)))
// Vorm geneste form data met keys zoals arrayName.index.key
// en objectNaam.object.key om naar een object.
const multipartKeys = Array.from(data.keys()).filter(key => key.includes('.'))
// Sorteer zodat we eerst de elementen op positie 0 in de
// array krijgen en dan de elementen op positie 1, enz.
multipartKeys.sort()
for (const multipartKey of multipartKeys) {
const keyParts = multipartKey.split('.')
let current: any = result
for (let i = 0; i < keyParts.length; i++) {
const keyPart = keyParts[i]
if (i === keyParts.length - 1) {
current[keyPart] = data.get(multipartKey)
continue
}
if (!current[keyPart]) {
current[keyPart] = isNaN(parseInt(keyParts[i + 1])) ? {} : []
}
current = current[keyPart]
}
}
return result as T
}Tenslotte is het belangrijk om de keys die Next automatisch toevoegt aan het FormData object te negeren. Hiervoor filteren we de keys eerst, zodat de keys die beginnen met $ACTION genegeerd worden.
export function convertFormData<T>(data: FormData): T {
const result: Record<string, unknown> = {}
// Verwijder alle keys die beginnen met $ACTION.
// Dit zijn interne dingen die door Next.js worden toegevoegd.
const keys = Array.from(data.keys()).filter(key => !key.startsWith('$ACTION'))
// Voeg alle top-level keys toe aan het resultaat.
keys
.filter(key => !key.includes('.'))
.forEach(key => (result[key] = data.get(key)))
// Vorm geneste form data met keys zoals arrayName.index.key en objectNaam.object.key om naar een object.
const multipartKeys = keys.filter(key => key.includes('.'))
// Sorteer zodat we eerst de elementen op positie 0 in de array krijgen en dan de elementen op positie 1, enz.
multipartKeys.sort()
for (const multipartKey of multipartKeys) {
const keyParts = multipartKey.split('.')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let current: any = result
for (let i = 0; i < keyParts.length; i++) {
const keyPart = keyParts[i]
// Als dit het laatste element is, moet dit de naam van een property zijn.
if (i === keyParts.length - 1) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
current[keyPart] = data.get(multipartKey)
continue
}
// Als de key nog niet in het resultaat zit moet deze aangemaakt worden.
// De key kan al bestaand als deze voorkwam in een eerder verwerkte multi-part key
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!current[keyPart]) {
// Als de volgende key een nummer is, moeten we een array aanmaken omdat het nummer een index is.
// In het andere geval moeten we een object aanmaken.
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
current[keyPart] = isNaN(parseInt(keyParts[i + 1])) ? {} : []
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
current = current[keyPart]
}
}
return result as T
}