Popup Benachrichtigung per Alert

Immer mehr Building Blocks wandern in meine Blazor CRM Web App. Dieses mal benötige ich eine Bootstrap Alert Box am Ende der Blazor Server Page.

Screenshot 2022-09-08 204240

Zunächst wird der Alert als Blazor Componente AlertPPEDV.razor im Visual Studio Projekt erzeugt. Der HTML Teil könnte so aussehen um den Alert im Bottom Bereich der Page zu zeigen

   1:  @if (ViewModel.isVisible)
   2:  {    
   3:      <div class="alert alert-warning alert-dismissible fade show fixed-bottom" >
   4:              @ViewModel.Nachricht

5: <button type="button" class="btn-close"

@onclick="()=>ViewModel.isVisible=false">

   6:              </button>
   7:      </div>
   8:  }

Ich schreibe könnte, weil ich ursprünglich wollte, dass Alert Display das Problem einfacher löst. Die nicht funktionierende Idee war, die Alert Componente in die MainLayout.razor Componenten einzubetten und per Referenz auf das Objekt ein und auszublenden.

   1:  <AlertPPEDV @ref="alert" ViewModel="vm">
   2:  </AlertPPEDV>
   3:  @code {
   4:      AlertPPEDV alert;

Die eigentliche Page, z.B. die index.razor, erhält die Referenz per <CascadingValue Value="alert">. Allerdings ist das Objekt _alert per CascadingParameter, welches eigentlich die Blazor Component enthalten sollte, dann immer null.

 [CascadingParameter]
    public AlertPPEDV _alert { get; set; }

Der Grund liegt im Rendering Prozess von Blazor. Solange dieser nicht abgeschlossen ist, sind @Ref Referenzen immer null. Microsoft schreibt dazu, dass per Design Referenzen auf Components per Cascading Parameter nicht weiter gegeben werden können.

Also schiebe ich nun ein Viewmodel dazwischen und löse das Problem mit der Anzeige des Alerts. Folgend die Teilprobleme:

  • Sichtbarkeit per isVisible Property
  • Anzeigetext auch HTML formatierbar
  • Benachrichtigung des UI über Änderung per Action Property
  • Timer um Alert automatisch zu schließen

Die C# Klasse AlertVM.cs beninhaltet auch die Methode Show um den Alert Bereich einzublenden.

   1:  public class AlertVM
   2:  {
   3:    public Action updateui;
   4:    public MarkupString Nachricht { get; set; }
   5:    public bool isVisible { get; set; }
   6:    Timer _timer;
   7:    public async Task Show(string msg)
   8:    {
   9:      Nachricht = new MarkupString(msg);
  10:      isVisible = true;
  11:      updateui?.Invoke();
  12:      _timer = new System.Threading.Timer((_) =>
  13:      {
  14:         isVisible = false;
  15:         updateui?.Invoke();
  16:        _timer?.Dispose();
  17:       }
  18:      , null, 9000, 3000);
  19:    }
  20:  }

AlertPPEDV.razor wird um einen Parameter, der das ViewModel von eben beinhaltet, ergänzt.  Darauf aufbauend der Aufruf aus der Action des VM per Invoke um den Rendervorgang neu anzustoßen.

   1:  @code {
   2:      [Parameter]
   3:      public AlertVM ViewModel { get; set; }
   4:    
   5:      protected override void OnInitialized()
   6:      {
   7:      ViewModel.updateui += () => InvokeAsync(StateHasChanged);
   8:      }
   9:  }

Mainlayout.razor muss passend dazu folgenden Code beinhalten, um den HTML Container für das Alert im Layout der Blazor App bereit zu stellen.

   1:  <CascadingValue Value="vm">
   2:  ....
   3:                  @Body
   4:  ....
   5:  </CascadingValue> 
   6:  <AlertPPEDV ViewModel="vm">
   7:  </AlertPPEDV>
   8:  @code {
   9:      AlertVM vm = new AlertVM();
  10:    

In jeder beliebigen Blazor Page (zb Index.razor) wird dann per CascacdingParameter die Instanz des Viewmodels geholt.

   1:  @code
   2:  {
   3:   [CascadingParameter]
   4:   public AlertVM _alertvm { get; set; }

Nun haben Sie per C# Logik Zugriff um Show aufzurufen.

   1:  <button @onclick='()=>_alertvm.Show("Hallo")' 
   2:  class="btn btn-outline-danger">alert</button>
Kommentare sind geschlossen