Mateusz Turzyński / Michał Michalczuk
@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++;
}
}
App.dll
.NET
(mscorlib.dll, System.core.dll etc)
WebAssembly
(mono.wasm)
.cs
.razor
Kompilacja
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<InputNumber @bind="@currentCount" />
<button class="btn btn-primary" @onclick="IncrementCount">
Click me
</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
public class PersonModel
{
[Required]
[Range(18, 60,
ErrorMessage = "Either too old or too young :(")]
public int Age { get; set; }
[Required]
[MinLength(5)]
[MaxLength(15)]
public string FullName { get; set; }
public string Password { get; set; }
[Compare(nameof(Password))]
public string RepeatPassword { get; set; }
}
@inject HttpClient Http
@code {
private PersonModel person = new PersonModel();
private async Task HandleValidSubmit()
{
await Http.PostJsonAsync("/api", person);
}
private void HandleInvalidSubmit()
{
Console.WriteLine("The form is not valid :(");
}
}
<EditForm Model="person"
OnValidSubmit="@HandleValidSubmit"
OnInvalidSubmit="@HandleInvalidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<h1>Personal form</h1>
<p>
<label>Enter your age:</label>
<InputNumber @bind-Value="person.Age" />
</p>
<p>
<label>Enter your name:</label>
<InputText @bind-Value="@person.FullName" />
<ValidationMessage For="@(() => person.FullName)" />
</p>
(...)
<p>
<button type="submit">Submit</button>
</p>
</EditForm>
<span>Colored span</span>
<div>Div with background</div>
<a href="#">Sample link with background</a>
<Styled>
span {
color: pink:
}
div {
background: green;
color: white;
}
a {
background: blue;
}
</Styled>
@code {}
@page "/simple-routing/{text}"
@page "/advanced-routing/{counter:int}"
<p>Text from parameter: @Text</p>
@if (Counter.HasValue)
{
<p>Counter: @Counter</p>
}
@code {
[Parameter]
public string Text { get; set; }
[Parameter]
public int? Counter { get; set; }
}
<NavLink href="simple-form"
Match="NavLinkMatch.All"
class="nav-link">
Simple form
</NavLink>
<NavLink href="simple-routing/test"
Match="NavLinkMatch.Prefix"
class="nav-link" >
Simple routing
</NavLink>
<div>
<p>Hello @FullName</p>
<button @onclick="OnDeleteAccount">
Delete my account
</button>
</div>
@code {
[Parameter]
public string FullName { get; set; }
[Parameter]
public EventCallback OnDeleteAccount { get; set; }
}
@page "/person"
@if (accountDeleted)
{
<p>Account deleted</p>
}
else
{
<PersonDetails FullName="John Rambo"
OnDeleteAccount="() => AccountDeleted()" />
}
@code {
private bool accountDeleted;
private void AccountDeleted()
{
accountDeleted = true;
}
}
// index.html
<script>
window.getElementDimensions = selector => {
try {
const element = document.querySelector(selector);
if (!element) {
return null;
}
return {
height: element.scrollHeight,
width: element.scrollWidth
};
} catch (ex) {
return null;
}
};
</script>
// UseJavaScript.razor
@inject IJSRuntime JSRuntime;
...
@code {
class BrowserSize {
public int Height { get; set; }
public int Width { get; set; }
}
private string Selector { get; set; } = "body";
private BrowserSize ClientSize { get; set; } = new BrowserSize();
private async Task UpdateSize()
{
var jsResult = await JSRuntime.InvokeAsync<BrowserSize>(
"getElementDimensions",
Selector
);
ClientSize = jsResult != null ? jsResult : new BrowserSize();
StateHasChanged();
}
}
// index.js
import { Chart } from 'chart.js';
import 'chartjs-plugin-labels';
export const charts = {
drawChart: (canvasSelector, type, { data = [], labels = [] }) => {
const canvas = document.querySelector(canvasSelector).getContext('2d');
new Chart(canvas, {
....
});
}
};
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
},
output: {
path: path.resolve(__dirname, '../wwwroot/js'),
filename: 'js-lib.js',
library: 'FrontNetJS'
}
};
@inject IJSRuntime JSRuntime;
<canvas id="chart-canvas"></canvas>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await InitializeChart();
}
}
private async Task InitializeChart()
{
await JSRuntime.InvokeVoidAsync("FrontNetJS.charts.drawChart",
"#chart-canvas",
"pie",
new {
Data = new List<int> { 100, 200, 300, 400},
Labels = new List<string> { "A", "B", "C", "D" }
}
);
StateHasChanged();
}
}
Browser
UI Thread
Blazor
Blazor jest jednowątkowy
vs
Obecnie: nigdzie
Przyszłość: SPA
Daleka przyszłość: tam gdzie potrzebna jest wydajność (dzięki AoT)
Obecnie: intranet, dashboardy
Przyszłość: prawdopodobnie zniknie po wydaniu Blazor WASM