#c# #.net-core #blazor #razor-component-library
#c# #.net-core #blazor #razor-component-library
Вопрос:
У меня есть библиотека компонентов razor, в которой я создаю пользовательские, повторно используемые компоненты. У меня есть компонент «ContentItem», который я хотел бы просто привязать к свойству объекта в компоненте, а затем использовать отражение или какой-либо другой метод для обнаружения необходимой информации. В качестве примера:
ContentItem.razor
<div>
<div>@DisplayName</div>
<div>@PropertyValue</div>
</div>
ContentItem.razor.cs
public partial class ContentItem
{
/// <summary>
/// The property that this component will bind to
/// </summary>
[Parameter]
public **???** ObjectProperty{ get; set; }
public string DisplayName;
public string PropertyValue;
protected override void OnParametersSet()
{
try
{
DisplayName = //reflection or some way to get the display attribute from the Object Property
PropertyValue = //reflection or inspection of the ObjectProperty
base.OnParametersSet();
}
catch (Exception ex)
{
throw new exception("Error", ex);
}
}
Страница в клиентском приложении
<div>
<ContentItem ObjectProperty="@User.FirstName" />
</div>
Таким образом, в основном все, что вам нужно было бы сделать при использовании компонента «ContentItem», — это передать свойство ObjectProperty, а затем компонент «ContentItem» выполнит какое-то отражение и / или проверку этого параметра для отображения HTML по желанию.
Ответ №1:
Вам нужно будет передать тип класса, свойство и значение отдельно компоненту.
Тип будет typeof(User)
таким, и имя свойства может быть производным, nameof(User.FirstName)
а значение будет таким, какое User.FirstName
значение хранится как string
или как угодно.
Параметры в вашем ContentItem
компоненте будут такими:
[Parameter]
public Type ObjectType { get; set; }
[Parameter]
public string ObjectProperty { get; set; }
[Parameter]
public string ObjectValue { get; set; }
и может быть вызван следующим образом:
<ContentItem
ObjectType="@(typeof(User))"
ObjectProperty="@(nameof(User.FirstName))"
ObjectValue="@User.FirstName" />
Итак, предположим, что ваш класс выглядит следующим образом:
public class User
{
[DisplayName("First name")]
public string FirstName { get; set; }
}
После этого в компоненте используйте приведенный ниже вспомогательный метод, чтобы получить DisplayName
:
public static string GetDisplayName(Type @type, string propertyName)
{
var memberInfo = @type?.GetMember(propertyName)[0];
var displayNameAttribute = memberInfo?.GetCustomAttribute<DisplayNameAttribute>();
string displayName = displayNameAttribute?.DisplayName ?? "";
return string.IsNullOrEmpty(displayName) ? propertyName : displayName;
}
Ответ №2:
В итоге я решил это с помощью каскадных значений / параметров. У меня есть компонент контейнера, который предоставляет каскадное значение с именем «BindObject».
<CascadingValue Name="BindObject" Value="@BindObject">
@if (BindObject != null)
{
@ChildContent
}
else
{
if (PlaceholderLines > 0)
{
<Placeholder DisplayLines="@PlaceholderLines"></Placeholder>
}
}
</CascadingValue>
Затем в моем компоненте элемента содержимого я использую каскадный параметр для получения объекта. Я также предоставляю параметр «BindProperty».
/// <summary>
/// When the ContentItem is placed inside of a ContentItems
/// container then the BindObject will be passed as a Cascading
/// Parameter. This ContentItem will then use reflection to
/// populate the Label and Value of this control.
/// </summary>
[CascadingParameter(Name = "BindObject")]
public object BindObject { get; set; }
/// <summary>
/// If a BindProperty is specified then a generic placeholder
/// will be used while the property is being loaded or evaluated.
/// The BindProperty only works when the ContentItem is placed
/// inside of a ContentItems control.
/// </summary>
[Parameter]
public string BindProperty { get; set; }
Теперь я могу один раз указать связанный объект в компоненте Content Items container, а затем поместить в него отдельные компоненты элемента содержимого. Я указываю «свойство BindProperty», а затем использую отражение в модели.
<ContentItems @ref="requestItems" BindObject="@jsonObject">
<ContentItem BindProperty="@nameof(jsonObject.UserId)" />
<ContentItem BindProperty="@nameof(jsonObject.FirstName)" />
<ContentItem BindProperty="@nameof(jsonObject.LastName)" />
<ContentItem BindProperty="@nameof(jsonObject.Email)" />
<ContentItem BindProperty="@nameof(jsonObject.RegionId)" />
<ContentItem BindProperty="@nameof(jsonObject.Password)" />
<ContentItem BindProperty="@nameof(jsonObject.Id)" />
</ContentItems>
Я использую аннотации данных для указания отображаемого имени, требований, значка и т.д. Компонент элемента содержимого использует отражение для использования этой информации, так что ее нужно определить только в самой модели. Я также написал несколько пользовательских аннотаций проверки, чтобы все было согласовано.
[Required]
[DisplayName("First Name:")]
[KzAlphaOnly(ErrorMessage = "Only letters are allowed here.")]
[MaxLength(16, ErrorMessage = "First name must be 16 characters or less.")]
[KzIcon(FaIcon = FaIcons.User)]
public string FirstName { get; set; }
[Required]
[KzAlphaOnly(ErrorMessage = "Only letters are allowed here.")]
[MaxLength(64, ErrorMessage = "Last Name name must be 64 characters or less.")]
[KzIcon(FaIcon = FaIcons.User)]
[DisplayName("Last Name:")]
public string LastName { get; set; }
[Required]
[StringLength(256)]
[DisplayName("Email:")]
[KzEmail]
[KzIcon(FaIcon = FaIcons.EnvelopeSquare)]
public string Email { get; set; }
Теперь я могу отображать информацию из модели с минимальными усилиями. Это полезно для шаблонов, карточек действий и т. Д.