如何解决无法绑定 Blazor 子组件下拉列表中的参数
我正在开发 ASP.NET Core (.NET 5.0) 服务器端 Blazor 应用程序。我正在尝试在模态中添加/编辑用户并在另一个模态中为用户分配角色。所以,我有一个父组件和两个子组件。
父组件:
@if (ShowPopup)
{
<!-- This is the popup to create or edit user -->
<EditUserModal ClosePopup="ClosePopup" CurrentUserRoles="" DeleteUser="DeleteUser" objUser="objUser" SaveUser="SaveUser" strError="@strError" />
}
@if (ShowUserRolesPopup)
{
<!-- This is the popup to create or edit user roles -->
<EditUserRolesModal AddRoletoUser="AddRoletoUser" AllRoles="AllRoles" ClosePopup="ClosePopup" CurrentUserRoles="@CurrentUserRoles" objUser="objUser" RemoveRoleFromUser="RemoveRoleFromUser" SelectedUserRole="@SelectedUserRole" strError="@strError" _allUsers="_allUsers" _userRoles="UserRoles" />
}
@code {
// Property used to add or edit the currently selected user
ApplicationUser objUser = new ApplicationUser();
// Roles to display in the roles dropdown when adding a role to the user
List<IdentityRole> AllRoles = new List<IdentityRole>();
// Tracks the selected role for the current user
string SelectedUserRole { get; set; }
// To enable showing the Popup
bool ShowPopup = false;
// To enable showing the User Roles Popup
bool ShowUserRolesPopup = false;
......
}
父组件包含保存数据对象(状态)的对象,例如objUser
、SelectedUserRole
等以及使用这些对象实现业务规则的方法,例如SaveUser
和 AddRoletoUser
。
子组件 EditUserModal 用于添加/编辑用户。它将 objUser
作为来自父组件的参数。 'objUser' 保存新/选定用户的值。
<div class="form-group">
<input class="form-control" type="text" placeholder="First Name" @bind="objUser.FirstName" />
</div>
<div class="form-group">
<input class="form-control" type="text" placeholder="Last Name" @bind="objUser.LastName" />
</div>
<div class="form-group">
<input class="form-control" type="text" placeholder="Email" @bind="objUser.Email" />
</div>
<button class="btn btn-primary" @onclick="SaveUser">Save</button>
@code {
[Parameter]
public ApplicationUser objUser { get; set; }
[Parameter]
public EventCallback SaveUser { get; set; }
.......
}
objUser
对象和 SaveUser
事件回调是子组件中的参数。以子组件的形式输入数据并保存后,在父组件中调用SaveUser
,自动与父组件中的objUser
对象进行数据绑定。该对象保存当前输入的数据。然后使用此对象调用数据库服务来创建/更新用户数据。它运行完美!
这是它的样子:
objUser
对象具有输入的数据,并且一切正常。
但是问题在 EditUserRolesModal 组件中,我正在用变量做一些类似的事情。 EditUserRolesModal 组件的代码:
<h5 class="my-3">Add Role</h5>
<div class="row">
<div class="col-md-10">
<div class="form-group">
<input class="form-control" type="text" @bind="objUser.Email" disabled />
</div>
<div class="form-group">
<select class="form-control" @bind="@SelectedUserRole">
@foreach (var option in AllRoles)
{
<option value="@option.Name">@option.Name</option>
}
</select>
</div>
<button class="btn btn-primary" @onclick="AddRoletoUser">Assign</button>
</div>
</div>
@code {
[Parameter]
public ApplicationUser objUser { get; set; }
[Parameter]
public string SelectedUserRole { get; set; }
[Parameter]
public List<IdentityRole> AllRoles { get; set; }
[Parameter]
public EventCallback AddRoletoUser { get; set; }
........
}
这里的参数 SelectedUserRole
用于保存要分配给用户的角色的值。它绑定到下拉列表。当我更改下拉列表的值时,调试显示该值在子组件(参数)对象中更新。下面的图片展示了它:
该值设置为子组件中的 SelectedUserRole
参数对象。
很像,objUser
在 EditUserModal
中被用来保存新/选定用户的字段 FirstName
、LastName
和 Email
的值, SelectedUserRole
在 EditUserRolesModal
中用于保存要分配给用户的选定角色的值。
但是当按下Assign按钮并通过子组件在父组件中调用AddRoletoUser
事件时,SelectedUserRole
变量在父组件中包含null。
两个子组件中的场景是一样的,目的是当子组件内部参数值发生变化时,绑定的值应该更新并反映在父组件的状态中。但是,它适用于 EditUserModal
组件,而不适用于 EditUserRolesModal
组件。为什么?我做错了什么?
附言我在 Blazor 或基于组件的网络编程方面没有经验。
编辑:我为此项目创建了一个公共存储库。它还包含数据库脚本文件。可以在 here 中找到。
解决方法
我没有非常仔细地分析您的代码,但是.. 为什么不将您的 ApplicationUser 逻辑代码(CRUD、更新角色等)放在一个地方 - 一个数据服务对象。您可以通过所有组件的依赖注入来访问这一份真相。
更新
直接回答你为什么objUser
得到更新,而不是SelectedUserRole
?非常简单的 ObjUser
是一个对象并通过引用传递。您在 objUser
上制作的任何 mod 都会在父级的原始版本上制作。另一方面,SelectedUserRole
是一个字符串,虽然它是一个对象,但它有点像原始类型一样处理。当 SetParametersAsync
在子组件中运行时,objUser 的子属性设置为引用,而 SelectedUserRole
的子属性设置为传递的字符串的副本 - 实际上是按值。
这就是我建议转向基于服务的模型的原因,您不会遇到这样的问题。
,这是罪魁祸首:
<button class="btn btn-primary" @onclick="AddRoleToUser">Assign</button>
当“分配”按钮被点击时,您应该调用 AddRoleToUser
事件回调,将 SelectedUserRole
值传递给 AddRoleToUser
方法。
这是如何做到这一点的完整代码片段:
[Parameter]
public EventCallback<string> AddRoleToUser { get; set; }
注意:是 EventCallback<string>
不是 EventCallback
做...
<button type='button" class="btn btn-primary"
@onclick="OnAddRoleToUser">Assign</button>
和
private async Task OnAddRoleToUser()
{
// skipped checking for brevity...
await AddRoleToUser.InvokeAsync(SelectedUserRole);
}
请注意,OnAddRoleToUser
方法调用 AddRoleToUser
'delegate',将 SelectedUserRole
传递给它
这就是你定义封装“委托”的方法
private async Task AddRoleToUser(string SelectedUserRole)
{
}
注意:您必须将属性 type='button"
添加到 <button type='button".../>
,
如果不是,您的按钮将获得默认的 type='submit"
重要提示:
SignInManager<TUser>
和 UserManager<TUser>
在 Razor 组件中不受支持。
See source...。您应该为所需的作业创建服务...
更新:
但我想知道,正如我的问题中提到的,为什么它在第一个子组件 EditUserModal 中与 objUser 一起工作?根据您的回答,我必须将 objUser 对象发送给 SaveUser 委托,但无论如何它都可以使用该方法。
现在它 works
...它似乎可以工作,就像在组件中使用 UserManager<TUser>
似乎可以工作一样。
您的代码的问题在于您通过将参数 objUser 绑定到输入文本元素来改变参数的状态,如下所示:
<input class="form-control" type="text" placeholder="First Name" @bind="objUser.FirstName" />
这个绑定实际上改变了参数的状态,但你不应该改变参数的状态。只有创建参数的组件,即父组件,才应该改变参数。换句话说,您应该将参数视为自动属性...不要更改其值。现在,在您兴奋之前先阅读以下内容:
好的,我已经查看了更多细节,看看发生了什么。下面有一个解释,但在开始之前,我将重申我的主张,即核心问题是子组件覆盖了它自己的 [Parameter] 属性值。通过避免这样做,您可以避免这个问题。
在这种情况下,以及在其他情况下,Blazor 依赖于安全的父对子参数分配(不会覆盖您想要保留的信息)并且没有副作用,例如触发更多渲染(因为那样您可能会在无限循环)。我们可能应该加强文档中的指导,而不仅仅是说“不要写入您自己的参数”,而是将其扩展为警告不要在此类属性设置器中产生副作用。 Blazor 使用 C# 属性来表示从父组件到子组件的通信通道,这对开发人员来说非常方便,并且在源代码中看起来不错,但是产生副作用的能力是有问题的。我们已经有一个编译时分析器,如果您在 [Parameter] 属性上没有正确的可访问性修饰符,它会发出警告 - 如果参数不是简单的 { get; ,也许我们应该扩展它以给出警告/错误。放; } 自动属性。
注意:我建议您阅读整个问题,因为这是关于 Blazor 最重要的事情之一,大多数开发人员未能消化和实施。
这是一个简单的代码片段,描述了如何在父组件与其子组件之间创建双向数据绑定、如何传递参数以及如何使用传入的参数。
TwoWayDataBinding.razor
<div>InternalValue: @InternalValue</div>
<div class="form-group">
<input type="text" class="form-control"
@bind="InternalValue"
@bind:event="oninput"
/>
</div>
@code {
private string InternalValue
{
get => Value;
set
{
if (value != Value)
{
ValueChanged.InvokeAsync(value);
}
}
}
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
}
Index.razor
@page "/"
<TwoWayDataBinding @bind-Value="Value"></TwoWayDataBinding>
<p> </p>
<div>
<input type="text" @bind="Value" @bind:event="oninput"/>
</div>
@code
{
private string Value { get; set; } = "Default value...";
}
如您所见,我没有改变传递给子组件的 Value 参数。相反,我定义了一个局部变量,该变量通过 EventCallback.InvokeAsync 方法返回给父组件。
使用 EditUserModal 组件遵循此模式...如果您没有成功,请告诉我。
不用说,您在 EditUserModal 组件中的参数 objUser
中所做的任何更改都会立即反映在 ManageUsers 组件中定义的 objUser
变量中,因为两者都指向相同的位置在内存中,因此当您单击 Save
按钮时,会调用 ManageUsers 组件中的 SaveUser
方法,并且一切看起来都很好......
再说一遍:
`SignInManager<TUser>` and `UserManager<TUser>` aren't supported in
Razor components.
您不会希望在部署应用程序时发现它,对吧。 使用服务...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。