Список компонентов Blazor, которые реализуют интерфейс - PullRequest
2 голосов
/ 17 марта 2020

Я создаю многократно используемую библиотеку классов Razor для компонентов Blazor.

У меня есть интерфейс, который может реализовать любой компонент Blazor:

public interface IControllableComponent
{
    void SetSomeValue(int someValue);
}

Как получить список всех компонентов которые реализуют этот интерфейс?

public interface IControllableComponentManager
{
    void SetSomeValueForAllControllableComponents(int someValue);
}

public class ControllableComponentManager : IControllableComponentManager
{
    IList<IControllableComponent> _controllableComponentList;

    public ControllableComponentManager()
    {
        _controllableComponentList = ??? // how to populate this list?
    }

    public void SetSomeValueForAllControllableComponents(int someValue)
    {
        foreach (var controllableComponent in _controllableComponentList)
        {
            controllableComponent.SetSomeValue(someValue);
        }
    }
}

Я хочу, чтобы моя повторно используемая библиотека классов Razor использовалась в коде позади в различных проектах:

public class MyControllableComponentBase : ComponentBase, IControllableComponent
{
    protected int _someValue;

    public void SetSomeValue(int someValue)
    {
        _someValue = someValue;
    }
}

Есть ли способ заполнить _controllableComponentList в ControllableComponentManager?

Я хотел бы сделать это чисто, правильно, без использования отражения. Желательно в стиле ASP. NET Core с внедрением зависимостей.

Проблема в том, что компоненты Blazor создаются в разметке Razor как <Component></Component>, поэтому я не знаю, как получить их ссылки на добавить их в List<>.

Ответы [ 3 ]

4 голосов
/ 17 марта 2020

Я думаю, вы можете сделать это следующим образом:

  1. Определить службу, которая хранит коллекцию типа интерфейса
  2. Предоставить метод для добавления компонента, метод для удалить компонент, делегат события для уведомления о добавлении, удалении и т. д. c.
  3. Вставьте службу в компонент, который вы хотите добавить в службу, и в методе OnInitialized сделайте что-то вроде этого:

    protected override void OnInitialized()
    {
        MyComponents.AddComponent(this);
        this.MyProperty = "I was born with the sun...";
    }  
    
3 голосов
/ 17 марта 2020

Вы можете получить ссылку на компонент Blazor, используя @ref, но вы можете сделать это только внутри разметки Razor.

Чтобы использовать ссылку на компонент Blazor вне разметки Razor, компонент должен зарегистрироваться сам.

Чтобы сделать это, вы должны использовать частичный класс для компонента в коде:

public partial class ComponentContainer : ComponentBase, IComponentContainer
{
    public Type ComponentType { get; protected set; }

    public RenderFragment Component { get; set; }

    [Parameter]
    public string Name { get; set; }

    [Inject]
    protected IComponentContainerManager ComponentContainerManager { get; set; }

    public void SetComponentType(Type componentType)
    {
        ComponentType = componentType;

        StateHasChanged();
    }

    protected override void OnInitialized()
    {
        ComponentContainerManager.RegisterComponentContainer(Name, this);

        Component = builder =>
        {
            if (ComponentType != null)
            {
                builder.OpenComponent(0, ComponentType);
                builder.CloseComponent();
            }
        };
    }
}

Использовать атрибут [Inject], чтобы использовать внедрение зависимостей для внедрения службы, где компонент может зарегистрироваться .

Используйте protected override void OnInitialized() из ComponentBase для регистрации компонента в службе регистрации.

public interface IComponentContainer
{
    Type ComponentType { get; }
    void SetComponentType(Type componentType);
}

public interface IComponentContainerManager
{
    void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer);
    void SetComponentType(string componentContainerName, Type componentType);
    Type GetComponentType(string componentContainerName);
}

public class ComponentContainerManager : IComponentContainerManager
{
    readonly IDictionary<string, IComponentContainer> _componentContainerDict = new Dictionary<string, IComponentContainer>();

    public void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer)
    {
        _componentContainerDict[componentContainerName] = componentContainer;
    }

    public void SetComponentType(string componentContainerName, Type componentType)
    {
        _componentContainerDict[componentContainerName].SetComponentType(componentType);
    }

    public Type GetComponentType(string componentContainerName)
    {
        return _componentContainerDict[componentContainerName].ComponentType;
    }
}

Теперь вы можете использовать IComponentContainerManager в любом месте своего кода.

1 голос
/ 17 марта 2020

Если вы хотите динамически создавать компоненты, вы можете использовать концепцию RenderFragment для этого. Фрагмент рендеринга по сути является функцией, которая использует RenderTreeBuilder для создания компонента. Это то, к чему ваши компоненты Razor компилируются, когда вы создаете .razor файлов.

Например, следующий код создает RenderFragment, который просто отображает один компонент:

Type componentType = typeof(MyControllableComponentBase);
RenderFragment fragment = builder =>
{
    builder.OpenComponent(0, componentType);
    builder.CloseComponent();
};

Затем вы можете назначить этот фрагмент свойству и динамически отобразить его в компоненте Razor:

<div>
   @Fragment
</div>

@code {
    public RenderFragment Fragment
    { get; set; }
}

Это решит проблему динамического создания компонентов, когда вы просто знаете их тип. Однако это не решит проблему, которую вы хотите вызвать SetSomeValue на экземплярах компонента. Для этого вам нужно понять, что вы на самом деле не контролируете экземпляры компонентов: построитель дерева рендеринга отвечает за создание компонентов из этой виртуальной разметки, поэтому вы никогда не вызовете new ComponentType(). Вместо этого вы полагаетесь на средство визуализации, чтобы создать для вас экземпляр, и , тогда вы можете взять ссылку на использованный экземпляр и работать с ним.

Вы можете использовать директиву @ref для захватить ссылки на визуализированные экземпляры компонентов :

<MyControllableComponent @ref="controllableComponent" />

@code {
    private MyControllableComponent controllableComponent;

    private void OnSomething()
    {
        controllableComponent.SetSomeValue(123);
    }
}
...