Как я могу передать свойство объекта компоненту Razor в качестве параметра

#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; }
 

Теперь я могу отображать информацию из модели с минимальными усилиями. Это полезно для шаблонов, карточек действий и т. Д.

введите описание изображения здесь