feat: Implement a real-time character counter and limit validation for the rich text editor, including timer management.

This commit is contained in:
2026-02-26 17:59:50 +01:00
parent 5f2e7a9b56
commit 6b23a40196

View File

@@ -4,6 +4,7 @@
@using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Forms
@namespace SZUAbsolventenverein.Module.HallOfFame @namespace SZUAbsolventenverein.Module.HallOfFame
@implements IDisposable
@inherits ModuleBase @inherits ModuleBase
@inject IHallOfFameService HallOfFameService @inject IHallOfFameService HallOfFameService
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@@ -42,6 +43,11 @@
{ {
<RichTextEditor Content="@_description" @ref="@_richTextEditorRef" <RichTextEditor Content="@_description" @ref="@_richTextEditorRef"
Placeholder="Beschreibe deinen Werdegang..."></RichTextEditor> Placeholder="Beschreibe deinen Werdegang..."></RichTextEditor>
<div class="text-muted small mt-1">
Aktuell: <strong
class="@(_currentCharCount > _charLimit ? "text-danger" : "text-success")">@_currentCharCount</strong>
von maximal @_charLimit Zeichen.
</div>
} }
</div> </div>
</div> </div>
@@ -157,9 +163,32 @@ new Stylesheet("_content/SZUAbsolventenverein.Module.HallOfFame/Module.css")
private string _modifiedby; private string _modifiedby;
private DateTime _modifiedon; private DateTime _modifiedon;
private int _charLimit = 500; private int _charLimit = 500;
private int _currentCharCount = 0;
private System.Timers.Timer _timer;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_timer = new System.Timers.Timer(1000); // 1 sekunde
_timer.Elapsed += async (s, e) =>
{
if (_richTextEditorRef != null && _descriptionLoaded)
{
try
{
var html = await _richTextEditorRef.GetHtml();
var plainText = System.Text.RegularExpressions.Regex.Replace(html ?? "", "<.*?>", String.Empty);
plainText = System.Net.WebUtility.HtmlDecode(plainText);
if (_currentCharCount != plainText.Length)
{
_currentCharCount = plainText.Length;
await InvokeAsync(StateHasChanged);
}
}
catch { } // Ignore interop errors during disposal
}
};
_timer.Start();
try try
{ {
// Load character limit setting // Load character limit setting
@@ -263,6 +292,7 @@ new Stylesheet("_content/SZUAbsolventenverein.Module.HallOfFame/Module.css")
{ {
try try
{ {
ClearModuleMessage();
validated = true; validated = true;
// Get the HTML content from the rich text editor // Get the HTML content from the rich text editor
if (_richTextEditorRef != null) if (_richTextEditorRef != null)
@@ -273,6 +303,16 @@ new Stylesheet("_content/SZUAbsolventenverein.Module.HallOfFame/Module.css")
var interop = new Oqtane.UI.Interop(JSRuntime); var interop = new Oqtane.UI.Interop(JSRuntime);
if (await interop.FormValid(form)) if (await interop.FormValid(form))
{ {
// Custom character limit validation for rich text
var plainText = System.Text.RegularExpressions.Regex.Replace(_description ?? "", "<.*?>", String.Empty);
plainText = System.Net.WebUtility.HtmlDecode(plainText);
if (plainText.Length > _charLimit)
{
AddModuleMessage($"Fehler: Die Beschreibung ist zu lang (Aktuell {plainText.Length}, Maximal {_charLimit} Zeichen).",
MessageType.Warning);
return;
}
_status = status; _status = status;
if (PageState.Action == "Add") if (PageState.Action == "Add")
@@ -333,4 +373,10 @@ new Stylesheet("_content/SZUAbsolventenverein.Module.HallOfFame/Module.css")
AddModuleMessage("Fehler beim Löschen des Eintrags.", MessageType.Error); AddModuleMessage("Fehler beim Löschen des Eintrags.", MessageType.Error);
} }
} }
public void Dispose()
{
_timer?.Stop();
_timer?.Dispose();
}
} }