Intro
This time I will try creating Single Page Application.
I want to know how to route Blazor pages, and if I can create hierarchical components, and etc..
Routing
First, I add "localhost:5000/" and "localhost:5000/{PageName}" as Blazor page routes.
HomeController.cs
...
[Route("/")]
[Route("/{page}")]
public ActionResult OpenPage(string page)
{
return View("Views/_Host.cshtml");
}
...
_Host.cshtml just calls a Blazor class for routing.
_Host.cshtml
@using BlazorSample.Views;
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
App.razor
@using BlazorSample.Views.Shared;
<Router AppAssembly="@typeof(Startup).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<p>Sorry, there's nothing at this address.</p>
</NotFound>
</Router>
MainLayout.razor is common layout for Blazor.
It's as same as _Layout.cshtml for Razor.
MainLayout.razor
@inherits LayoutComponentBase
@Body
SearchPage.razor
@page "/SearchPage";
<input type="text" @bind="productName">
<button @onclick="UpdateValue">Update</button>
@code{
public string productName = "";
public async Task UpdateValue()
{
productName = "Hello World!";
}
}
In Blazor, the page path is decided by "@page".
So I can access the SearchPage with "localhost:5000/SearchPage".
- ASP.NET Core Blazor routing | Microsoft Docs
- Create and use ASP.NET Core Razor components | Microsoft Docs
- Project structure for Blazor apps | Microsoft Docs
_Layout.cshtml
Now, the structure of SearchPage like below.
But if you don't use any other razor pages, you can remove "_ViewStart.cshtml" and merge "_Layout.cshtml" into "_Host.cshtml".
_Host.cshtml
@using BlazorSample.Views;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<base href="/">
<script type="text/javascript">
if (/MSIE \d|Trident.*rv:/.test(navigator.userAgent)) {
document.write('<script src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.closest%2CIntersectionObserver%2Cdocument.querySelector%2Cfeatures=Array.prototype.forEach%2CNodeList.prototype.forEach"><\/script>');
document.write('<script src="js/blazor.polyfill.min.js"><\/script>');
}
</script>
</head>
<body>
<div>Hello Host</div>
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
<script src="_framework/blazor.server.js"></script>
</body>
</html>
I also can write common layout in "MainLayout.razor".
But because I can't write <script>, I have to write it in "_Layout.cshtml" or "_Host.csthml".
ViewData
Because Blazor can't access "ViewData" and "ViewBag", so I have to control them in Controller classes or Razor files.
HomeController.cs
...
[Route("/")]
[Route("/{page}")]
public ActionResult OpenPage(string page)
{
ViewData["Title"] = GetTitle(page);
return View("Views/_Host.cshtml");
}
private string GetTitle(string page)
{
switch(page)
{
case "SearchPage":
return "Search";
default:
return "Home";
}
}
...
Send data from Router to Component
Last time, I could send date from Razor to Blazor by "[Parameter]".
How about from Router?
It is as same as from Razor to Blazor.
App.razor
@using BlazorSample.Views.Shared;
<Router AppAssembly="@typeof(Startup).Assembly">
<Found Context="routeData">
@{
var values = routeData.RouteValues as Dictionary<string, object> ?? new Dictionary<string, object>() ;
values.Add("Name", "Hello World");
var newRouteData = new RouteData(routeData.PageType, values);
}
<RouteView RouteData="@newRouteData" DefaultLayout="@typeof(MainLayout)" />
</Found>
...
</Router>
Similarly, I can set default page when the route isn't found.
@using BlazorSample.Views.Shared;
<Router AppAssembly="@typeof(Startup).Assembly">
...
<NotFound>
@{
var routeData = new RouteData(typeof(SearchPage), new Dictionary<string, object>());
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</NotFound>
</Router>
Create hierarchical components
In Angular, a component can have child components.
How about Blazor?
It's just as same as Angular.
SearchPage.razor
...
@foreach (var item in Products)
{
<SearchResultRow Product=item></SearchResultRow>
}
...
SearchResultRow.razor
@using Models;
<div class="search_result_row">
<div class="search_result_row_id">@Product.Id</div>
<div class="search_result_row_name">@Product.Name</div>
</div>
@code{
[Parameter]
public Product Product {get; set; }
}
Separate C# codes from Blazor HTML
When I create small components, I can write both HTML and C# codes in one file.
But when it become more larger and complicated, I want to separate them.
I can move C# codes by partial class.
SearchPage.razor.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Models;
namespace BlazorSample.Views
{
public partial class SearchPage
{
[Inject]
public Services.IBlazorService Blazor{ get; set; }
[Parameter]
public string Name {get; set;}
public string productName = "";
public List<Product> Products = new List<Product>
{
new Product
{
Id = 0,
Name = "Hello",
},
new Product
{
Id = 1,
Name = "World",
},
};
public async Task UpdateValue()
{
var product = await Blazor.GetMessageAsync("Hello");
Console.WriteLine(productName);
Console.WriteLine(Name);
productName = product.Name;
}
}
}
Now I can remove C# codes from "SearchPage.razor".
SearchPage.razor
@page "/SearchPage";
<input type="text" @bind="productName">
<button @onclick="UpdateValue">Update</button>
@foreach (var item in Products)
{
<SearchResultRow Product=item></SearchResultRow>
}
One important thing is making those namespaces the same.
Or "SearchPage.razor" can't find and get compiling errors.
Top comments (0)