从动态创建的 Blazor 组件订阅事件,以便调用页面可以执行某些操作

问题描述

我有一个 Blazor Web Assembly .NET 5 托管应用程序,但在处理动态创建的 Blazor 组件时遇到问题。

我的问题的一个例子:

Index.razor 页面这会调用动态组件并通过调用注入的服务 _slideInRenderedOutput 将其呈现给 SlideInService

@page "/"
@implements Idisposable
@inject ISlideInService SlideInService
<h1>Hello,world!</h1>

Welcome to your new app.

<TelerikButton Primary="true" OnClick="ToggleComponentSlideIn">Show Simple Form in Slide-In</TelerikButton>

@_slideInRenderedOutput;

<TelerikButton Primary="true" OnClick="Reset">Reset</TelerikButton>

@code
{
    private RenderFragment _slideInRenderedOutput;

    private async Task ToggleComponentSlideIn(MouseEventArgs obj)
    {
        _slideInRenderedOutput = SlideInService.Show<SimpleForm>("Test Title").Markup;
    }

    private async Task Reset(MouseEventArgs obj)
    {
        SlideInRenderedOutput = null;
    }

    protected override async Task OnInitializedAsync()
    {
        Console.WriteLine($"Index.razor: OnInitializedAsync()");

        await base.OnInitializedAsync();
    }

    protected override async Task OnParameteRSSetAsync()
    {
        Console.WriteLine($"Index.razor: OnParameteRSSetAsync()");

        await base.OnParameteRSSetAsync();
    }

    public void dispose()
    {
        Console.WriteLine($"Index.razor: dispose()");
    }
}

ISlideInService.cs:

    public interface ISlideInService
    {
        /// <summary>
        /// Shows a slide-in containing a <typeparamref name="TComponent"/> with the specified <paramref name="title"/>.
        /// </summary>
        /// <param name="title">Slide-In title.</param>
        ISlideInServiceInstance Show<TComponent>(string title) where TComponent : IComponent;

        void Close();
    }

ISlideInServiceInstance.cs:

    public interface ISlideInServiceInstance
    {
        RenderFragment Markup { get; set; }
    }

SlideInService.cs:

    public class SlideInService : ISlideInService,ISlideInServiceInstance
    {
        /// <summary>
        /// Shows the slide-in with the component type using the specified title.
        /// </summary>
        /// <param name="title">Slide-In title.</param>
        public ISlideInServiceInstance Show<T>(string title) where T : IComponent
        {
            return Show<T>(title,new SlideInParameters(),new SlideInoptions());
        }

        /// <summary>
        /// Shows the slide-in with the component type using the specified <paramref name="title"/>,/// passing the specified <paramref name="parameters"/> and setting a custom CSS style.
        /// </summary>
        /// <param name="contentComponent">Type of component to display.</param>
        /// <param name="title">Slide-In title.</param>
        /// <param name="parameters">Key/Value collection of parameters to pass to component being displayed.</param>
        /// <param name="options">Options to configure the slide-in.</param>
        private ISlideInServiceInstance Show(Type contentComponent,string title,SlideInParameters parameters,SlideInoptions options)
        {
            if (!typeof(IComponent).IsAssignableFrom(contentComponent))
            {
                throw new ArgumentException($"{contentComponent.FullName} must be a Blazor Component");
            }

            var slideInInstanceId = Guid.NewGuid();
            SlideIn slideInReference = null;

            var slideInContent = new RenderFragment(builder =>
            {
                var i = 0;
                builder.OpenComponent(i++,contentComponent);
                foreach (var parameter in parameters._parameters)
                {
                    builder.AddAttribute(i++,parameter.Key,parameter.Value);
                }
                builder.CloseComponent();
            });

            var slideInInstance = new RenderFragment(builder =>
            {
                builder.OpenComponent<SlideIn>(0);
                builder.AddAttribute(1,"Title",title);
                builder.AddAttribute(2,"Options",options);
                builder.AddAttribute(3,"ChildContent",slideInContent);
                builder.CloseComponent();
            });

            Markup = slideInInstance;

            return this;
        }

        public RenderFragment Markup { get; set; }

        public void Close()
        {
            Markup = null;
        }
    }

SlideIn.razor 组件:

@implements Idisposable
@inject ISlideInService SlideInService

