Textarea (EditorTemplate)
Das Textarea-EditorTemplate rendert ein mehrzeiliges Eingabefeld. Einbindung über
@Html.EditorFor(...) mit [UIHint(UiEditor.Basis.Textarea)]. Die maximale Länge kommt
aus [StringLength]/[MaxLength].
Textarea
Mehrzeiliges Eingabefeld.
@model TextareaBeispielViewModel
<form class="fmh-demo-form">
@Html.EditorFor(m => m.Bemerkung)
</form>
using System.ComponentModel.DataAnnotations;
namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
public class TextareaBeispielViewModel
{
[Display(Name = "Bemerkung")]
[UIHint(UiEditor.Basis.Textarea)]
public string Bemerkung { get; set; } = "Mehrzeiliger Text\nüber das Textarea-EditorTemplate.";
}
}
Darstellung
Das Textarea-EditorTemplate rendert ein mehrzeiliges Eingabefeld. Eingebunden über Html.EditorFor mit [UIHint(UiEditor.Basis.Textarea)].
[Display(Name = "Bemerkung")]
[UIHint(UiEditor.Basis.Textarea)]
public string Bemerkung { get; set; }
Weitere Konfigurationen
| Konfiguration | Wirkung |
|---|---|
[Display(Name = "…")] |
Beschriftung |
[Required] |
Pflichtangabe, siehe Beispiel «Textarea mit Pflichtangabe» |
[StringLength(n)] / [MaxLength(n)] |
Obergrenze des Zeichenzählers (ohne Angabe 1000), siehe Beispiel «Textarea mit Feldlänge» |
[HelpText(Name = "…")] |
Ergänzender Hilfetext, siehe Beispiel «Textarea mit Hilfetext» |
[AdditionalMetadata(UiMetadata.Basis.TextareaRows, n)] |
Höhe des Feldes, siehe Beispiel «Textarea mit Zeilenhöhe» |
[AdditionalMetadata(UiMetadata.Basis.TextareaCols, n)] |
Wird als cols gerendert, bleibt aber ohne sichtbare Wirkung: form-control setzt die Breite auf 100 % |
UiMetadata.Basis liegt im Paket FMH.Komponente.Basis.Validierung, die Werte der beiden Konstanten sind "rows" und "cols". Die Metadaten brauchen den registrierten AdditionalMetadataProvider.
Nicht verwechseln mit dem gleichnamigen DisplayTemplate Textarea (schreibgeschützte Darstellung).
Textarea mit Feldlänge
[MaxLength] setzt die Obergrenze des Zählers unter dem Feld. Mit einem Längen-Attribut schneidet der Browser nicht ab, die Prüfung greift beim Absenden.
@model TextareaFeldlaengeBeispielViewModel
<form asp-area="Basis" asp-controller="Textarea" asp-action="Feldlaenge" method="post">
@Html.EditorFor(m => m.Interessen)
@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
{
/// <summary>
/// Feldlänge: Der Zähler unter dem Feld zeigt die Länge gegen die Obergrenze aus
/// <c>[MaxLength]</c>. Mit einem Längen-Attribut schneidet der Browser nicht ab, die Eingabe
/// läuft über die Grenze hinaus, der Zähler schlägt aus und die Prüfung greift am Server.
/// </summary>
public class TextareaFeldlaengeBeispielViewModel
{
[Display(Name = "Interessen")]
[UIHint(UiEditor.Basis.Textarea)]
[MaxLength(500, ErrorMessage = "Bitte höchstens 500 Zeichen erfassen.")]
public string Interessen { 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
{
/// <summary>
/// Ein Controller pro Template (Textarea); je eine Action/View pro Variante.
/// Die POST-Actions zeigen, was der Server aus der Eingabe macht: ein leeres Feld ist mit
/// [Required] ungültig, ein zu langer Text scheitert an [MaxLength], und der Hilfetext ist
/// reine Anzeige und hält nichts auf.
/// </summary>
[Area("Basis")]
public class TextareaController : Controller
{
[HttpGet]
public IActionResult Feldlaenge()
{
return this.View(new TextareaFeldlaengeBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Feldlaenge(TextareaFeldlaengeBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Feldlaenge));
}
[HttpGet]
public IActionResult Hilfetext()
{
return this.View(new TextareaHilfetextBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Hilfetext(TextareaHilfetextBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Hilfetext));
}
[HttpGet]
public IActionResult Pflicht()
{
return this.View(new TextareaPflichtBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Pflicht(TextareaPflichtBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Pflicht));
}
/// <summary>
/// Gemeinsame PRG-Logik der Varianten: Bei gültigem Modell eine Meldung setzen und per
/// Redirect auf die GET-Action, sonst dieselbe View mit den Fehlern erneut anzeigen.
/// </summary>
/// <param name="modell">Das abgeschickte Modell.</param>
/// <param name="action">Der Name der GET-Action dieser Variante.</param>
/// <returns>Die Weiterleitung oder die erneut gerenderte View.</returns>
private IActionResult PruefeUndZeige(object modell, string action)
{
if (this.ModelState.IsValid)
{
// Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
this.AddAlert("Formular ist gültig – Text übernommen.", AlertType.Success, autoHide: true);
// PRG-Pattern: nach erfolgreichem POST per Redirect auf die GET-Action.
return this.RedirectToAction(action);
}
// Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Meldungen erscheinen.
return this.View(action, modell);
}
}
}
Darstellung
Unter dem Eingabefeld steht ein Zähler im Format getippt/erlaubt. Die Obergrenze kommt aus [MaxLength] oder [StringLength], ohne beides sind es 1000 Zeichen.
[Display(Name = "Interessen")]
[UIHint(UiEditor.Basis.Textarea)]
[MaxLength(500, ErrorMessage = "Bitte höchstens 500 Zeichen erfassen.")]
public string Interessen { get; set; }
Der entscheidende Unterschied liegt darin, wer die Grenze durchsetzt:
| Modell | Gerendertes maxlength |
Verhalten |
|---|---|---|
| ohne Längen-Attribut | 1000 (der Standardwert) |
Der Browser lässt nach 1000 Zeichen nichts mehr zu, der Zähler bleibt stehen |
[MaxLength(n)] / [StringLength(n)] |
leer | Die Eingabe läuft über die Grenze hinaus, der Zähler wird fett und rot, die Meldung kommt aus der Validierung |
[StringLength(n, MinimumLength = m)] |
n × 4 |
Behelf für die Prüfung am Client: Erst ein deutlich höheres Limit lässt die Mindestlänge überhaupt melden |
Der Zähler selbst hängt am Attribut data-field-length und wird von jquery-fmh-validation.js bei jeder Eingabe neu geschrieben.
Textarea mit Hilfetext
Ergänzender Hilfetext beim Eingabefeld über [HelpText]; er erscheint beim Fokus.
@model TextareaHilfetextBeispielViewModel
<form asp-area="Basis" asp-controller="Textarea" asp-action="Hilfetext" method="post">
@Html.EditorFor(m => m.Notiz)
@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
{
/// <summary>
/// Hilfetext: Der Text unter dem Eingabefeld erscheint beim Fokus und verschwindet beim
/// Verlassen des Feldes; eine Fehlermeldung verdrängt ihn.
/// </summary>
public class TextareaHilfetextBeispielViewModel
{
[Display(Name = "Notiz zur Anmeldung")]
[UIHint(UiEditor.Basis.Textarea)]
[HelpText(Name = "Stichworte genügen, die Notiz erscheint auf der Bestätigung.")]
public string Notiz { 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
{
/// <summary>
/// Ein Controller pro Template (Textarea); je eine Action/View pro Variante.
/// Die POST-Actions zeigen, was der Server aus der Eingabe macht: ein leeres Feld ist mit
/// [Required] ungültig, ein zu langer Text scheitert an [MaxLength], und der Hilfetext ist
/// reine Anzeige und hält nichts auf.
/// </summary>
[Area("Basis")]
public class TextareaController : Controller
{
[HttpGet]
public IActionResult Feldlaenge()
{
return this.View(new TextareaFeldlaengeBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Feldlaenge(TextareaFeldlaengeBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Feldlaenge));
}
[HttpGet]
public IActionResult Hilfetext()
{
return this.View(new TextareaHilfetextBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Hilfetext(TextareaHilfetextBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Hilfetext));
}
[HttpGet]
public IActionResult Pflicht()
{
return this.View(new TextareaPflichtBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Pflicht(TextareaPflichtBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Pflicht));
}
/// <summary>
/// Gemeinsame PRG-Logik der Varianten: Bei gültigem Modell eine Meldung setzen und per
/// Redirect auf die GET-Action, sonst dieselbe View mit den Fehlern erneut anzeigen.
/// </summary>
/// <param name="modell">Das abgeschickte Modell.</param>
/// <param name="action">Der Name der GET-Action dieser Variante.</param>
/// <returns>Die Weiterleitung oder die erneut gerenderte View.</returns>
private IActionResult PruefeUndZeige(object modell, string action)
{
if (this.ModelState.IsValid)
{
// Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
this.AddAlert("Formular ist gültig – Text übernommen.", AlertType.Success, autoHide: true);
// PRG-Pattern: nach erfolgreichem POST per Redirect auf die GET-Action.
return this.RedirectToAction(action);
}
// Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Meldungen erscheinen.
return this.View(action, modell);
}
}
}
Darstellung
[HelpText] legt einen Hinweis unter das Eingabefeld. Er ist zunächst ausgeblendet (d-none) und erscheint, sobald das Feld den Fokus bekommt.
[Display(Name = "Notiz zur Anmeldung")]
[UIHint(UiEditor.Basis.Textarea)]
[HelpText(Name = "Stichworte genügen, die Notiz erscheint auf der Bestätigung.")]
public string Notiz { get; set; }
HelpText liegt in FMH.Komponente.Basis.Ui.Validation.DataAnnotation.
Was zu beachten ist
- Ein- und Ausblenden übernehmen
onfocusin/onfocusoutdes Validators. Ohne jQuery-Validation am Formular bleibt der Hinweis unsichtbar. - Eine Fehlermeldung hat Vorrang: Ist das Feld ungültig, bleibt der Hilfetext weg und der Platz gehört der Meldung.
- Der Hilfetext ist reine Anzeige, er hält das Absenden nie auf.
Textarea mit Pflichtangabe
Pflichtfeld über [Required] – Absenden mit leerem Feld zeigt die Validierung.
@model TextareaPflichtBeispielViewModel
<form asp-area="Basis" asp-controller="Textarea" asp-action="Pflicht" method="post">
@Html.EditorFor(m => m.Bemerkungen)
@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
{
/// <summary>
/// Pflichtangabe: Die Beschriftung bekommt den Stern, ein leeres Feld hält das Absenden auf und
/// die Meldung erscheint unter dem Eingabefeld.
/// </summary>
public class TextareaPflichtBeispielViewModel
{
[Display(Name = "Bemerkungen")]
[UIHint(UiEditor.Basis.Textarea)]
[Required(ErrorMessage = "Bitte teilen Sie uns Ihre Bemerkungen mit.")]
public string Bemerkungen { 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
{
/// <summary>
/// Ein Controller pro Template (Textarea); je eine Action/View pro Variante.
/// Die POST-Actions zeigen, was der Server aus der Eingabe macht: ein leeres Feld ist mit
/// [Required] ungültig, ein zu langer Text scheitert an [MaxLength], und der Hilfetext ist
/// reine Anzeige und hält nichts auf.
/// </summary>
[Area("Basis")]
public class TextareaController : Controller
{
[HttpGet]
public IActionResult Feldlaenge()
{
return this.View(new TextareaFeldlaengeBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Feldlaenge(TextareaFeldlaengeBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Feldlaenge));
}
[HttpGet]
public IActionResult Hilfetext()
{
return this.View(new TextareaHilfetextBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Hilfetext(TextareaHilfetextBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Hilfetext));
}
[HttpGet]
public IActionResult Pflicht()
{
return this.View(new TextareaPflichtBeispielViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Pflicht(TextareaPflichtBeispielViewModel modell)
{
return this.PruefeUndZeige(modell, nameof(this.Pflicht));
}
/// <summary>
/// Gemeinsame PRG-Logik der Varianten: Bei gültigem Modell eine Meldung setzen und per
/// Redirect auf die GET-Action, sonst dieselbe View mit den Fehlern erneut anzeigen.
/// </summary>
/// <param name="modell">Das abgeschickte Modell.</param>
/// <param name="action">Der Name der GET-Action dieser Variante.</param>
/// <returns>Die Weiterleitung oder die erneut gerenderte View.</returns>
private IActionResult PruefeUndZeige(object modell, string action)
{
if (this.ModelState.IsValid)
{
// Erfolgsfall: hier würden die eigentlichen Aktionen ausgeführt (z. B. Speichern).
this.AddAlert("Formular ist gültig – Text übernommen.", AlertType.Success, autoHide: true);
// PRG-Pattern: nach erfolgreichem POST per Redirect auf die GET-Action.
return this.RedirectToAction(action);
}
// Ungültige Eingabe: dieselbe Seite erneut anzeigen, damit die Meldungen erscheinen.
return this.View(action, modell);
}
}
}
Darstellung
[Required] macht die Eingabe zur Pflicht.
[Display(Name = "Bemerkungen")]
[UIHint(UiEditor.Basis.Textarea)]
[Required(ErrorMessage = "Bitte teilen Sie uns Ihre Bemerkungen mit.")]
public string Bemerkungen { get; set; }
Die Beschriftung bekommt über die Klasse required den Stern, das Feld zusätzlich das HTML-Attribut required. Die Meldung erscheint unter dem Feld im invalid-feedback-Bereich, serverseitig prüft [Required] unabhängig vom Browser erneut.
Textarea mit Zeilenhöhe
Mit UiMetadata.Basis.TextareaRows wird das Feld höher, hier acht Zeilen statt der voreingestellten vier.
@model TextareaZeilenBeispielViewModel
<form class="fmh-demo-form">
@Html.EditorFor(m => m.Protokoll)
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;
using FMH.Komponente.Infrastruktur;
namespace FMH.Komponente.WebUI.Areas.Basis.Models
{
/// <summary>
/// Zeilenhöhe: <c>UiMetadata.Basis.TextareaRows</c> setzt die Höhe des Eingabefeldes, hier acht
/// Zeilen statt der voreingestellten vier.
/// </summary>
public class TextareaZeilenBeispielViewModel
{
[Display(Name = "Protokoll")]
[UIHint(UiEditor.Basis.Textarea)]
[AdditionalMetadata(UiMetadata.Basis.TextareaRows, 8)]
public string Protokoll { get; set; } = "Acht Zeilen hoch statt der voreingestellten vier.";
}
}
Darstellung
Die Höhe des Feldes steuert UiMetadata.Basis.TextareaRows, voreingestellt sind vier Zeilen.
[Display(Name = "Protokoll")]
[UIHint(UiEditor.Basis.Textarea)]
[AdditionalMetadata(UiMetadata.Basis.TextareaRows, 8)]
public string Protokoll { get; set; }
Das Template rendert das rows-Attribut, setzt daraus aber zusätzlich min-height als Inline-Stil: Bootstrap 5 ignoriert rows innerhalb eines form-floating-Containers, und ohne den Stil bliebe das Feld unverändert hoch.
UiMetadata.Basis.TextareaCols wird ebenfalls gelesen und als cols gerendert, bleibt aber wirkungslos, weil form-control die Breite auf 100 % des Containers setzt. Für ein schmaleres Feld ist der umgebende Container zuständig, nicht cols.