Sau*_*abh 2 c# blazor blazor-server-side asp.net-core-3.0 .net-core-3.0
引用此VisualStudioMagazine文章,我试图将代码放在单独的文件中,而不是剃刀视图中。
我试过了:
@page "/Item"
@using WebApplication1.Shared
@using WebApplication1.Client.Services;
@inject HttpClient Http
@inherits ItemComponent
@if (ItemList != null)
{
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Category</th>
<th>Metal</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
@foreach (var item in ItemList)
{
<tr>
<td>@item.ID</td>
<td>@item.Name</td>
<td>@item.Category</td>
<td>@item.Metal</td>
<td>@item.Price</td>
<td>@item.Quantity</td>
</tr>
}
</tbody>
</table>
}
@functions{
public ItemModel[] ItemList;
ItemComponent IC = new ItemComponent();
protected override async Task OnInitAsync()
{
ItemList = IC.GetItems().Result;
//ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
StateHasChanged();
}
}
Run Code Online (Sandbox Code Playgroud)
和ItemComponent:
using System.Threading.Tasks;
using WebApplication1.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Blazor;
namespace WebApplication1.Client.Services
{
public class ItemComponent
{
public async Task<ItemModel[]> GetItems()
{
ItemModel[] ItemList;
HttpClient Http = new HttpClient();
ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
return ItemList;
}
}
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,它表明:
严重性代码说明项目文件行抑制状态错误CS0115'Item.BuildRenderTree(RenderTreeBuilder)':找不到合适的方法来覆盖WebApplication1.Client D:\ Other \ blazor \ WebApplication1.Client \ obj \ Debug \ netstandard2.0 \ RazorDeclaration \ Pages \ ItemModule \ Item.razor.g.cs 30有效
另外按照教程页面不能继承BlazorComponent,ItemComponent因为它没有参考。
有什么方法可以将Blazor视图中的大多数代码分离到单独的代码文件中?
更新1
根据Chris Answer进行更改后,它显示异常
System.Net.Http.HttpRequestException:无法建立连接,因为目标计算机主动拒绝了它。---> System.Net.Sockets.SocketException:无法建立连接,因为目标计算机主动拒绝了它。在System.Net.Http.ConnectHelper.ConnectAsync(字符串主机,Int32端口,CancellationToken cancelleToken)-内部异常堆栈跟踪的结尾-在System.Net.Http.ConnectHelper.ConnectAsync(字符串主机,Int32端口,CancellationToken System.Threading.Tasks.ValueTask上的CancellationToken)1. System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request,CancellationToken cancellingToken)上System.Net.Http.HttpConnectionPool.getgetResult()
1.get_Result() at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask1.get_Result() at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask1.get_Result()在System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage请求,布尔doRequestAuth,的CancellationToken的CancellationToken)
在System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage请求,的CancellationToken的CancellationToken)在System.Net.Http.HttpClient1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at System.Net.Http.HttpClient.GetStringAsyncCore(TaskMicrosoft.AspNetCore.Builder.BlazorMonoDebugProxyAppBuilderExtensions.DebugHome(HttpContext上下文)的.FinishSendAsyncUnbuffered(任务1 getTask)在Microsoft.AspNetCore.Builder.BlazorMonoDebugProxyAppBuilderExtensions.GetOpenedBrowserTabs(String debuggerHost)在Microsoft.AspNetCore.Builder.BlazorMonoDebugProxyAppBuilderExtensions.DebugHome(HttpContext context)
这里还有另一个解决方案,类似于Louis Hendrick 的观点:
如果您需要或希望将页面生命周期代码与数据绑定分开,您可以在同一 Razor 视图上使用继承的代码和注入的 ViewModel。
近年来,有很多关于使用“状态”概念管理应用程序当前状态的讨论。自 Flux 模式(尤其是 Redux 实现)兴起以来,这在 React(以及现在的其他 JS 框架)世界中特别流行。
视图模型通常表示特定页面的状态,并且通常包含与该页面如何呈现相关的属性(例如,用于选择列表的数据、用于说明页面部分是否应该可见的额外属性等。 ) 以及一个属性,该属性包含要绑定在该页面上的数据的对象(例如一个SalesOrder类)。
基于状态的方法做了很多相同的事情,但不是按适用于(如视图模型那样)的页面对状态进行分组,基于状态的方法通常按行为对代码进行分组(例如,所有与订购披萨有关的状态,因此当前 Pizza 包含哪些内容以及如果订单正在处理中应显示哪些 UI 元素)并识别该状态可能由多个组件显示 - 因此 State 对象不一定直接映射到ViewModel 通常会采用的方式。
基于状态的方法有两个主要好处:
MyState.SaveButtonEnabled' property is true`来检查当数据类上的属性设置为某个值时按钮是否将被禁用。这比尝试通过 UI 自动化等测试行为要简单得多。举个例子就更容易了,谢天谢地,微软 Blazor 团队的Blazing Pizza 的 blazor-workshop提供了一个很棒的例子。
作为该教程中的一个快速示例 - 这是保存与正在进行的订单相关的当前状态的OrderState类:
Run Code Online (Sandbox Code Playgroud)public class OrderState { public event EventHandler StateChanged; public bool ShowingConfigureDialog { get; private set; } public Pizza ConfiguringPizza { get; private set; } public Order Order { get; private set; } = new Order(); public void ShowConfigurePizzaDialog(PizzaSpecial special) { ConfiguringPizza = new Pizza() { Special = special, SpecialId = special.Id, Size = Pizza.DefaultSize, Toppings = new List<PizzaTopping>(), }; ShowingConfigureDialog = true; } public void CancelConfigurePizzaDialog() { ConfiguringPizza = null; ShowingConfigureDialog = false; StateHasChanged(); } public void ConfirmConfigurePizzaDialog() { Order.Pizzas.Add(ConfiguringPizza); ConfiguringPizza = null; ShowingConfigureDialog = false; StateHasChanged(); } public void RemoveConfiguredPizza(Pizza pizza) { Order.Pizzas.Remove(pizza); StateHasChanged(); } public void ResetOrder() { Order = new Order(); } private void StateHasChanged() { StateChanged?.Invoke(this, EventArgs.Empty); } } ```
请注意,此状态类没有绑定到它的 UI 的概念,但它确实具有控制 UI 行为的属性。
在该示例中,razor 类仍然具有 @functions 块,但是通过在 State 类中引入在控制 UI 行为(例如ShowingConfigureDialog)中具有显式作用的属性,它们被大大简化了。例如,来自index.razor:
Run Code Online (Sandbox Code Playgroud)<ul class="pizza-cards"> @if (specials != null) { @foreach (var special in specials) { <li onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')"> <div class="pizza-info"> <span class="title">@special.Name</span> @special.Description <span class="price">@special.GetFormattedBasePrice()</span> </div> </li> } } </ul> </div> ```
整个教程都很棒,我强烈建议您完成它。
您仍然可以将@code块中的代码放在基类文件中,也可以使用状态方法。
人们倾向于不这样做的原因是,如果您的状态文件正在驱动 UI 行为,那么@code接线代码通常最终只有几行,因此通常似乎不值得放入单独的文件中。
你只需要继承ComponentBase你的ItemComponent类像这样。
public class ItemComponent : ComponentBase
{
public async Task<ItemModel[]> GetItems()
{
ItemModel[] ItemList;
HttpClient Http = new HttpClient();
ItemList = await Http.GetJsonAsync<ItemModel[]>("api/Item/GetItems");
return ItemList;
}
}
Run Code Online (Sandbox Code Playgroud)
这篇文章有点过时BlazorComponent了,不久前已将其重命名。
只要确保将functions视图块中的所有代码都移到基类中即可,因为两种方法的混合使用可能会产生奇怪的副作用。
小智 5
您有两个选择。克里斯·桑迪(Chris Sainty)已经提到了第一个。创建一个从ComponentBase继承的类,并在您的Razor视图中继承它。
您的课程将定义为:
public class MyBaseClass : ComponentBase
在剃刀视图中使用:
@inherits MyBaseClass
这使MyBaseClass成为Razor视图的页面后方代码,并且能够覆盖该视图的所有生命周期事件。
第二个选项是创建一个ViewModel。您创建一个标准的C#类,并使用属性注入将其注入到Razor视图中。
您通常可以定义类:
public class MyViewModel
并将其注入到您的Razor视图中:
@inject MyViewModel
该ViewModel类不知道页面生命周期事件,并且与Blazor相关的任何内容都不相关。如果您只想将Razor视图绑定到一个对象,并且需要一些可以重用的对象(或想将其放在共享项目中),那么这是一个不错的选择。
如果需要或要使页面生命周期代码与数据绑定分开,则可以在同一Razor View上使用后面的继承代码和注入的ViewModel。
我通过创建一个继承自 ComponentBase 的类并简单地从组件中的该基类继承来阅读有关父类方法的文章。我不是粉丝,因为它迫使我向类公开应该在内部/私下维护的类结构,并且跟踪保护的继承我认为是正确的答案。
但是,我可能在这里遗漏了一些东西,所以请不要因为我推荐这个而屠杀我,但是为什么你不能只使用部分指令,创建一个 ComponentName.razor.cs 的“sidecar”(我的术语)文件并简单地声明类作为部分类。我试过这个,它工作得很好......
使用当前编写的模板项目,在 Counter 组件中,我简单地剥离了所有代码,结果如下:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Run Code Online (Sandbox Code Playgroud)
然后我继续创建边车文件 Counter.razor.cs 并填充:
using Microsoft.AspNetCore.Components;
namespace FirstBlazorWasm.Pages //my test namespace
{
public partial class Counter //<--- note the partial class definition
{
private int currentCount;
private void IncrementCount()
{
currentCount++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
叫我先生 2003 年,但它有效。:)