{"id":88114,"date":"2020-09-08T22:07:21","date_gmt":"2020-09-08T22:07:21","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=88114"},"modified":"2022-05-02T20:39:00","modified_gmt":"2022-05-02T20:39:00","slug":"first-steps-with-blazor","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/first-steps-with-blazor\/","title":{"rendered":"First Steps with Blazor"},"content":{"rendered":"<p>Microsoft announced its new .NET web framework, <a href=\"https:\/\/dotnet.microsoft.com\/apps\/aspnet\/web-apps\/blazor\">Blazor<\/a>, in 2018. Since then, Blazor has caused a lot of buzz, especially in the JavaScript community, for approaching features like the use of WebAssembly and its templating system as well as addressing performance.<\/p>\n<p>Blazor stands for Browser + Razor, which gives you an idea of what\u2019s behind the new framework. Razor is the ASP.NET programming syntax that Microsoft uses to create its C# (or VB.NET) dynamic pages. Now, you can create web applications using only C# and run them in a web browser.<\/p>\n<p>No, you didn\u2019t read it wrong, the modern browsers you\u2019ve used now can run C# in native speed. Not only C# but all the other major back-end languages.<\/p>\n<p>And it\u2019s not just the language, but Blazor now has all the power of .NET available, including:<\/p>\n<ul>\n<li>.NET APIs and tools over all the platform, the IDE and the community. All of that mature enough because of the platform history;<\/li>\n<li>C#, as mentioned, but also F#, which allows you to include machine learning features into your web applications;<\/li>\n<li>The possibility to choose between Visual Studio IDE and VS Code, both mature and massively adopted by the community;<\/li>\n<li>All the well-known characteristics of the .NET platform like the performance, robustness, scalability and responsiveness.<\/li>\n<\/ul>\n<p>Some may think that the ability to run C# in browsers is another attempt to recreate the old Java Applets, famous for their security breaches, and related complexities. That\u2019s not it. This can happen thanks to the <a href=\"http:\/\/webassembly.org\/\">WebAssembly<\/a> (also known as <em>Wasm<\/em>).<\/p>\n<p>WebAssembly is a low-level assembly-like binary instruction format for a stack-based virtual machine. It enables the execution of applications made with some targeted programming languages in web clients and servers, through a process called portable compilation.<\/p>\n<p>Blazor resembles other common web frameworks, like Angular, for example. You have a routing system, very similar layouts, forms, and interoperability between your language and JavaScript functions, etc.<\/p>\n<p>In this tutorial, you\u2019ll be guided through the creation of a simple web app, a CRUD. Blazor auto-generates some code examples when creating a new project, and one of them is going to be the base for this tutorial development. It will be enough for you to feel how the framework works, as well as get started.<\/p>\n<h2><a id=\"post-88114-_heading=h.30j0zll\"><\/a>Setting Up the Project<\/h2>\n<p>For the tutorial example, you need to have both <a href=\"https:\/\/code.visualstudio.com\/\">Visual Studio Code<\/a> and <a href=\"https:\/\/dotnet.microsoft.com\/download\/visual-studio-sdks\">.NET SDK<\/a> installed. The example you\u2019ll develop here can also be done with Visual Studio IDE.<\/p>\n<p>Select a folder via command line and run the command:<\/p>\n<pre class=\"lang:c# theme:vs2012\">dotnet new blazorserver -o first-steps-blazor --no-https<\/pre>\n<p>The <code>--no-https<\/code> is important because you don\u2019t want to add extra complexities in this example by including HTTPS in the example. Keep it simple for now.<\/p>\n<p>The following figure shows the result of its execution:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"870\" height=\"366\" class=\"wp-image-88115\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-35.png\" \/><\/p>\n<p><strong>Figure 1. Creating a new Blazor-based project<\/strong><\/p>\n<p>After that, <code>cd<\/code> the created project root folder and run the command <code>dotnet run<\/code>. It will start the application up and make it available at <a href=\"http:\/\/localhost:5000\">http:\/\/localhost:5000<\/a>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"870\" height=\"366\" class=\"wp-image-88116\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-36.png\" \/><\/p>\n<p><strong>Figure 2. Starting up the app<\/strong><\/p>\n<p>If your browser doesn\u2019t automatically open the page, do it yourself and check the results (Figure 3).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1250\" height=\"694\" class=\"wp-image-88117\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-37.png\" \/><\/p>\n<p><strong>Figure 3. Hello World, Blazor.<\/strong><\/p>\n<p>Once you\u2019re here, the app started up successfully. Take a look at the three samples shown here; they were auto-generated to give you some reference material when getting started.<\/p>\n<p>In this article, the goal is to \u201credo\u201d the third one, the <em>Fetch data<\/em> example. For now, it only displays a list of forecast data kept in memory in direct communication to a service class. When you finish the modifications, a full CRUD of weather forecasts will be available to feed the data table better.<\/p>\n<p>It\u2019s time to open your project in VS Code in order to start coding, but first, make sure to install the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-dotnettools.csharp\">C# extension<\/a> to the IDE (in case you don\u2019t have it already). It is extremely important since it provides the features for developing with .NET, syntax highlighting, debugging, etc. In the Visual Studio IDE, that\u2019s not necessary.<\/p>\n<p>Figure 4 shows how the extension looks like for your reference.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1429\" height=\"306\" class=\"wp-image-88118\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-38.png\" \/><\/p>\n<p><strong>Figure 4. Installing C# for Visual Studio Code<\/strong><\/p>\n<p>Now, go to the <em>File &gt; Open Folder&#8230;<\/em> menu option and select the <em>first-steps-blazor<\/em> folder. This will import all the project files into the workspace. You will also notice that the extension will colorize the C# code when you open any <em>.cs<\/em> or <em>.razor<\/em> file from the samples. Try it out!<\/p>\n<p>Before proceeding to the example implementation, take some time to look at the files and folders that were auto-generated. Here\u2019s a summary:<\/p>\n<ul>\n<li><em>\/Data<\/em>: this is the folder that keeps the models, services and repositories of your app. For now, there are only two classes:\n<ul>\n<li>WeatherForecast represents the model itself by holding the attributes of a forecast object;<\/li>\n<li>WeatherForecastService is the service class and will keep the methods for forecast manipulation in the CRUD example.<\/li>\n<\/ul>\n<\/li>\n<li><em>\/Pages<\/em>: if you\u2019re familiar with ASP.NET projects, this won\u2019t be that new. It\u2019s where the project keeps the Blazor pages. It is equivalent to the Razor elements you\u2019d have in a common ASP.NET Core app.<\/li>\n<li><em>\/Shared<\/em>: this is where the project keeps its Blazor templates. That\u2019s an essential part of it, because it demonstrates the componentization power of the framework, just like you see in others (like Angular, for example). Blazor has a powerful templating system, which you should consider using at any time you feel there\u2019s a piece of the view that could be reused throughout the project.<\/li>\n<li><em>_Imports.razor<\/em>: a place you can use to centralize all the using statements of your Blazor pages.<\/li>\n<li><em>App.razor<\/em>: where everything starts, the root of a Blazor app. Its job is basically about checking for the routing system. If everything\u2019s ok, it mounts the app from top to bottom, making use of the established templates. Here, Blazor starts by importing the MainLayout template, which, in turn, calls the rest of the hierarchy.<\/li>\n<\/ul>\n<p>The rest of the files belong to the usual .NET structural setup, so no more details on them.<\/p>\n<h2><a id=\"post-88114-_heading=h.1fob9te\"><\/a>The Forecast CRUD<\/h2>\n<p>Before going any further, you must take some time analyzing the forecast created example code. There are two C# files, as mentioned, and the Blazor page that keeps the overall logic of the table data displaying, the <em>FetchData.razor<\/em>.<\/p>\n<p>Three points stand out at this moment:<\/p>\n<ol>\n<li>Every time you need to use something external, do as you did before: import the statements in the beginning via <code>@using<\/code> operator. Since there\u2019s no constructor in such pages, if you need to inject a service do it through the <code>@inject<\/code> operator.<\/li>\n<li>Whenever you need to create local objects, methods or operations within the page, do it under the <code>@code{ ... }<\/code> section.<\/li>\n<li>Once an object is imported correctly or defined in the bottom\u2019s <code>@code<\/code>, you can make use of it within the HTML by always prefixing it with a <code>@<\/code>. They\u2019re also available when accessed into Blazor components or via programming expressions (like <code>@if<\/code> or <code>@foreach<\/code>).<\/li>\n<\/ol>\n<p>Now, you\u2019re equipped with all you need, so jump right into the example implementation. By the end of it, the app should look like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1233\" height=\"973\" class=\"wp-image-88119\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-39.png\" \/><\/p>\n<p><strong>Figure 5. Weather forecast CRUD<\/strong><\/p>\n<p>First, you need to make sure your model is updated for all the different scenarios of the example. Take a look at its new contents:<\/p>\n<p><strong>Listing 1. New forecast model code<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing System.ComponentModel.DataAnnotations;\r\nnamespace first_steps_blazor.Data\r\n{\r\n    public class WeatherForecast : ICloneable\r\n    {\r\n        [Required]\r\n        public int Id { get; set; }\r\n        [Required]\r\n        public DateTime Date { get; set; } = DateTime.Today;\r\n        [Required]\r\n        [Range(-60, 60, ErrorMessage = \"Temp C. must be between -60 and 60.\")]\r\n        public int TemperatureC { get; set; }\r\n        [Required]\r\n        public int TemperatureF =&gt; 32 + (int)(TemperatureC \/ 0.5556);\r\n        [Required]\r\n        public string Summary { get; set; }\r\n        public object Clone()\r\n        {\r\n            return this.MemberwiseClone();\r\n        }\r\n    }\r\n}<\/pre>\n<p>Note that all the previous fields were saved with just some small changes. The first change is the addition of an Id. The id of a model is important because it helps later to identify within the list each of the editing and deleting actions.<\/p>\n<p>The Date field received a default value: today.<\/p>\n<p>Notice how each one of the fields is annotated with the <code>[Required]<\/code> annotation. <code>TemperatureC<\/code> is getting another one: the <code>[Range]<\/code> annotation that validates if the Celsius temperature is between -60 and 60 degrees.<\/p>\n<p>The use of the annotations, that belong to the <code>ComponentModel.DataAnnotations<\/code>, will help you to assimilate the validations under the form. Blazor forms can understand these annotations and display proper messages in the view.<\/p>\n<p>Finally, the class is also an <code>ICloneable<\/code>, which means that you can clone an instance of it via <code>Clone()<\/code> method. That will be important later.<\/p>\n<p>The next stop is the <code>WeatherForecastService<\/code>. Listing 2 shows what code you should put there now.<\/p>\n<p><strong>Listing 2. New code for the WeatherForecastService<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nnamespace first_steps_blazor.Data\r\n{\r\n    public class WeatherForecastService\r\n    {\r\n        private List&lt;WeatherForecast&gt; Forecasts = new List&lt;WeatherForecast&gt;();\r\n        private static string[] Summaries = new[]\r\n        {\r\n            \"Freezing\", \"Bracing\", \"Chilly\", \"Cool\", \"Mild\", \"Warm\", \"Balmy\", \"Hot\", \"Sweltering\", \"Scorching\"\r\n        };\r\n        public WeatherForecastService()\r\n        {\r\n            var rng = new Random();\r\n            Forecasts.AddRange(Enumerable.Range(1, 5).Select(index =&gt; new WeatherForecast\r\n            {\r\n                Id = index,\r\n                Date = DateTime.Now.AddDays(index),\r\n                TemperatureC = rng.Next(-20, 55),\r\n                Summary = Summaries[rng.Next(Summaries.Length)]\r\n            }).ToArray());\r\n        }\r\n        public Task&lt;string[]&gt; GetSummaries()\r\n        {\r\n            return Task.FromResult(Summaries);\r\n        }\r\n        public Task&lt;List&lt;WeatherForecast&gt;&gt; GetAll()\r\n        {\r\n            return Task.FromResult(this.Forecasts);\r\n        }\r\n        public Task&lt;WeatherForecast&gt; GetById(int id)\r\n        {\r\n            return Task.FromResult((WeatherForecast)this.Forecasts.Where(note =&gt; note.Id == id).First().Clone());\r\n        }\r\n        public Task Add(WeatherForecast forecast)\r\n        {\r\n            try\r\n            {\r\n                forecast.Id = this.Forecasts.Count + 1;\r\n                this.Forecasts.Add(forecast);\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                return Task.FromException(e);\r\n            }\r\n            return Task.CompletedTask;\r\n        }\r\n        public Task Update(int id, WeatherForecast forecast)\r\n        {\r\n            try\r\n            {\r\n                this.Forecasts[this.Forecasts.FindIndex(note =&gt; note.Id == id)] = forecast;\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                return Task.FromException(e);\r\n            }\r\n            return Task.CompletedTask;\r\n        }\r\n        public Task Delete(int id)\r\n        {\r\n            try\r\n            {\r\n                this.Forecasts = this.Forecasts.Where(note =&gt; note.Id != id).ToList();\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                return Task.FromException(e);\r\n            }\r\n            return Task.CompletedTask;\r\n        }\r\n    }\r\n}<\/pre>\n<p>The first important point here is the fact that the place where the list of forecasts is going to be held has changed. Before, the Blazor page kept it. Now, the service will do the job, as it would do in a real-world scenario.<\/p>\n<p>The list of summaries also remained here, but with a different purpose. Now, instead of being used just to generate random values for the table data, it is available to the pages via <code>GetSummaries()<\/code> method. This is going to be useful for the summaries combo box of the page. In other words, every time the user needs to create a new forecast, it\u2019s possible to choose it from a select element fed with this list (returned from the service).<\/p>\n<p>The old method that initialized and returned a list of forecasts is now over, and this task was reassigned to the constructor.<\/p>\n<p>The rest of the methods are just simple CRUD operations performed with the in-memory list of forecasts, and you can go over them for more details. Make sure to refer to each of the try\/catch flows for async treatment. Plus, notice the calling to the <code>Clone()<\/code> method in the <code>GetById()<\/code>. This is because when getting a forecast by id, the form can use the same object to the editing process, which would alter the attribute values directly in the form (objects in C# work <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/ref\">as references<\/a>) so that behavior should be avoided.<\/p>\n<p>And then it comes the <code>FetchData.razor<\/code> code. Since its code is too big, break it into two pieces. The first one, shown in Listing 3, displays the <code>@code<\/code> contents that are going to be necessary for the second part: the Blazor\/HTML code.<\/p>\n<p><strong>Listing 3. Code contents for the @code section<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">@code {\r\n    WeatherForecast Forecast = new WeatherForecast();\r\n    private string[] summaries;\r\n    private List&lt;WeatherForecast&gt; forecasts;\r\n    private int calculatedTemperatureF = 0;\r\n    private string currentId = null;\r\n    private async Task UpdateTemperature()\r\n    {\r\n        calculatedTemperatureF = 32 + (int)(Forecast.TemperatureC \/ 0.5556);\r\n    }\r\n    private async Task AddForecast()\r\n    {\r\n        if (currentId != null)\r\n        {\r\n            await ForecastService.Update(Int32.Parse(currentId), this.Forecast);\r\n            await JSRuntime.InvokeVoidAsync(\"alert\", \"Updated!\");\r\n        }\r\n        else\r\n        {\r\n            await ForecastService.Add(this.Forecast);\r\n            await JSRuntime.InvokeVoidAsync(\"alert\", \"Added!\");\r\n        }\r\n        await Reset();\r\n    }\r\n    private async Task Reset()\r\n    {\r\n        this.Forecast = new WeatherForecast();\r\n        this.currentId = null;\r\n        calculatedTemperatureF = 0;\r\n        await UpdateTemperature();\r\n    }\r\n    private async Task EditForecast(int id)\r\n    {\r\n        this.Forecast = await ForecastService.GetById(id);\r\n        this.currentId = this.Forecast.Id.ToString();\r\n    }\r\n    private async Task DeleteForecast(int id)\r\n    {\r\n        bool confirmed = await JSRuntime.InvokeAsync&lt;bool&gt;(\"confirm\", \"Are you sure?\");\r\n        if (confirmed)\r\n        {\r\n            await ForecastService.Delete(id);\r\n            await JSRuntime.InvokeVoidAsync(\"alert\", \"Done!\");\r\n            forecasts = await ForecastService.GetAll();\r\n        }\r\n    }\r\n    protected override async Task OnInitializedAsync()\r\n    {\r\n        forecasts = await ForecastService.GetAll();\r\n        summaries = await ForecastService.GetSummaries();\r\n        await Reset();\r\n    }\r\n}<\/pre>\n<p>Before that, this section of the page was short. The <code>OnInitializedAsync<\/code> method (at the end of the listing) works like the constructor of the page, more as you\u2019d do with an onload event in JavaScript. After the page finishes the loading, Blazor triggers this method. This is why it is a <code>protected<\/code> method because it is inherited from Blazor superclass.<\/p>\n<p>This method is used to initialize the forecast data to be displayed in the table. Now, it keeps doing the same by retrieving the full list of items from the service and storing into the forecasts local variable. This is the list that it\u2019s going to be used to display the items in the table.<\/p>\n<p>It also retrieves all the summaries, since the select element needs them to display the combo options. And finally, the method resets the local variables by calling the <code>Reset()<\/code> method. This method, in turn, takes the task of cleaning up all the local references the CRUD page uses. That\u2019s important every time you finish an adding, updating or deleting action.<\/p>\n<p>Back to the local variables, you have<\/p>\n<ul>\n<li>the <code>Forecast<\/code> object, which is going to be the model object to auto bind to each of the form\u2019s fields;<\/li>\n<li>the <code>summaries<\/code> array, which is going to store the list of summaries retrieved from the service;<\/li>\n<li>the <code>calculatedTemperatureF<\/code>, which is going to hold the value of the Fahrenheit temperature. In this example, the F\u00ba value should not be inputted but automatically calculated based on the C\u00ba value. It\u2019s another excellent opportunity to explore how Blazor deals with the DOM events, like the <code>onchange<\/code> and <code>onblur<\/code>;<\/li>\n<li>and the <code>currentId<\/code> will hold the currently selected id for the editing and deleting actions after each button is clicked.<\/li>\n<\/ul>\n<p>And finally, you\u2019ve got the methods. They deserve some special considerations:<\/p>\n<ul>\n<li>The <code>UpdateTemperature()<\/code> explicitly duplicates the temperature calculation done in the model. It is going to be triggered after the <code>tempC<\/code> loses the focus, on the <code>onblur<\/code> event. You could have called the getter of <code>TemperatureF<\/code> field directly; however, this was made on purpose for you to see how such event handling works within Blazor.<\/li>\n<li>The <code>AddForecast()<\/code> method works for both adding and updating actions. Whenever the <code>currentId<\/code> holds any value, it means that an editing process is in progress, so the service updating method should be called; otherwise, the adding flow goes on.\n<ul>\n<li>Here, it\u2019s also important to point out that it\u2019s the first time the example makes use of the <a href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/blazor\/call-javascript-from-dotnet?view=aspnetcore-3.1\">interop ability<\/a> of Blazor. You can talk directly to JavaScript within Blazor pages through the JSRuntime class.<\/li>\n<li>It provides the <code>InvokeVoidAsync<\/code> and <code>InvokeAsync<\/code> methods (for both voided and returning functions) for implementing such integration. The first argument is the JavaScript function, and the second represents its arguments. Look how the code calls an explicit JavaScript alert. The deleting method makes use of the <code>confirm<\/code> function.<\/li>\n<\/ul>\n<\/li>\n<li>Make sure to refresh the forecasts list after deleting an item, for example.<\/li>\n<\/ul>\n<p>Listing 4 shows the rest of the content, to be placed at the beginning of the page file:<\/p>\n<p><strong>Listing 4. Remaining code contents for the FetchData.razor page<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">@page \"\/fetchdata\"\r\n@using first_steps_blazor.Data\r\n@using System.Collections.Generic\r\n@inject WeatherForecastService ForecastService\r\n@inject IJSRuntime JSRuntime\r\n&lt;h1&gt;Weather forecast&lt;\/h1&gt;\r\n&lt;p&gt;This component demonstrates fetching data from a service.&lt;\/p&gt;\r\n@if (forecasts == null)\r\n{\r\n    &lt;p&gt;&lt;em&gt;Loading...&lt;\/em&gt;&lt;\/p&gt;\r\n}\r\nelse\r\n{\r\n    &lt;EditForm Model=@Forecast OnValidSubmit=@AddForecast&gt;\r\n        &lt;DataAnnotationsValidator\/&gt;\r\n        &lt;ValidationSummary\/&gt;\r\n        &lt;InputText @bind-Value=currentId type=\"hidden\" \/&gt;\r\n        &lt;div class=\"form-group\"&gt;\r\n            &lt;label for=\"date\"&gt;Date&lt;\/label&gt;\r\n            &lt;InputDate @bind-Value=Forecast.Date class=\"form-control\" id=\"date\" placeholder=\"Date\" \/&gt;\r\n            &lt;ValidationMessage For=\"() =&gt; Forecast.Date\"\/&gt;\r\n        &lt;\/div&gt;\r\n        \r\n        &lt;div class=\"form-row\"&gt;\r\n            &lt;div class=\"form-group col-md-6\"&gt;\r\n                &lt;label for=\"tempC\"&gt;Temp. (C)&lt;\/label&gt;\r\n                &lt;InputNumber @bind-Value=\"Forecast.TemperatureC\" \r\n                    @onblur=\"UpdateTemperature\" class=\"form-control\" id=\"tempC\" placeholder=\"Temp. (C)\" \/&gt;\r\n                &lt;ValidationMessage For=\"() =&gt; Forecast.TemperatureC\"\/&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div class=\"form-group col-md-6\"&gt;\r\n                &lt;label for=\"tempF\"&gt;Temp. (F)&lt;\/label&gt;\r\n                &lt;InputNumber @bind-Value=calculatedTemperatureF disabled class=\"form-control\" id=\"tempF\" placeholder=\"Temp. (F)\" \/&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=\"form-group\"&gt;\r\n            &lt;label for=\"summary\"&gt;Summary&lt;\/label&gt;\r\n            &lt;InputSelect @bind-Value=Forecast.Summary class=\"form-control\" id=\"summary\"&gt;\r\n                &lt;option&gt;Select an option...&lt;\/option&gt;\r\n                @foreach (var summary in summaries)\r\n                {\r\n                    &lt;option value=\"@summary\"&gt;@summary&lt;\/option&gt;\r\n                }\r\n            &lt;\/InputSelect&gt;\r\n            &lt;ValidationMessage For=\"() =&gt; Forecast.Summary\"\/&gt;\r\n        &lt;\/div&gt;\r\n        &lt;input type=\"submit\" class=\"btn btn-primary\" value=\"+ Add\" \/&gt;\r\n    &lt;\/EditForm&gt;\r\n    &lt;hr \/&gt;\r\n    &lt;table class=\"table\"&gt;\r\n        &lt;thead&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;Date&lt;\/th&gt;\r\n                &lt;th&gt;Temp. (C)&lt;\/th&gt;\r\n                &lt;th&gt;Temp. (F)&lt;\/th&gt;\r\n                &lt;th&gt;Summary&lt;\/th&gt;\r\n                &lt;th&gt;Actions&lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n        &lt;\/thead&gt;\r\n        &lt;tbody&gt;\r\n            @foreach (var forecast in forecasts)\r\n            {\r\n                &lt;tr&gt;\r\n                    &lt;td&gt;@forecast.Date.ToShortDateString()&lt;\/td&gt;\r\n                    &lt;td&gt;@forecast.TemperatureC&lt;\/td&gt;\r\n                    &lt;td&gt;@forecast.TemperatureF&lt;\/td&gt;\r\n                    &lt;td&gt;@forecast.Summary&lt;\/td&gt;\r\n                    &lt;td colspan=\"2\"&gt;\r\n                        &lt;button class=\"btn btn-secondary\" @onclick=\"@(async() =&gt; await EditForecast(@forecast.Id))\"&gt;Edit&lt;\/button&gt;\r\n                        &lt;button class=\"btn btn-danger\" @onclick=\"@(async() =&gt; await DeleteForecast(@forecast.Id))\"&gt;Delete&lt;\/button&gt;\r\n                    &lt;\/td&gt;\r\n                &lt;\/tr&gt;\r\n            }\r\n        &lt;\/tbody&gt;\r\n    &lt;\/table&gt;\r\n}<\/pre>\n<p>First, make sure to import\/inject everything the page will need, just as if you\u2019re doing it in a common C# class.<\/p>\n<p>Start by analyzing the form composition. You can make use of plain HTML form tags here. The reason the example does not is due to the Blazor validations and data binding. For each of the main form elements, you have a corresponding Blazor component tag.<\/p>\n<p><code>EditForm<\/code> stands for an editable form that auto binds its data field values directly onto the attributes of a model object (in this case, the <code>Forecast<\/code> local object). Every time you type any text into a field, this value is automatically set to the provided <code>@bind-Value<\/code> property. This can only happen because you also informed the form\u2019s Model property correctly.<\/p>\n<p>The same is valid for the <code>OnValidSubmit\u2019s<\/code> one. In this case, however, it is a method, the one Blazor will trigger when the form is submitted.<\/p>\n<p>Right after, you can see the other two tags: <code>DataAnnotationsValidator<\/code> and <code>ValidationSummary<\/code>. The former explicitly says to Blazor that this form should be validated against the model annotations inserted before. Remember? If a field is marked as required (or within a range, like in <code>tempC<\/code>) it should respect this rule before submitting the form; otherwise, Blazor will display the proper messages in an HTML ul list at the top of the page, thanks to the latter tag (<code>ValidationSummary<\/code>).<\/p>\n<p>Note, however, that below each of the input components of the form you can see a <code>ValidationMessage<\/code> tag as well. These work the same way as <code>ValidationSummary<\/code>; the only difference is that they\u2019re assigned to a specific input field, rather than to the whole validation context. Figure 6 shows how both validation categories work.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1562\" height=\"720\" class=\"wp-image-88120\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-40.png\" \/><\/p>\n<p><strong>Figure 6. Displaying the two groups of validation messages<\/strong><\/p>\n<p>After you correctly fill in each of the fields, Blazor changes their the color states, as shown in Figure 7.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1547\" height=\"530\" class=\"wp-image-88121\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/09\/word-image-41.png\" \/><\/p>\n<p><strong>Figure 7. Look and feel of a correctly filled form<\/strong><\/p>\n<p>Take a look now at the <code>tempC<\/code> field. It has a different property called <code>@onblur<\/code>, which triggers the <code>UpdateTemperature()<\/code> method implemented before.<\/p>\n<p>Another good example is the <code>@onlick<\/code> functions implemented in both editing and deleting action buttons. These, however, are passing on the forecast id as a parameter to their own methods. Since they\u2019re async methods, make sure to call them into an async block.<\/p>\n<p>Finally, you have two more <code>foreach<\/code> loops that iterate over the <code>summaries<\/code> and <code>forecasts<\/code> lists. The first works by feeding the select combo box and the second displays the table of forecasts data.<\/p>\n<p>It\u2019s time to test the example. Make sure to explore each one of the functionalities: the deleting, the adding and updating of forecasts, as well as the auto calculating of a Fahrenheit temperature based on the Celsius one.<a id=\"post-88114-_heading=h.3znysh7\"><\/a><\/p>\n<h2>Conclusion<\/h2>\n<p>You can find the source code for this project sample in my <a href=\"https:\/\/github.com\/iamjuliosampaio\/SimpleCrud-Blazor\">GitHub repo<\/a>. Let me know if you had any trouble making the example work.<\/p>\n<p>It\u2019s always worth mentioning the importance of reading the <a href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/blazor\/\">official docs<\/a> of Blazor (like any other new tech you\u2019d be willing to learn). It is very complete with in-depth information of each of the many pieces of Blazor, as well as great samples. You can even play ahead with this tutorial example and increment it with more helpful features you learn from there. Keep on track and good studies!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Blazor allows you to create client-side code with C# instead of JavaScript. In this article, Julio Sampaio gets you started with your first Blazor project.&hellip;<\/p>\n","protected":false},"author":323407,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[5134],"coauthors":[93894],"class_list":["post-88114","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/88114","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/323407"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=88114"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/88114\/revisions"}],"predecessor-version":[{"id":88123,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/88114\/revisions\/88123"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=88114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=88114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=88114"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=88114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}