In a more advanced or complex multi-step form, fields are organized into different groups. However, in the default email template rendering, all fields are displayed in a flat structure.
Has anyone implemented a way to group fields in emails so that they reflect the same structure as the form itself?
Hi @bjarnef I think the IFormServicemight be able to help you out you should be able to retrieve the Form model then from there I believe you can retrieve field metadata for the fields and/or you should also be able to retrieve in sets? Then it’s just a mapping exercise to match up the fields fromFormsHtmlModel to the correct area
The Umbraco.Forms.Core.Models.FormsHtmlModel dosen’t know about pages and fieldsets
So something like…
@using Umbraco.Forms.Core.Services
@inject IFormService _formService
var form = _formService.Get(Model.FormId);
var fieldPages = form!.Pages;
@foreach (var page in form.Pages)
{
// Access Page: page.Caption
foreach (var fieldset in page.FieldSets)
{
// Access Fieldset: fieldset.Caption
foreach (var container in fieldset.Containers)
{
foreach (var field in container.Fields)
{
// Match the structural field to the submitted data model
var submittedField = Model.Fields.FirstOrDefault(x => x.Id == field.Id);
if (submittedField != null && !ignoreFields.Contains(submittedField.FieldType))
{
}
}
}
}
}
should replace the existing loop in the Example Template…
@foreach (var field in Model.Fields.Where(x => ignoreFields.Contains(x.FieldType) == false)) {..}
Yes, I ended up using IFormService as well and a helper method.
It works okay if workflow executes right after submission, but there’s a risk form definition may change after a submission and e.g. before a workflow executes on approval or rejection.
It would be useful it forms include scheme/meta data about this (avoiding the additional lookup) and I think record could use this to structure fields in boxes, especially with a complex/advanced form.
public interface IFormsEmailModelFactory
{
FormEmailViewModel Create(FormsHtmlModel model);
}
public class FormsEmailModelFactory(IFormService formService) : IFormsEmailModelFactory
{
private readonly IFormService _formService = formService;
public FormEmailViewModel Create(FormsHtmlModel model)
=> FormsEmailModelMapper.ToEmailViewModel(model, _formService);
}
public static class FormsEmailModelMapper
{
public static FormEmailViewModel ToEmailViewModel(
FormsHtmlModel model,
IFormService formService)
{
var form = formService.GetFromCache(model.FormName)
?? throw new InvalidOperationException($"Form '{model.FormName}' was not found.");
var valuesByFieldId = model.Fields
.DistinctBy(x => x.Id)
.ToDictionary(
f => f.Id,
f =>
{
var values = f.GetValues();
return values.Length switch
{
0 => string.Empty,
1 => values[0]?.ToString() ?? string.Empty,
_ => string.Join(", ", values.Select(v => v?.ToString()))
};
});
var vm = new FormEmailViewModel
{
FormName = model.FormName,
ValuesByFieldId = valuesByFieldId
};
foreach (var page in form.Pages)
{
if (page is null)
continue;
var pageVm = new FormEmailPage
{
Caption = page.Caption ?? string.Empty
};
foreach (var fieldset in page.FieldSets)
{
if (fieldset is null)
continue;
var fieldsetVm = new FormEmailFieldset
{
Caption = fieldset.Caption ?? string.Empty
};
foreach (var container in fieldset.Containers)
{
if (container is null)
continue;
foreach (var field in container.Fields)
{
if (field is null)
continue;
valuesByFieldId.TryGetValue(field.Id, out var value);
fieldsetVm.Fields.Add(new FormEmailField
{
FieldTypeId = field.FieldTypeId,
FieldId = field.Id,
Alias = field.Alias,
Caption = field.Caption,
Value = value
});
}
}
pageVm.Fieldsets.Add(fieldsetVm);
}
vm.Pages.Add(pageVm);
}
return vm;
}
}
public class FormEmailViewModel
{
public string FormName { get; set; } = string.Empty;
public List<FormEmailPage> Pages { get; set; } = [];
public Dictionary<Guid, string> ValuesByFieldId { get; set; } = [];
public string? GetValue(Guid fieldId)
=> ValuesByFieldId.TryGetValue(fieldId, out var v) ? v : null;
}
public class FormEmailPage
{
public string Caption { get; set; } = string.Empty;
public List<FormEmailFieldset> Fieldsets { get; set; } = [];
}
public class FormEmailFieldset
{
public string Caption { get; set; } = string.Empty;
public List<FormEmailField> Fields { get; set; } = [];
}
public class FormEmailField
{
public Guid FieldTypeId { get; set; }
public Guid FieldId { get; set; }
public string Alias { get; set; } = string.Empty;
public string Caption { get; set; } = string.Empty;
public string? Value { get; set; }
}