如何将异步值分配给 Razor 组件 [参数] 属性?
How to Assign Async Value to Razor Component [Parameter] Property?
我正在尝试在 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'
我找到的所有为 Razor 组件分配 [Parameter]
值的示例都是同步的,我发现的唯一异步值是用于回调和方法(不是参数)。
此外,在线搜索没有return任何明显的东西,所以我在这里发帖希望找到答案。
请注意,我知道使用 protected override async Task OnInitializedAsync
并在其中存储一个值,但与上述方法相比,这似乎需要很多仪式,尤其是考虑到我将要使用的多种服务和属性时最终还是要绑定。
那么,如何以我喜欢的方式将异步调用的值分配给 Razor 组件 [Parameter]
属性?
试试这个。
@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();
}
}
问题是,Counter.Get()
不是一个整数值;这是一个现在或将来在某个未定义点有一个 int 的任务。所以你不能把它的值赋给现在需要 int 的东西,因为 int 不一定存在。
您已经找到了答案,虽然它 "seems like a lot of ceremony",但它确实是唯一的方法:
- 创建一个 int 属性 来保存值。
- 声明一个异步方法
- 在该方法中,将
Counter.Get()
的 await
ed 值分配给保存值 的 int
- 设置组件的 Count 属性 等于 int 属性
可能感觉有点仪式感,但你应该心存感激。异步本质上是非常复杂的,并且 async/await 可用已经为您解决了大约 95% 的艰苦工作。如果你认为这个解决方案很混乱,你应该看看如何才能把它弄好 without async/await!
在@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()" />
(请注意我的 posted example and above uses reflection, which is slow. In the actual version of the solution that I am using, I compile the member access as lambda expressions and cache the result. You can find that by starting here 如果你足够勇敢地四处闲逛。)
感觉有点老套,但你可以这样做:
<MyComponent Count="@Counter.Get().Result" />
我正在尝试在 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'
我找到的所有为 Razor 组件分配 [Parameter]
值的示例都是同步的,我发现的唯一异步值是用于回调和方法(不是参数)。
此外,在线搜索没有return任何明显的东西,所以我在这里发帖希望找到答案。
请注意,我知道使用 protected override async Task OnInitializedAsync
并在其中存储一个值,但与上述方法相比,这似乎需要很多仪式,尤其是考虑到我将要使用的多种服务和属性时最终还是要绑定。
那么,如何以我喜欢的方式将异步调用的值分配给 Razor 组件 [Parameter]
属性?
试试这个。
@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();
}
}
问题是,Counter.Get()
不是一个整数值;这是一个现在或将来在某个未定义点有一个 int 的任务。所以你不能把它的值赋给现在需要 int 的东西,因为 int 不一定存在。
您已经找到了答案,虽然它 "seems like a lot of ceremony",但它确实是唯一的方法:
- 创建一个 int 属性 来保存值。
- 声明一个异步方法
- 在该方法中,将
Counter.Get()
的await
ed 值分配给保存值 的 int
- 设置组件的 Count 属性 等于 int 属性
可能感觉有点仪式感,但你应该心存感激。异步本质上是非常复杂的,并且 async/await 可用已经为您解决了大约 95% 的艰苦工作。如果你认为这个解决方案很混乱,你应该看看如何才能把它弄好 without async/await!
在@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()" />
(请注意我的 posted example and above uses reflection, which is slow. In the actual version of the solution that I am using, I compile the member access as lambda expressions and cache the result. You can find that by starting here 如果你足够勇敢地四处闲逛。)
感觉有点老套,但你可以这样做:
<MyComponent Count="@Counter.Get().Result" />