Blazor UI Control selbstgebaut

Nachdem Microsoft nun Blazor vom Experimental Objekt zum (ja zu was eigentlich?) gemacht hat und für irgendwann dieses Jahr für fertig (zumindest die Server Kiste) angekündigt hat, kann man in der Tat mal versuchen in Richtung Produktion zu gehen. Um das mit den Versionen mal zu verdeutlichen. 8.März – Version 0.9 Release. 25. April Version 3 Preview 4 – First offical Preview.

BrandBlazor_nohalo_1000xJedenfalls hat Microsoft bisher keine Hemmungen gezeigt alles und jederzeit umzuwerfen, das scheint nun vorbei. Also wird es an der Zeit rein konzeptionell ein Usercontrol für Blazor zu bauen. Hier Razor Component genannt. Warum das nun Razor heißt ist mir nicht so klar. Zwar gibt es viele Ähnlichkeiten zu dem Razor Views aus ASP.NET core, aber die Unterschiede sind trotzdem erheblich. Kompatibel ist anders.

Die Aufgabe: ein Chart Control für arme (ganz arme) Developer

Auch wenn Blazor im Client oder alternativ am Server laufen kann, folgende Hinweise

  • Deklarative UI Sprache ist weiter HTML und CSS
  • für DOM Manipulation wird weiter JavaScript benötigt
  • Blazor am Client debuggen – geht bei mir nicht
  • nur theoretisch ist Blazor Server Code direkt im Client ausführbar

Mein Ansatz ist vom Client her zu entwickeln. Also erst die HTML Sicht, das Javascript, erst dann eine Razor Seite (auf dem Server), dann auf dem Client transferieren und letztendlich in ein eigenes Library Projekt als DLL kapseln.

