This tutorial shows you how to build and modify a Blazor app.
Follow the guidance in the Get started with ASP.NET Core Blazor article to create a Blazor project for this tutorial. Name the project ToDoList.
Browse to each of the app’s three pages in the Pages folder: Home, Counter, and Fetch data. These pages are implemented by the Razor component files Index.razor, Counter.razor, and FetchData.razor.
On the Counter page, select the Click me button to increment the counter without a page refresh. Incrementing a counter in a webpage normally requires writing JavaScript. With Blazor, you can write C# instead.
Examine the implementation of the Counter
component in the Counter.razor file.
Pages/Counter.razor:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The UI of the Counter
component is defined using HTML. Dynamic rendering logic (for example, loops, conditionals, expressions) is added using an embedded C# syntax called Razor. The HTML markup and C# rendering logic are converted into a component class at build time. The name of the generated .NET class matches the file name.
Members of the component class are defined in an @code
block. In the @code
block, component state (properties, fields) and methods are specified for event handling or for defining other component logic. These members are then used as part of the component’s rendering logic and for handling events.
When the Click me button is selected:
Counter
component’s registered onclick
handler is called (the IncrementCount
method).Counter
component regenerates its render tree.Modify the C# logic of the Counter
component to make the count increment by two instead of one.
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount += 2;
}
}
Rebuild and run the app to see the changes. Select the Click me button. The counter increments by two.
Include a component in another component using an HTML syntax.
Add the Counter
component to the app’s Index
component by adding a <Counter />
element to the Index
component (Index.razor).
If you’re using Blazor WebAssembly for this experience, a SurveyPrompt
component is used by the Index
component. Replace the <SurveyPrompt>
element with a <Counter />
element. If you’re using a Blazor Server app for this experience, add the <Counter />
element to the Index
component:
Pages/Index.razor:
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<Counter />
Rebuild and run the app. The Index
component has its own counter.
Components can also have parameters. Component parameters are defined using public properties on the component class with the [Parameter]
attribute. Use attributes to specify arguments for a component in markup.
Update the component’s @code
C# code as follows:
IncrementAmount
property with the [Parameter]
attribute.IncrementCount
method to use the IncrementAmount
property when increasing the value of currentCount
.Pages/Counter.razor:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public int IncrementAmount { get; set; } = 1;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}
Specify an IncrementAmount
parameter in the Index
component’s <Counter>
element using an attribute. Set the value to increment the counter by ten.
Pages/Index.razor:
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<Counter IncrementAmount="10" />
Reload the Index
component. The counter increments by ten each time the Click me button is selected. The counter in the Counter
component continues to increment by one.
The @page
directive at the top of the Counter.razor file specifies that the Counter
component is a routing endpoint. The Counter
component handles requests sent to /counter
. Without the @page
directive, a component doesn’t handle routed requests, but the component can still be used by other components.
If working with a Blazor Server app, the WeatherForecastService
service is registered as a singleton in Startup.ConfigureServices
. An instance of the service is available throughout the app via dependency injection (DI):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
The @inject
directive is used to inject the instance of the WeatherForecastService
service into the FetchData
component.
Pages/FetchData.razor:
@page "/fetchdata"
@using ToDoList.Data
@inject WeatherForecastService ForecastService
The FetchData
component uses the injected service, as ForecastService
, to retrieve an array of WeatherForecast
objects:
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
If working with a Blazor WebAssembly app, HttpClient
is injected to obtain weather forecast data from the weather.json file in the wwwroot/sample-data folder.
Pages/FetchData.razor:
@inject HttpClient Http
...
protected override async Task OnInitializedAsync()
{
forecasts =
await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
An @foreach
loop is used to render each forecast instance as a row in the table of weather data:
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
Add a new component to the app that implements a simple todo list.
Add an empty file named Todo.razor to the app in the Pages folder:
Provide the initial markup for the component:
@page "/todo"
<h1>Todo</h1>
Add the Todo
component to the navigation bar.
The NavMenu
component (Shared/NavMenu.razor) is used in the app’s layout. Layouts are components that allow you to avoid duplication of content in the app.
Add a <NavLink>
element for the Todo
component by adding the following list item markup below the existing list items in the Shared/NavMenu.razor file:
<li class="nav-item px-3">
<NavLink class="nav-link" href="todo">
<span class="oi oi-list-rich" aria-hidden="true"></span> Todo
</NavLink>
</li>
Rebuild and run the app. Visit the new Todo page to confirm that the link to the Todo
component works.
Add a TodoItem.cs file to the root of the project to hold a class that represents a todo item. Use the following C# code for the TodoItem
class:
public class TodoItem
{
public string Title { get; set; }
public bool IsDone { get; set; }
}
Return to the Todo
component (Pages/Todo.razor):
@code
block. The Todo
component uses this field to maintain the state of the todo list.foreach
loop to render each todo item as a list item (<li>
).@page "/todo"
<h1>Todo</h1>
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
@code {
private IList<TodoItem> todos = new List<TodoItem>();
}
The app requires UI elements for adding todo items to the list. Add a text input (<input>
) and a button (<button>
) below the unordered list (<ul>...</ul>
):
@page "/todo"
<h1>Todo</h1>
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
<input placeholder="Something todo" />
<button>Add todo</button>
@code {
private IList<TodoItem> todos = new List<TodoItem>();
}
Rebuild and run the app. When the Add todo button is selected, nothing happens because an event handler isn’t wired up to the button.
Add an AddTodo
method to the Todo
component and register it for button selections using the @onclick
attribute. The AddTodo
C# method is called when the button is selected:
<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>
@code {
private IList<TodoItem> todos = new List<TodoItem>();
private void AddTodo()
{
// Todo: Add the todo
}
}
To get the title of the new todo item, add a newTodo
string field at the top of the @code
block and bind it to the value of the text input using the bind
attribute in the <input>
element:
```
private IList<TodoItem> todos = new List<TodoItem>();
private string newTodo;
```
```
<input placeholder="Something todo" @bind="newTodo" />
```
AddTodo
method to add the TodoItem
with the specified title to the list. Clear the value of the text input by setting newTodo
to an empty string:```
@page "/todo"
<h1>Todo</h1>
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>
@code {
private IList<TodoItem> todos = new List<TodoItem>();
private string newTodo;
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
}
```
Rebuild and run the app. Add some todo items to the todo list to test the new code.
The title text for each todo item can be made editable, and a check box can help the user keep track of completed items. Add a check box input for each todo item and bind its value to the IsDone
property. Change @todo.Title
to an <input>
element bound to @todo.Title
:
```
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<input @bind="todo.Title" />
</li>
}
</ul>
```
<h1>
header to show a count of the number of todo items that aren’t complete (IsDone
is false
).```
<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
```
Todo
component (Pages/Todo.razor):```
@page "/todo"
<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<input @bind="todo.Title" />
</li>
}
</ul>
<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>
@code {
private IList<TodoItem> todos = new List<TodoItem>();
private string newTodo;
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
}
```
#C# #blazor #aspdotnet