.Net Core开发学习(四) ——Blazor(MVVM)应用


MVVM(Model-View-ViewModel)

MVVM,双向数据绑定。既 模型数据页面显示的数据 双向绑定,改变视图数据 也会 改变模型数据
使用Blazor框架可以减少很多Js脚本代码甚至可以无Js脚本实现快速开发。当然,想要做到这点,必须浏览器与服务器保持实时连接,所以Blazor的底层是基于WebSocket的。

现在比较出名的mvvm架构的框架有vue.js,angular.js等。


创建Blazor应用

创建应用
创建应用1

创建完成后运行一下

运行结果
OK,项目启动成功


项目结构

项目结构
项目结构和web应用大体很像,可以参考.Net Core开发学习(二) ——Web应用,现在我来讲讲不同之处。

Razor组件:在Blazor应用中有很多以 .razor 结尾,这其实是 Razor组件。Razor组件是一种可重用的视图组件,类似于用户控件,可以很方便的嵌入到其他视图,在MVC应用中也可以使用。

_Host.cshtml:全局布局文件,在Startup.cs中可以看到endpoints.MapFallbackToPage("/_Host")。
在app标签中component指定如何绘制组件。

呈现模式描述
ServerPrerendered在静态 HTML 中呈现 App 组件,并包含 Blazor Server 应用的标记。 用户代理启动时,此标记用于启动 Blazor 应用。
Server呈现 Blazor 服务器应用的标记。 不包括 App 组件的输出。 用户代理启动时,此标记用于启动 Blazor 应用。
Static在静态 HTML 中呈现 App 组件。

App.razor:全局应用配置文件,包括路由、Layout、用户验证等配置和返回样式,详情可见Microsoft.AspNetCore.Components命名空间。

_Imports.razor:全局引用命名空间配置文件。


Razor组件

打开文件:Pages > Index.razor

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<!--使用 SurveyPrompt组件-->
<SurveyPrompt Title="How is Blazor working for you?" />

@page :声明一个可处理请求的文件,"/" 指定路由

我们看到使用了 <SurveyPrompt /> 这个Razor组件。

打开文件:Pages > SurveyPrompt.razor,代码如下

<div class="alert alert-secondary mt-4" role="alert">
    <span class="oi oi-pencil mr-2" aria-hidden="true"></span>
    <strong>@Title</strong>

    <span class="text-nowrap">
        Please take our
        <a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2112271">brief survey</a>
    </span>
    and tell us what you think.
</div>

@code {
    // Demonstrates how a parent component can supply parameters
    [Parameter]
    public string Title { get; set; }
}

@code 中,声明了一个参数 Title,使用 [Parameter] 属性修饰。


运行效果

运行效果

可以看到,SurveyPrompt.razor 已经出现在了 Index.razor 中。


数据绑定

(1) 单项绑定

打开文件: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++;
    }
}

可以看到,代码 <p> 标签中显示 currentCount,当 点击button 时执行 IncrementCount() 方法。


运行效果
运行效果

(2) 双项绑定

创建文件:Pages > FormTest.razor

@page "/FormTest"

<h1>FormTest</h1>

姓名:
<input type="text" @bind="@Name" />
@switch (nameError)
{
    case 1:
        <div>警告:姓名不能小于3位大于5位</div>
        break;
}
<br />
生日:
<input type="datetime" @bind="@Birthday" />
@switch (birthdayError)
{
    case 1:
        <div>警告:请输入一个正确的生日</div>
        break;
}
<br />
<br />
<br />
输出
<br />
姓名:@Name
<br />
生日:@Birthday
@code {
    private string name = "";
    private string Name
    {
        get { return name; }
        set
        {
            if (value.Length < 3 || value.Length > 5)
            {
                nameError = 1;
            }
            else
            {
                nameError = -1;
                name = value;
            }
        }
    }
    private int nameError = -1;

    private DateTime birthday = new DateTime(2001, 1, 1);
    private DateTime Birthday
    {
        get { return birthday; }
        set
        {
            if (value < new DateTime(1900, 1, 1) || value > DateTime.Now)
            {
                birthdayError = 1;
            }
            else
            {
                birthdayError = -1;
                birthday = value;
            }
        }
    }
    private int birthdayError = -1;
}

运行效果

运行效果

是不是感觉跟wpf、winform等桌面应用差不多了,哈哈,在以前的web应用中想实现这些功能肯定是要写JS的,现在直接绑定就可以了。

数据加载

查看方法:Startup.cs > ConfigureServices(),代码如下

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    //添加一个单例WeatherForecastService依赖注入
    services.AddSingleton<WeatherForecastService>();
}

添加了一个 单例WeatherForecastService依赖注入 ,为页面提供数据服务。

打开文件:Pages > FetchData.razor,代码如下

@page "/fetchdata"

@using BlazorApp1.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <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>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

@inject:将依赖注入进页面。
OnInitializedAsync():异步初始化,同步使用 OnInitialized()

在OnInitializedAsync方法中将数据加载入forecasts,再绑定数据到页面上。


Blazor生命周期

初始化事件: 在组件从其父组件接收初始参数后初始化将调用 OnInitializedAsync 和 OnInitialized。

OnInitialized():组件初始化方法(同步),写法如下。

protected override void OnInitialized()
{
    ...
}

OnInitializedAsync():组件初始化方法(异步),写法如下。

protected override async Task OnInitializedAsync()
{
    await ...
}

参数设置事件: 在组件已接收到的参数从其父和值被分配给属性被调用,并且初始化后每次呈现组件都将执行,OnParametersSetAsync 和 OnParametersSet。

OnParametersSet():参数设置方法(同步),写法如下。

protected override void OnParametersSet()
{
	...
}

OnParametersSetAsync():参数设置方法(异步),写法如下。

protected override async Task OnParametersSetAsync()
{
    await ...
}

渲染完成事件: 在组件完成渲染后调用,OnAfterRenderAsync 和 OnAfterRender。

OnAfterRender():渲染完成方法(同步),写法如下。

protected override void OnAfterRender(bool firstRender)
{
	...
}

OnAfterRenderAsync():渲染完成方法(异步),写法如下。

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    await ...
}

布局

默认布局

打开文件:App.razor

<Router AppAssembly="@typeof(Program).Assembly">
    <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>

Router > Found > RouteView[DefaultLayout] 处设置布局


使用指定布局

创建文件: Share > TestLayout.razor

@inherits LayoutComponentBase
<h3 style="margin: 15px;">TestLayout</h3>

<div class="main" style="margin:15px;">
    @Body
</div>

@code {

}

修改文件: Pages > Error.razor

在文件顶部添加一行代码

@layout TestLayout

运行项目,地址栏输入 /error,效果如下
运行效果
因为页面与Layout都是组件,所以布局也可以嵌套,大家可自行尝试。


数据验证

创建文件: Data > User.cs

public class User
{
    [Required]
    public string Name { get; set; }
}

创建文件: Page > Users > CreateUser.razor

@page "/User/Create"
<h3>CreateUser</h3>

<EditForm Model="@user" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText id="name" @bind-Value="user.Name" />

    <button type="submit">Submit</button>
</EditForm>

@code {
    private Data.User user = new Data.User();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }
}

运行效果
运行效果

OK,一个简单的Blazor应用就完成啦。

Logo

前往低代码交流专区

更多推荐