String

Konfigurationen des String-EditorTemplates. Jedes Beispiel zeigt in Tabs die cshtml, das ViewModel und den Controller (GET/POST); darunter das funktionierende Formular zum Ausprobieren.

Eindeutige Id (UniqueId)

Die gerenderte HTML-Id wird um einen Zufallswert ergänzt, damit mehrfach gerenderte Formulare kollisionsfrei bleiben.

@model UniqueIdBeispielViewModel

<form asp-area="Basis" asp-controller="UniqueId" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.Kommentar)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class UniqueIdBeispielViewModel
    {
        [Display(Name = "Kommentar")]
        [UniqueId]
        public string Kommentar { get; set; }
    }
}
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    [Area("Basis")]
    public class UniqueIdController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new UniqueIdBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(UniqueIdBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Validierung()
        {
            return this.View(new UniqueIdValidierungBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Validierung(UniqueIdValidierungBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Das Model-Binding läuft über den name des Feldes – der bleibt trotz [UniqueId]
                // der Eigenschaftsname, nur die HTML-Id trägt den Zufallswert.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Validierung));
            }

            // Beim erneuten Rendern findet das EditorTemplate den ModelState-Eintrag nicht, weil es
            // ihn über die (zufällige) Id sucht – die Meldung erscheint deshalb nur in der
            // Validierungs-Zusammenfassung, nicht am Feld selbst.
            return this.View(modell);
        }
    }
}

Darstellung

[UniqueId] ist ein Marker-Attribut: Die gerenderte HTML-Id wird um einen Zufallswert ergänzt, damit mehrfach gerenderte Formulare (Nachlade-Szenarien, Modals) kollisionsfrei bleiben.

[UniqueId]
public string Kommentar { get; set; }

Konsequenzen

  • Erschwertes Arbeiten mit Selenium/UI-Tests, da sich die Ids pro Rendering ändern.
  • Die direkte Zuordnung der MVC-Validierungsmeldung zur Eigenschaft über die Id entfällt.

Ausführlich — mit dem Vergleich «zweimal gerendert» und dem Zusammenspiel mit der Validierung — auf der Demoseite Validierung › UniqueId.

Pflichtfeld abhängig von einem Feld (RequiredIf)

"Firmenname" wird nur zur Pflicht, wenn "Anmeldeart" den Wert "Firma" hat.

@model RequiredIfBeispielViewModel