Zuerst wird ein Visual Studio Projekt vom Typ aspnetcore 3 (Sprache C#) – Blazor (server-side) angelegt. Als Zeichenbrett kommt ein HTML5 Canvas zum Einsatz. Fürs erste reicht eine einfache HTML Seite im wwwroot Verzeichnis. Die Logik wird in eine JavaScript Datei ausgelagert.

Das Window Objekt wird um ein JavaScript Objekt samt Funktionen erweitert. Die Chart Mini Lib hat nur eine Aufgabe eine Tortengrafik anhand eines Werte Arrays zu zeichnen.

   1:  <script src="ChartInterop.js"></script>
   2:  <canvas id="canvas1" height="200" width="200"></canvas>
   3:  <script>
   4:      interop.drawCircle([ 30, 20, 50]);
   5:  </script>

 

Nun bin ich mit JavaScript nicht besonders gut befreundet, aber das bekomme ich gerade noch hin Kreisfunktionen zu zeichnen.

   1:  window.interop = {
   2:      drawCircle: function (parameter) {
   3:          var farben = ["red", "blue", "yellow", "green", "silver"];
   4:          var c = document.getElementById("canvas1");
   5:          var cx = c.getContext("2d");
   6:          let currentAngle = -0.5 * Math.PI;
   7:          var summe = parameter.reduce(function (sum, count) { return sum + count; }, 0);
   8:          var ii = 0;
   9:          for (let item of parameter) {
  10:              let sliceAngle = (item/ summe) * 2 * Math.PI;
  11:              cx.beginPath();
  12:              cx.arc(100, 100, 100,
  13:                  currentAngle, currentAngle + sliceAngle);
  14:              currentAngle += sliceAngle;
  15:              cx.lineTo(100, 100);
  16:              cx.fillStyle = farben[ii];
  17:              cx.fill();
  18:              ii++;
  19:          }
  20:          return true;
  21:      }
  22:  };

Natürlich könnte man auch ein einfache Function schreiben, aber JSInterop von Blazor braucht das in dieser Form, wie wir gleich lernen werden. Per View im Browser sollte nun folgendes zu sehen sein.

image

Um die hässlichkeit des JavaScript Codes zu verstecken, kapseln wir das nun weg. Aktuell haben sich die Redmonder entschieden Blazor Datein mit der Endung .razor zu versehen. VIsual Studio hat dafür stand Mai 2019 keine Templates so das man einfach ne Text Datei passende benennt. Eine Razor Datei kann direkt betrachtet werden und ist auch gleichzeitig ein UserControl (Component). Dabei wird View und Code gemischt (geht auch per Codebehind).

   1:  @page "/miniChart"
   2:  @using Microsoft.JSInterop
   3:  @using Microsoft.AspNetCore.Components;
   4:   
   5:  @inject IJSRuntime JSRuntime
   6:  <canvas id="canvas1" height="200" width="200"></canvas>
   7:   
   8:  @functions {
   9:  [Parameter]
  10:  int[] Daten { get; set; }
  11:   
  12:  public async void DrawCircleAsync()
  13:  {
  14:    await JSRuntime.InvokeAsync<bool>("interop.drawCircle", Daten);
  15:   }
  16:  }
  17:   
  18:   

Was man hier wissen muss

@page leitet die Razor Seite ein, die Route ist Pflicht.

@Using referenziert auf den Namespace

@Inject übernimmt Depenency Injection (geht auch per Konstruktor Injection)

@functions hält Propertys und Funktionen

mit [Parameter] wird später die Schnittstelle zum Aufrufer gestaltet

InvokeAsync ist die JavaScript Interop Bridge samt Parameter

 

Auf gehts zur Verwendung des neuen Component Controls mit einer weiteren .razor Seite. Diese unbedingt in das Verzeichnis Pages platzieren. Im Gegensatz zu asp.net core Razor ist das kein muss, aber es wird gleich noch das Layout benötigt.  Minichart kann direkt mit dem Parameter beschickt werden. Die Syntax für das Integer Array ist ein wenig eigenwillig. Wahrscheinlich kommen in der Praxis die Daten ohnehin eher dynamisch aus eine REST Service. Also Ansatz #2 aus Zeile 7, die Daten als Property ausführen und mit der @Daten Syntax im HTML Binden (hier oneway). Die Button Click Funktion wird mit identem Ansatz gebunden @draw. Völlig abgefahren ist der Ansatz einen Zeiger auf die Instanz des UI Objektes minchart zu erhalten. Man würde eine ID oder X:Name erwarten. Gibt es nicht. Die Lösung ist ein privates Property mit dem passenden Typ und per ref Attribut im HTML View die Objekte zusammen zu führen. Soviel zu Ähnlichkeiten mit cshmtl Razor Views (ist da ganz anders)

   1:  @page "/seite"
   2:  @using Pages
   3:  <minichart Daten="new[] {40,20,40}" ref="miniChart1"></minichart>
   4:  <button onclick="@draw">zeichen</button>
   5:   
   6:  @functions {
   7:      int[] daten = { 30, 10, 60 };
   8:      private minichart miniChart1;
   9:      void draw()
  10:      {
  11:   
  12:          miniChart1.DrawCircleAsync();
  13:   
  14:      }
  15:  }

Ja wo ist es denn das JavaScript? in der seite.razor darf es nämlich nicht sein. Es muss in das Layout, das sich _hosts.cshtml findet.

   1:  <body>
   2:      <app>@(await Html.RenderComponentAsync<App>())</app>
   3:   
   4:      <script src="_framework/blazor.server.js"></script>
   5:      <script src="~/ChartInterop.js"></script>
   6:  </body>

Hier wird auch klar wie Microsoft die App in die Blazor Server Side Lösung einflechtet. Wenn wir nun ein neues Projekt anlegen für eine Client Side Razor App, sieht das ein wenig anders aus. Es ist die Index.html mit einer anderen JavaScript Library.

   1:  <app>Loading...</app>
   2:  <script src="_framework/blazor.webassembly.js"></script>

Auch der Webhost Builder in der Startup.cs unterscheidet sich deutlich. Aber das ist ein anderes Thema. Ansonsten läuft der so erstellte Code in der Tat auch in der Client seitigen Blazor Lösung.

Kommentare sind geschlossen