<TelerikAnimationContainer @ref="_slideInRef"
                           Width="@Options.Width"
                           ParentClass="slide-in"
                           AnimationType="AnimationType.SlideLeft"
                           AnimationDuration="600">
    <div class="slide-in-content">
        <div class="slide-in-header">
            <span>@Title</span>
        </div>
        <CascadingValue Value="this">
            @ChildContent
        </CascadingValue>
    </div>
</TelerikAnimationContainer>

@code {
    private TelerikAnimationContainer _slideInRef;

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

    [Parameter]
    public SlideInoptions Options { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public async Task CloseAsync()
    {
        await _slideInRef.HideAsync();
        SlideInService.Close();
        StateHasChanged();
    }

    protected override async Task OnInitializedAsync()
    {
        Console.WriteLine($"SlideIn.razor: OnInitializedAsync()");

        await base.OnInitializedAsync();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            Console.WriteLine($"SlideIn.razor: OnAfterRenderAsync(firstRender:true)");
            await _slideInRef.ShowAsync();
        }
        else
        {
            Console.WriteLine($"SlideIn.razor: OnAfterRenderAsync(firstRender:false)");
        }

        await base.OnAfterRenderAsync(firstRender);
    }

    protected override async Task OnParameteRSSetAsync()
    {
        Console.WriteLine($"SlideIn.razor: OnParameteRSSetAsync()");

        await base.OnParameteRSSetAsync();
    }

    public void dispose()
    {
        Console.WriteLine($"SlideIn.razor: dispose()");
    }
}

SimpleForm.razor:

@using System.ComponentModel.DataAnnotations
@implements Idisposable

<div class="slide-in-body">
    <EditForm EditContext="@_editContext" OnValidSubmit="@HandleValidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <label for="clientId">Client Id</label>
        <InputText id="clientId" @bind-Value="_exampleModel.ClientId" />

        <label for="name">Name</label>
        <InputText id="name" @bind-Value="_exampleModel.Name" />

        <button type="submit">Submit</button>
    </EditForm>
</div>
<div class="slide-in-actions">
        <TelerikButton
            ButtonType="@ButtonType.Button"
            Primary="false"
            @onclick="Cancel">
            Cancel
        </TelerikButton>

        <TelerikButton
            ButtonType="@ButtonType.Button"
            Primary="true"
            @onclick="Save"
            Enabled="@(_editContext.Validate())">
            Save
        </TelerikButton>
</div>

@code {
    private ExampleModel _exampleModel = new();

    private EditContext _editContext;


    protected override void OnInitialized()
    {
        _editContext = new EditContext(_exampleModel);
    }

    [CascadingParameter]
    private SlideIn SlideIn { get; set; }

    [Parameter]
    public int? ClientId { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Console.WriteLine($"SimpleForm.razor: OnInitializedAsync()");

        _exampleModel.ClientId = ClientId.ToString();

        await base.OnInitializedAsync();
    }


    protected override async Task OnParameteRSSetAsync()
    {
        Console.WriteLine($"SimpleForm.razor: OnParameteRSSetAsync()");

        await base.OnParameteRSSetAsync();
    }

    public void dispose()
    {
        Console.WriteLine($"SimpleForm.razor: dispose()");
    }

    public class ExampleModel
    {
        [required]
        public string ClientId { get; set; }

        [required]
        [StringLength(10,ErrorMessage = "Name is too long.")]
        public string Name { get; set; }
    }

    private void HandleValidSubmit()
    {
        Console.WriteLine("Form submitted OK");
        SlideIn.CloseAsync();
    }

    private async Task Save(MouseEventArgs obj)
    {
        var isValid = _editContext?.Validate() ?? false;

        if (isValid)
        {
            Console.WriteLine("Form submitted OK");
            await SlideIn.CloseAsync();
        }
    }

    private async Task Cancel(MouseEventArgs obj)
    {
        await SlideIn.CloseAsync();
    }
}

当我单击呈现的 Cancel 上的 SimpleForm 按钮时,它会从其动态 Parent CloseAync CascadingParameter 上调用 SlideIn 1}} 组件。

然后我想要做的是通知 Index.page(创建动态组件的原始调用站点/页面)动态 SlideIn 组件已被其子 {{1 }} 组件,以便我可以在索引页面上执行一些操作(例如刷新网格)。

我花了一天左右的时间试图实现这一目标,但没有成功,我正在尝试使用动态组件做些什么?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)