如何将异步值分配给Razor组件〔参数〕属性



我正试图在Blazor服务器端项目中创建我的第一个Razor组件。Razor组件名为MyComponent,其属性配置为从输入中检索其值:

MyComponent.razor

[Parameter]
public int Count {get; set;}

我从通过IServiceCollection配置的注入服务中提取计数,如下所示:

public interface ICountingService
{
ValueTask<int> Get();
}

托管页面Index.razor如下所示:

@page "/"
@inject ICountingService Counter
<h1>Hello World!</h1>
<MyComponent Count="@Counter.Get()" />

但是,我似乎无法为Count属性绑定正确的值。

我得到以下错误:

cannot convert from 'System.Threading.Tasks.ValueTask<int>' to 'int'

我发现的所有将[Parameter]值分配给RazorComponents的示例都是同步的,我发现的唯一异步值是回调和方法(而不是参数(。

此外,在网上搜索并没有返回任何明显的结果,所以我在这里发帖,希望能找到答案。

请注意,我知道使用protected override async Task OnInitializedAsync并在其中存储值,但与上面的方法相比,这似乎需要很多仪式,尤其是在考虑到我最终必须绑定的多个服务和属性时。

那么,如何以我喜欢的方式将异步调用中的值分配给RazorComponent[Parameter]属性呢?

问题是,Counter.Get()不是一个int值;这是一个任务,无论是现在还是将来,它都会在某个未定义的点上有一个int。所以你不能把它的值分配给现在需要一个int的东西,因为这个int还不一定存在。

你已经得到了答案,尽管这"看起来很有仪式感",但这确实是唯一的方法:

  • 创建一个int属性来保存该值
  • 声明一个异步方法
  • 在该方法中,将Counter.Get()awaited值分配给保存该值的int
  • 将组件的Count属性设置为int属性

这可能感觉像是一种仪式,但你应该心存感激。异步从本质上来说是非常复杂的,拥有可用的异步/等待已经为您完成了大约95%的艰苦工作。如果你认为这个解决方案很混乱,你应该看看如果没有async/await,需要什么才能把它做好

试试这个。

@page "/"
@inject ICountingService Counter
<h1>Hello World!</h1>
<MyComponent Count="@CounterValue" />
@code{

public int CounterValue {get; set;}
protected override async Task OnInitializedAsync()
{
CounterValue = await Counter.Get();
}
}

在@mason-wheeler和@rich-bryant提供了他们的答案后,我仔细思考了一下,找到了我的解决方案,我已经发布在这里:

https://github.com/Mike-E-angelo/Blazor.ViewProperties

我称之为ViewProperty,它看起来如下:

public interface IViewProperty
{
ValueTask Get();
}
public sealed class ViewProperty<T> : IViewProperty
{
public static implicit operator ViewProperty<T>(ValueTask<T> instance) => new ViewProperty<T>(instance);
readonly ValueTask<T> _source;
public ViewProperty(ValueTask<T> source) => _source = source;
public T Value { get; private set; }
public bool HasValue { get; private set; }
public async ValueTask Get()
{
Value    = await _source;
HasValue = true;
}
public override string ToString() => Value.ToString();
}

然后将其与组件基本类型配对,然后组件基本类型遍历组件的视图属性并调用它们各自的异步操作:

public abstract class ViewPropertyComponentBase : ComponentBase
{
protected override async Task OnParametersSetAsync()
{
var properties = GetType().GetRuntimeProperties();
foreach (var metadata in properties.Where(x => x.GetCustomAttributes<ParameterAttribute>().Any() &&
typeof(IViewProperty).IsAssignableFrom(x.PropertyType)))
{
if (metadata.GetValue(this) is IViewProperty property)
{
await property.Get().ConfigureAwait(false);
}
}
}
}

使用以上内容的剃须刀组件示例:

MyComponent.razor

@inherits ViewPropertyComponentBase
@if (Count.HasValue)
{
<p>Your magic number is @Count.</p>
}
else
{
<p>Loading, please wait...</p>
}

@code {
[Parameter]
public ViewProperty<int> Count { get; set; }
}

由此产生的使用是一个具有直接绑定的干净视图,不需要重写或其他额外的仪式:

@page "/"
@inject ICounter Count
<h1>Hello, world!</h1>
Welcome to your new app.
<MyComponent Count="@Count.Count()" />

(请注意,我发布的示例和上面的示例使用了反射,这很慢。在我使用的解决方案的实际版本中,我将成员访问编译为lambda表达式并缓存结果。如果你足够勇敢,可以从这里开始。(

这感觉有点粗糙,但你可以这样做:

<MyComponent Count="@Counter.Get().Result" />

相关内容

  • 没有找到相关文章

最新更新