DokumentListe
Pflicht-Upload
Ohne mindestens ein zugeordnetes Dokument ist das Formular ungültig. Geprüft wird DokumentIds des DokumentListeViewModel.
@model DokumentListePflichtBeispielViewModel
@* Die Prüfung von DocumentUploadRequired läuft serverseitig — die Meldung erscheint also erst
nach dem Absenden. Ohne asp-validation-summary bliebe sie unsichtbar. *@
<form asp-area="FileUpload" asp-controller="DokumentListePflichtBeispiel" asp-action="Index" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
@Html.EditorFor(m => m.Nachweise)
@Html.Fmh(Fmh.Button()
.Text("Absenden")
.Size(ButtonSize.Sm)
.Attributes(new { @class = "mt-3" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;
using FMH.Komponente.FileUpload.Models;
using FMH.Komponente.FileUpload.Ui.Validation.DataAnnotation;
using FMH.Komponente.Infrastruktur;
namespace FMH.Komponente.WebUI.Areas.FileUpload.Models
{
public class DokumentListePflichtBeispielViewModel
{
// DocumentUploadRequired sucht am ViewModel die erste Eigenschaft vom Typ
// DokumentListeViewModel und prüft deren DokumentIds. Die Liste startet in diesem Beispiel
// bewusst leer: Sind bereits Dokumente vorhanden, gilt die Bedingung als erfüllt und die
// Meldung wäre nie zu sehen.
[UIHint("DokumentListe")]
[Display(Name = "Nachweis (mindestens ein Dokument)")]
[DocumentUploadRequired]
[AdditionalMetadata(MvcConst.MetaDataUploadUrl, "/FileUpload/DokumentListePflichtBeispiel/Upload")]
[AdditionalMetadata(MvcConst.MaxFileSizeInMb, "1")]
[AdditionalMetadata(MvcConst.MaxFiles, "1")]
[AdditionalMetadata(MvcConst.MetaDataDownloadActionName, "Download")]
[AdditionalMetadata(MvcConst.MetaDataDownloadControllerName, "DokumentListePflichtBeispiel")]
public DokumentListeViewModel Nachweise { get; set; } = new DokumentListeViewModel();
}
}
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.FileUpload.Models;
using Microsoft.AspNetCore.Mvc;
namespace FMH.Komponente.WebUI.Areas.FileUpload.Controllers.Beispiele
{
// Zeigt [DocumentUploadRequired]. Die angezeigte Dokumentliste bleibt hier bewusst immer leer:
// Sobald bereits Dokumente zugeordnet sind, gilt die Bedingung über das versteckte Feld
// «NachweiseExistingDocuments» als erfüllt — die Pflichtmeldung wäre dann nicht mehr auslösbar
// und das Beispiel nur einmal vorführbar.
[Area("FileUpload")]
public class DokumentListePflichtBeispielController : Controller
{
// Nur die Zuordnung Id -> Name für die Rückmeldung; die Einträge landen nicht in der Tabelle.
private static readonly ConcurrentDictionary<int, string> Dokumente =
new ConcurrentDictionary<int, string>();
private static int letzteId;
[HttpGet]
public IActionResult Index()
{
return this.View(new DokumentListePflichtBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(DokumentListePflichtBeispielViewModel modell)
{
// Der ungültige Zweig ist hier der Lerninhalt: Ohne übertragenes Dokument ist
// DokumentIds leer, DocumentUploadRequired meldet Document_Required und die View wird
// mit dem Fehler erneut angezeigt. Die Prüfung läuft rein serverseitig — es gibt keinen
// unaufdringlichen Client-Adapter dafür.
if (this.ModelState.IsValid == false)
{
return this.View(modell);
}
var namen = modell.Nachweise.DokumentIds
.Select(id => Dokumente.TryGetValue(id, out var name) ? name : $"Id {id}");
this.AddAlert(
$"Formular gültig – Nachweis vorhanden: {string.Join(", ", namen)}",
AlertType.Success,
autoHide: true);
return this.RedirectToAction(nameof(this.Index));
}
/// <summary>
/// Der Upload-Endpunkt dieses Beispiels. Antwort muss ein Json mit der Eigenschaft
/// <c>id</c> sein — erst diese Id landet als verstecktes Feld <c>DokumentIds</c> im
/// Formular und macht die Pflichtprüfung zufrieden.
/// </summary>
/// <returns>Json mit der vergebenen Id.</returns>
[HttpPost]
public IActionResult Upload()
{
if (this.Request.Headers.ContainsKey("IsDropzoneRequest") == false || this.Request.Form.Files.Count == 0)
{
return this.Json(new { id = 0 });
}
var datei = this.Request.Form.Files[0];
var bezeichnung = this.Request.Form["DateiBezeichnung"].ToString();
var name = string.IsNullOrWhiteSpace(bezeichnung) ? datei.FileName : bezeichnung;
var id = Interlocked.Increment(ref letzteId);
Dokumente[id] = name;
return this.Json(new { id });
}
/// <summary>
/// Der Download-Endpunkt. In diesem Beispiel bleibt die Tabelle leer, der Endpunkt ist
/// dennoch konfiguriert, damit das ViewModel vollständig ist.
/// </summary>
/// <param name="id">Die Id des Dokuments.</param>
/// <returns>Die Datei zum Herunterladen.</returns>
[HttpGet]
public IActionResult Download(int id)
{
if (Dokumente.TryGetValue(id, out var name) == false)
{
return this.NotFound();
}
var inhalt = Encoding.UTF8.GetBytes($"Platzhalter-Inhalt für «{name}» (Id {id}).");
return this.File(inhalt, "text/plain", name);
}
}
}
Darstellung
[DocumentUploadRequired] macht mindestens ein Dokument zur Pflicht.
[UIHint("DokumentListe")]
[Display(Name = "Nachweis (mindestens ein Dokument)")]
[DocumentUploadRequired]
[AdditionalMetadata(MvcConst.MetaDataUploadUrl, "/FileUpload/DokumentListePflichtBeispiel/Upload")]
[AdditionalMetadata(MvcConst.MaxFiles, "1")]
public DokumentListeViewModel Nachweise { get; set; } = new DokumentListeViewModel();
Wie die Prüfung arbeitet
- Sie sucht am ViewModel die erste Eigenschaft vom Typ
DokumentListeViewModelund prüft derenDokumentIds. Ist die Liste leer odernull, ist das Formular ungültig; die Meldung stammt aus den Ressourcen (Document_Required). - Zusätzlich wertet sie das versteckte Feld
<Feldname>ExistingDocumentsaus, das das Template immer mitrendert: Sind bereits Dokumente zugeordnet (Bearbeitungsfall), gilt die Bedingung als erfüllt, auch wenn in diesem Durchgang nichts hochgeladen wurde. Dafür wirdIHttpContextAccessorbenötigt — inProgram.csmitAddHttpContextAccessor()registrieren.
Was zu beachten ist
- Die Prüfung läuft serverseitig. Es gibt keinen unaufdringlichen Client-Adapter für
data-val-documentuploadrequired; die Meldung erscheint also erst nach dem Absenden — eineasp-validation-summaryim Formular macht sie sichtbar. - Enthält das ViewModel mehrere Eigenschaften vom Typ
DokumentListeViewModel, gewinnt immer die erste. Für mehrere Pflichtlisten in einem Formular ist das Attribut daher nicht geeignet. - Zum Vorführen muss die Liste leer starten. Sind bereits Dokumente vorhanden, greift die
ExistingDocuments-Abkürzung und die Meldung ist nicht mehr auslösbar. - Für eine bedingte Pflicht (nur, wenn ein anderes Feld einen bestimmten Wert hat) gibt es
[DocumentUploadRequiredIf].