skip to Main Content

Blazor now seems to support some form of inheritance but there is no clear documentation around it abstract style components are possible.

For example lets say I want to be able to insert N number of <DynamicComponent> elements on a page, I need to be able to know up front what props they expose and how to fill them.

So I want to ideally force anyone making a component of this type to always have fields like:

@code {
   [Parameter] public abstract EventCallback OnSomeMandatoryEvent {get;}
   [Parameter] public abstract ISomeInterface Data {get;}
}

This way I know for sure the components I am wanting to pull in will always expose the required properties the convention requires.

So is this use case supported?

2

Answers


  1. Chosen as BEST ANSWER

    For anyone else who comes here looking for information on abstract classes for Blazor you can do the following:

    public abstract class CustomElementComponent : ComponentBase
    {    
        [Parameter]
        public EventCallback<string> SomeEvent { get; set; }
    
        [Parameter]
        public SomeModel Data { get; set; }
    
        protected override Task OnInitializedAsync()
        {
            // whatever
            return base.OnInitializedAsync();
        }
    }
    

    Then use it like so:

    @inherits CustomElementComponent
    
    <div class="field">
      <label class="label">Text To Show</label>
      <div class="control">
        <input class="input" type="text" @bind="Data.Text">
      </div>
    </div>
    
    @code {
        // All lifecycles from abstract class have been run and Data is available
    }
    

    I struggled to find any docs out around this specifically so while the interfaces above in the answer are good sometimes an abstract class will be what you need as you can hook into the lifecycle methods there too, allowing you to create conventions at base layers.

    Its also worth noting you can also use generics on the abstract classes and add it to the consuming component like so @inherits CustomElementComponent<SomeType>


  2. This is how to implement interfaces in components.

    Here are two interfaces:

    public interface IActionComponent
    {
        [Parameter] public EventCallback OnSomeMandatoryEvent { get; set; }
        [Parameter] public IBaseData? Data { get; set; }
    }
    
    public interface IBaseData
    {
        Guid Uid { get; }
    }
    

    And three components – only two of which implement the interface:

    // Component1.razor
    @implements IActionComponent
    <h3>Component1</h3>
    
    @code {
        [Parameter] public EventCallback OnSomeMandatoryEvent { get; set; }
        [Parameter] public IBaseData? Data { get; set; }
    }
    
    // Component2.razor
    @implements IActionComponent
    <h3>Component2</h3>
    
    @code {
        [Parameter] public EventCallback OnSomeMandatoryEvent { get; set; }
        [Parameter] public IBaseData? Data { get; set; }
    }
    
    // Component3.razor
    <h3>Component3</h3>
    
    @code {
        [Parameter] public EventCallback OnSomeMandatoryEvent { get; set; }
        [Parameter] public IBaseData? Data { get; set; }
    }
    

    And a test page:

    @page "/"
    @using System.Reflection
    
    <h3>Dynamic Component Tester</h3>
    
    @if (ComponentType is not null)
    {
        <DynamicComponent Type=ComponentType />
    }
    
    <div>
        <input type="text" @bind-value=componentName />
        <button class="btn btn-primary" @onclick=SetComponent>Add Component</button>
        <div class="alert alert-info m-2">
            Types to enter are: Component1, Component2 or Component3
        </div>
    </div>
    
    @if (message != string.Empty)
    {
        <div class="alert alert-danger">
            @message
        </div>
    
    }
    
    @code {
        private Type? ComponentType;
    
        private string componentName = string.Empty;
    
        private string message = string.Empty;
    
        private void SetComponent()
        {
            message = string.Empty;
            var newType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == componentName);
            if (newType is not null)
            {
                var v = newType.GetInterface(typeof(IActionComponent).FullName ?? string.Empty);
                if (v is not null)
                {
                    ComponentType = newType;
                    return;
                }
                else
                {
                    message = "Component does not implement IActionComponent";
                    return;
                }
            }
            message = "Can't load the Component";
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search