<form asp-area="Basis" asp-controller="RequiredIf" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.AnmeldeArt)
    @Html.EditorFor(m => m.Firmenname)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class RequiredIfBeispielViewModel
    {
        [Display(Name = "Anmeldeart")]
        public string AnmeldeArt { get; set; }

        [Display(Name = "Firmenname")]
        [RequiredIf(nameof(AnmeldeArt), "Firma", ErrorMessage = "Bitte geben Sie den Firmennamen an, wenn die Anmeldeart „Firma“ gewählt ist.")]
        public string Firmenname { get; set; }
    }
}
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    // Ein Controller pro Attribut (RequiredIf); je eine Action/View pro EditorTemplate.
    // Die POST-Actions illustrieren die Validierungslogik: trifft die Bedingung zu und ist das
    // Feld leer, ist ModelState.IsValid false.
    [Area("Basis")]
    public class RequiredIfController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new RequiredIfBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(RequiredIfBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Textbox()
        {
            return this.View(new RequiredIfTextboxBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Textbox(RequiredIfTextboxBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Gültig: entweder trifft die Bedingung nicht zu oder das Feld ist ausgefüllt.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Textbox));
            }

            // Ungültig: Bedingung trifft zu und das Feld ist leer -> View mit den Fehlern.
            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Textarea()
        {
            return this.View(new RequiredIfTextareaBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Textarea(RequiredIfTextareaBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Textarea));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Password()
        {
            return this.View(new RequiredIfPasswordBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Password(RequiredIfPasswordBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Password));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Datum()
        {
            return this.View(new RequiredIfDatumBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Datum(RequiredIfDatumBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Datum));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Dropdown()
        {
            return this.View(new RequiredIfDropdownBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Dropdown(RequiredIfDropdownBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Dropdown));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Combobox()
        {
            return this.View(new RequiredIfComboboxBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Combobox(RequiredIfComboboxBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Combobox));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Multiselect()
        {
            return this.View(new RequiredIfMultiselectBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Multiselect(RequiredIfMultiselectBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Bei einer Collection prüft das Attribut zusätzlich auf Count > 0.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Multiselect));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult RadioList()
        {
            return this.View(new RequiredIfRadioListBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult RadioList(RequiredIfRadioListBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Hier liefert die Bedingung eine Radio-Gruppe: gültig, solange „Vor Ort“ gewählt ist
                // oder bei „Online“ eine E-Mail-Adresse erfasst wurde.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.RadioList));
            }

            return this.View(modell);
        }
    }
}

Darstellung

[RequiredIf] macht ein Feld nur dann zur Pflicht, wenn eine abhängige Eigenschaft einen bestimmten Zielwert hat. Verglichen wird über ToString().

[RequiredIf(nameof(AnmeldeArt), "Firma", ErrorMessage = "Bitte erfassen Sie den Firmennamen")]
public string Firmenname { get; set; }

Weitere Konfigurationen

Option Wirkung
Zweite Bedingung Überladung mit zwei Eigenschaft/Zielwert-Paaren — beide Bedingungen müssen erfüllt sein
DisableIfNotRequired = true Deaktiviert das Feld, solange es nicht erforderlich ist
HideIfNotRequired = true Blendet das Feld aus, solange es nicht erforderlich ist

Clientseitig rendert das Attribut data-val-requiredif-*; das zugehörige Skript jquery.validate.unobtrusive.requiredif.js muss im JS-Bundle enthalten sein. Leere Collections zählen serverseitig wie kein Wert.

Dieselbe Bedingung auf den übrigen EditorTemplates (Textarea, Password, DateTime, DropdownList, ComboboxList, MultiselectList) zeigt die Demoseite Validierung › RequiredIf.

Pflichtfeld abhängig von einer Werteliste (RequiredIfContains)

"Referenznummer" wird nur zur Pflicht, wenn "Zahlungsart" "Rechnung" oder "Kreditkarte" ist.

@model RequiredIfContainsBeispielViewModel

<form asp-area="Basis" asp-controller="RequiredIfContains" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.Zahlungsart)
    @Html.EditorFor(m => m.Referenznummer)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class RequiredIfContainsBeispielViewModel
    {
        [Display(Name = "Zahlungsart")]
        public string Zahlungsart { get; set; }

        [Display(Name = "Referenznummer")]
        [RequiredIfContains(nameof(Zahlungsart), "Rechnung", "Kreditkarte", ErrorMessage = "Bitte geben Sie die Referenznummer an, wenn als Zahlungsart „Rechnung“ oder „Kreditkarte“ gewählt ist.")]
        public string Referenznummer { get; set; }
    }
}
using System.Collections.Generic;
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    [Area("Basis")]
    public class RequiredIfContainsController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new RequiredIfContainsBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(RequiredIfContainsBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }

        [HttpGet]
        public IActionResult CheckboxList()
        {
            return this.View(new RequiredIfContainsCheckboxListBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult CheckboxList(RequiredIfContainsCheckboxListBeispielViewModel modell)
        {
            // Sicherheitsnetz für den Fehlerfall: Wird die View mit den Validierungsfehlern erneut
            // gerendert, muss die Collection gesetzt sein – das CheckboxList-Template prüft nicht auf null.
            // Ohne Häkchen schickt der Browser gar keinen Wert für die Liste mit.
            modell.Verpflegung ??= new List<int>();

            if (this.ModelState.IsValid)
            {
                // Gültig: es ist weder Allergiker- noch Sonderkost angehakt oder die Angaben zur Kost
                // sind erfasst. Die Bedingung greift, sobald *ein* angehakter Wert ein Zielwert ist.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.CheckboxList));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Textbox()
        {
            return this.View(new RequiredIfContainsTextboxBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Textbox(RequiredIfContainsTextboxBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Gültig: die Zahlungsart steht auf keinem der Zielwerte oder die Referenznummer ist erfasst.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Textbox));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Dropdown()
        {
            return this.View(new RequiredIfContainsDropdownBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Dropdown(RequiredIfContainsDropdownBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Dropdown));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Zielwerte()
        {
            return this.View(new RequiredIfContainsZielwerteBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Zielwerte(RequiredIfContainsZielwerteBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Die Zielwerte liest das Attribut bei der Prüfung aus LaenderMitMwstNummer –
                // das gebundene Modell trägt die Liste also mit in die Validierung.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Zielwerte));
            }

            return this.View(modell);
        }

        [HttpGet]
        public IActionResult Ausblenden()
        {
            return this.View(new RequiredIfContainsAusblendenBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Ausblenden(RequiredIfContainsAusblendenBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // HideIfNotRequired wirkt nur clientseitig: das Feld wird trotzdem mitgeschickt
                // und serverseitig geprüft.
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);
                return this.RedirectToAction(nameof(this.Ausblenden));
            }

            return this.View(modell);
        }
    }
}

Darstellung

[RequiredIfContains] macht ein Feld zur Pflicht, wenn der Wert der abhängigen Eigenschaft in einer Werteliste enthalten ist.

[RequiredIfContains(nameof(Zahlungsart), "Rechnung", "Kreditkarte", ErrorMessage = "Bitte erfassen Sie die Referenznummer")]
public string Referenznummer { get; set; }

Weitere Konfigurationen

Option Wirkung
TargetValuesProperty Liest die Zielwerte zur Validierungszeit aus einer Eigenschaft des Modells (Array oder Liste) statt aus der Annotation — dazu die Überladung nur mit dependentProperty verwenden
DisableIfNotRequired / HideIfNotRequired Wie bei [RequiredIf]

Clientseitig rendert das Attribut data-val-requiredifcontains-* (Skript jquery.validate.unobtrusive.requiredifcontains.js).

Text

Einfache Textbox für string ohne Validierung.

@model TextBeispielViewModel

<form asp-area="Basis" asp-controller="Text" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.Vorname)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class TextBeispielViewModel
    {
        [Display(Name = "Vorname")]
        public string Vorname { get; set; }
    }
}
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    [Area("Basis")]
    public class TextController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new TextBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(TextBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }
    }
}

Darstellung

Das String-EditorTemplate (Views/Shared/EditorTemplates/String.cshtml aus FMH.Komponente.Basis) wird über den ASP.NET-Core-Standardmechanismus eingebunden — nicht über Html.Fmh(...):

@Html.EditorFor(m => m.Vorname)

Es rendert ein Bootstrap-Floating-Label-Textfeld mit Slots für Hilfetext und Validierungsmeldung. Der Anzeigename kommt aus den Model-Metadaten.

Weitere Konfigurationen (FMH)

Attribut Wirkung
[Display(Name = "…")] Setzt den Anzeigenamen des Floating Labels
[HelpText(Name = "…")] Ergänzender Hilfetext unter dem Feld (siehe Beispiel «Text mit Hilfetext»)
[Required] / [RequiredIf] Pflichtfeld-Verhalten (siehe die weiteren Beispiele auf dieser Seite)
[UniqueId] Kollisionsfreie HTML-Ids bei mehrfach gerenderten Formularen

Häufige Standard-DataAnnotations

Aus System.ComponentModel.DataAnnotations — server- und clientseitig (unobtrusive) wirksam. Alle unterstützen ErrorMessage bzw. ErrorMessageResourceType/-Name für lokalisierte Meldungen.

Attribut Wirkung
[StringLength(50, MinimumLength = 3)] Begrenzt die Zeichenlänge (max. und optional min.); rendert maxlength und data-val-length
[MaxLength(50)] / [MinLength(3)] Nur Ober- bzw. Untergrenze der Länge
[RegularExpression(@"^[A-Z]{2}\d+$")] Erzwingt ein Muster; rendert data-val-regex
[Range(1, 100)] Wertebereich (typischer bei Zahlenfeldern, funktioniert aber auch auf konvertierten Werten)
[Compare(nameof(Passwort))] Wert muss mit einer anderen Eigenschaft übereinstimmen (z. B. Passwort-Bestätigung)
[EmailAddress] Prüft ein E-Mail-Format (Framework-Standard; FMH-Variante siehe eigene Email-Demo)
[Phone] Prüft ein Telefonnummern-Format
[Url] Prüft ein URL-Format
[CreditCard] Prüft eine Kreditkartennummer (Luhn)
[DataType(DataType.Password)] Semantik-Hinweis fürs Rendering (z. B. type="password", type="email"), keine Validierung

Text mit erforderlicher Eingabe

Pflichtfeld über [Required] – Absenden mit leerem Feld zeigt die Validierung.

@model TextErforderlichBeispielViewModel

<form asp-area="Basis" asp-controller="TextErforderlich" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.Nachname)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class TextErforderlichBeispielViewModel
    {
        [Display(Name = "Nachname")]
        [Required(ErrorMessage = "Bitte Nachnamen eingeben")]
        public string Nachname { get; set; }
    }
}
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    [Area("Basis")]
    public class TextErforderlichController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new TextErforderlichBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(TextErforderlichBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }
    }
}

Darstellung

Pflichtfeld über das Standard-Attribut [Required]. Das EditorTemplate erkennt die Pflicht über die Model-Metadaten und kennzeichnet das Feld; die Validierung läuft server- und clientseitig (unobtrusive Validation).

[Required(ErrorMessage = "Bitte erfassen Sie den Nachnamen")]
public string Nachname { get; set; }

Weitere Konfigurationen

Option Wirkung
ErrorMessage Eigene Fehlermeldung statt der Framework-Standardmeldung
ErrorMessageResourceType / -Name Lokalisierte Meldung aus einer Ressourcenklasse

Text mit Hilfetext

Ergänzender Hilfetext beim Eingabefeld über [HelpText].

@model TextHilfetextBeispielViewModel

<form asp-area="Basis" asp-controller="TextHilfetext" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.Raum)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-2" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;

namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
    public class TextHilfetextBeispielViewModel
    {
        [Display(Name = "Raum")]
        [HelpText(Name = "Geben Sie den bevorzugten Raum für die Tätigkeiten an.")]
        public string Raum { get; set; }
    }
}
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.WebUI.Areas.Basis.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.Basis.Controllers.Beispiele
{
    [Area("Basis")]
    public class TextHilfetextController : Controller
    {
        [HttpGet]
        public IActionResult Index()
        {
            return this.View(new TextHilfetextBeispielViewModel());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(TextHilfetextBeispielViewModel modell)
        {
            if (this.ModelState.IsValid)
            {
                // Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
                this.AddAlert("Formular ist gültig – Validierung erfolgreich.", AlertType.Success, autoHide: true);

                // PRG-Pattern (Post-Redirect-Get): nach erfolgreichem POST per Redirect auf die
                // GET-Action weiterleiten, damit ein erneutes Laden/Aktualisieren das Formular nicht
                // nochmals abschickt (verhindert doppeltes Speichern).
                return this.RedirectToAction(nameof(this.Index));
            }

            // Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Validierungsfehler erscheinen.
            return this.View(modell);
        }
    }
}

Darstellung

Geben Sie den bevorzugten Raum für die Tätigkeiten an.

Ergänzender Hilfetext über [HelpText]. Der Text erscheint beim Eingabefeld und wird über GetName() aufgelöst.

[HelpText(Name = "Gebäude und Raumnummer, z. B. A 2.17")]
public string Raum { get; set; }

Weitere Konfigurationen

Option Wirkung
Name Der Hilfetext direkt — oder der Ressourcen-Schlüssel, falls ResourceType gesetzt ist
ResourceType Ressourcenklasse für lokalisierte Hilfetexte; unbekannter Schlüssel ergibt einen leeren Text