DEV Community

loading...

【Blazor Server】CSS Isolation & using child components

Masui Masanori
Programmer, husband, father I love C#, TypeScript, etc.
Updated on ・5 min read

Intro

This time, I try CSS isolatio and creating child conponents in a component.

Environments

  • .NET ver.5.0.101

Base Project

I create a project like them.
I use ASP.NET Core MVC and Blazor Server.

HomeController.cs

using System;
using Microsoft.AspNetCore.Mvc;

namespace BlazorSample.Controllers
{
    public class HomeController: Controller
    {
        [Route("")]
        [Route("{page}")] // <- DON'T DO THIS for CSS Isolation
        public ActionResult OpenPage(string page)
        {
            ViewData["Title"] = $"Page {page}";
            return View("Views/_Host.cshtml");         
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
    <base href="~/" />
</head>
<body>
    @RenderBody()
    <script src="_framework/blazor.server.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

_Host.cshtml

@namespace BlazorSample.Views
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
Enter fullscreen mode Exit fullscreen mode

App.razor

@using Shared
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
Enter fullscreen mode Exit fullscreen mode

MainLayout.razor

@inherits LayoutComponentBase
@Body
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor

@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<div id="sheet_area">
</div>
Enter fullscreen mode Exit fullscreen mode

CSS Isolation

From .NET 5, I can create CSS files for each Blazor files.
I have to do these two things.

1.Add CSS link to {ProjectName}.styles.css in _Layout.cshtml or _Host.cshtml.
2.Add {BlazorFileName}.css. For example, if Blazor file name is "DisplayGridPage.razor", the CSS file name is "DisplayGridPage.razor.css".

_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
    <base href="~/" />
    <!-- When the page is loaded, the content of CSS will be changed -->
    <link href="BlazorSample.styles.css" rel="stylesheet" />
</head>
<body>
    @RenderBody()
    <script src="_framework/blazor.server.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor.css

#sheet_area
{
    height: 30vh;
    width: 30vw;
    background-color: aqua;
    display: grid;
}
h1{
    color: blue;
}
Enter fullscreen mode Exit fullscreen mode

Get HTML named CSS?

Unfortunately, the CSS wasn't work.

Because in the Controller class, I routed "localhost:5000/" and "localhost:5000/{page}" to return View.
So "localhost:5000/BlazorSample.styles.css" was also treated as View.

Though I might be able to resolve by using middleware, I decided change the route.

HomeController.cs

...
namespace BlazorSample.Controllers
{
    public class HomeController: Controller
    {
        [Route("")]
        [Route("Pages/{page}")]
        public ActionResult OpenPage(string page)
...
Enter fullscreen mode Exit fullscreen mode

Generated CSS

When I add CSS for MainLayout.razor, the CSS for DisplayGridPage.razor will be generated from MainLayout.razor.css and DisplayGridPage.razor.css

/* _content/BlazorSample/Views/DisplayGridPage.razor.rz.scp.css */
#sheet_area[b-p832tuedyv]
{
    height: 30vh;
    width: 30vw;
    background-color: aqua;
    display: grid;
}
h1[b-p832tuedyv]{
    color: blue;
}
/* _content/BlazorSample/Views/Shared/MainLayout.razor.rz.scp.css */
h1[b-m6a6nzx0h4]{
    color: red;
}
header[b-m6a6nzx0h4]{
    background-color: rosybrown;
}
Enter fullscreen mode Exit fullscreen mode

Using components

Next, I try add child components.
There is no any special way to create and add child components.

1.Create Blazor files(.razor, .razor.cs, .razor.css)

Cell.razor

<div class="cell_frame">Hello cell</div>
Enter fullscreen mode Exit fullscreen mode

Cell.razor.cs

using Microsoft.AspNetCore.Components;

namespace BlazorSample.Views.Components
{
    public partial class Cell
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Cell.razor.css

.cell_frame{
    background-color: gray;
    border: 1px solid black;
    color: goldenrod;
}
Enter fullscreen mode Exit fullscreen mode

2.Add 1. into the parent component as a HTML element.

DisplayGridPage.razor

@page "/"

<h1>Hello, world!</h1>
Welcome to your new app.
<div id="sheet_area">
    @for(var i = 1; i <= 3; i++){
        <BlazorSample.Views.Components.Cell></BlazorSample.Views.Components.Cell>
    }
</div>
Enter fullscreen mode Exit fullscreen mode

Result

Alt Text

Child component's CSS

In this sample, "BlazorSample.styles.css" includes "MainLayout.razor.css" and "DisplayGridPage.razor.css".
How about "Cell.razor.css" ?

All of them are merged and added into "BlazorSample.styles.css".

BlazorSample.styles.css

/* _content/BlazorSample/Views/Components/Cell.razor.rz.scp.css */
.cell_frame[b-64je09gotq]{
    background-color: gray;
    border: 1px solid black;
    color: goldenrod;
}
/* _content/BlazorSample/Views/Components/SheetSelector.razor.rz.scp.css */
/* _content/BlazorSample/Views/DisplayGridPage.razor.rz.scp.css */
#sheet_area[b-p832tuedyv]
{
    height: 30vh;
    width: 30vw;
    background-color: aqua;
    display: grid;
}
h1[b-p832tuedyv]{
    color: blue;
}
/* _content/BlazorSample/Views/Shared/MainLayout.razor.rz.scp.css */
h1[b-m6a6nzx0h4]{
    color: red;
}
header[b-m6a6nzx0h4]{
    background-color: rosybrown;
}
Enter fullscreen mode Exit fullscreen mode

Set values from the parent

When I want to set values into the child components, I can add parameters into them.

Cell.razor.cs

using System;
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Views.Components
{
    public partial class Cell
    {
        [Parameter]
        public int Index { get; set; }
        [Parameter]
        public BlazorSample.Spreadsheets.Cell CellValue{ get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Cell.razor

<div class="cell_frame" id="cell_@Index">Hello cell@(Index)</div>
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor.cs

...
    public partial class DisplayGridPage
    {
        [Parameter]
        public List<BlazorSample.Spreadsheets.Cell> Cells { get; set; }

        public DisplayGridPage()
        {
            Cells = new List<Cell>
            {
                new Cell(1, 1, "Hello"),
                new Cell(1, 2, "World"),
            };
        }
...
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor

...
<div id="sheet_area">
    @code
    {
        int index = 1;
    }
    @foreach (var cell in Cells)
    {
        <BlazorSample.Views.Components.Cell Index="index" CellValue="cell">
        </BlazorSample.Views.Components.Cell>
        index += 1;
    }
</div>
...
Enter fullscreen mode Exit fullscreen mode

When the parameter values will be set?

When the constructor is called, the parameters has not been set their values.
If I want to do something on initializing, I have to override Blazor event methods.

Cell.razor.cs

...
    public partial class Cell
    {
        [Parameter]
        public int Index { get; set; }
        [Parameter]
        public BlazorSample.Spreadsheets.Cell CellValue{ get; set; }
        public Cell()
        {
            // the values haven't set yet.
            // Output: [Constructor] CellValue Index: 0 Cell?:True
            Console.WriteLine($"[Constructor] CellValue Index: {Index} Cell?:{CellValue == null}" );
        }
        protected override void OnInitialized()
        {
            // Output: [Init] CellValue Index: 1 Cell?:False
            Console.WriteLine($"[Init] CellValue Index: {Index} Cell?:{CellValue == null}" );
        }
    }
...
Enter fullscreen mode Exit fullscreen mode

I think Blazor's grammer is similar to Angular.

Callback

I can use "EventCallback" to call from child components to the parent component.

Cell.razor.cs

...
    public partial class Cell
    {
...
        [Parameter]
        public EventCallback<string> OnCellClicked { get; set; }
        public async Task OnClick()
        {
            await OnCellClicked.InvokeAsync();
        }
    }
...
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor

...
    @foreach (var cell in Cells)
    {
        <BlazorSample.Views.Components.Cell Index="index" CellValue="cell"
            OnCellClicked="@((message) => Console.WriteLine(message))">
        </BlazorSample.Views.Components.Cell>
        index += 1;
    }
...
Enter fullscreen mode Exit fullscreen mode

Escape "" in Blazor

I can use two ways to use ""(double quotations) in Blazor files.

        <!-- Use @() -->
        <BlazorSample.Views.Components.Cell Index="index" CellValue="cell"
            OnCellClicked="@(_ => Console.WriteLine("World"))">
        </BlazorSample.Views.Components.Cell>
        <!-- Use ''(single quotations) -->
        <BlazorSample.Views.Components.Cell Index="index" CellValue="cell"
            OnCellClicked='_ => Console.WriteLine("World")'>
        </BlazorSample.Views.Components.Cell>
Enter fullscreen mode Exit fullscreen mode

Call child components' methods

I can control child components' instances by @ref.

Cell.razor.cs

...
    public partial class Cell
    {
...
        public void Greet()
        {
            Console.WriteLine("Hello World!");
        }
...
Enter fullscreen mode Exit fullscreen mode

DisplayGridPage.razor

...
    <BlazorSample.Views.Components.Cell Index="10" 
            CellValue='new BlazorSample.Spreadsheets.Cell(10, 10, "HelloWorld")'
            OnCellClicked='_ => Console.WriteLine("World")' @ref="cell">
        </BlazorSample.Views.Components.Cell>
</div>
<button @onclick="ClickSample">Click</button>
@code
{
    BlazorSample.Views.Components.Cell cell;
    void ClickSample()
    {
        cell.Greet();
    }
}
Enter fullscreen mode Exit fullscreen mode

Resources

CSS

Components

Discussion (0)