Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand the element reference topic to show the pattern for using ElementReference across component boundaries #15517

Closed
javiercn opened this issue Nov 5, 2019 — with docs.microsoft.com · 0 comments · Fixed by #16685

Comments

Copy link
Member

javiercn commented Nov 5, 2019

See the comment and associated sample in the following issue.

dotnet/aspnetcore#16821 (comment)

Several people has tried to use ElementReference between components.
ElementReferences are only guaranteed to be valid during the component OnAfterRender method, so they can't be simply tossed around to other components (they are also structs).

When a parent component wants to make an element reference available to other components it can do so as shown in the example uploaded to the issue by allowing other components (for example children) to register callbacks with itself and then invoking those registered callbacks during the onafterrender event and passing in the element reference at that time so that other components can perform actions against it.

@page "/"
<h1 @ref="_title">Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;

namespace BlazorWasmHosted.Client.Pages
{
    public partial class Index : ComponentBase, IObservable<ElementReference>, IDisposable
    {
        private IList<IObserver<ElementReference>> _subscriptions = new List<IObserver<ElementReference>>();
        private bool _disposing;
        private ElementReference _title;

        protected override void OnAfterRender(bool firstRender)
        {
            base.OnAfterRender(firstRender);
            foreach (var subscription in _subscriptions)
            {
                try
                {
                    subscription.OnNext(_title);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

        public void Dispose()
        {
            _disposing = true;
            foreach (var subscription in _subscriptions)
            {
                try
                {
                    subscription.OnCompleted();
                }
                catch (Exception)
                {
                }
            }
            _subscriptions.Clear();
        }

        public IDisposable Subscribe(IObserver<ElementReference> observer)
        {
            if (_disposing)
            {
                throw new InvalidOperationException("Parent component is being disposed");
            }
            _subscriptions.Add(observer);
            return new Subscription(observer, this);
        }

        private class Subscription : IDisposable
        {
            public Subscription(IObserver<ElementReference> observer, Index self)
            {
                Observer = observer;
                Self = self;
            }

            public IObserver<ElementReference> Observer { get; }
            public Index Self { get; }

            public void Dispose()
            {
                Self._subscriptions.Remove(Observer);
            }
        }
    }
}
@inject IJSRuntime JS

<div class="alert alert-secondary mt-4" role="alert">
    <span class="oi oi-pencil mr-2" aria-hidden="true"></span>
    <strong>@Title</strong>

    <span class="text-nowrap">
        Please take our
        <a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
    </span>
    and tell us what you think.
</div>

@code {
    // Demonstrates how a parent component can supply parameters
    [Parameter]
    public string Title { get; set; }
}
using Microsoft.AspNetCore.Components;
using System;

namespace BlazorWasmHosted.Client.Shared
{
    public partial class SurveyPrompt : ComponentBase, IObserver<ElementReference>, IDisposable
    {
        [Parameter] public IObservable<ElementReference> Parent { get; set; }

        private IDisposable _subscription = null;
        protected override void OnParametersSet()
        {
            base.OnParametersSet();
            if (_subscription != null)
            {
                _subscription.Dispose();
            }

            _subscription = Parent.Subscribe(this);
        }

        public void OnCompleted()
        {
            _subscription = null;
        }

        public void OnError(Exception error)
        {
            _subscription = null;
        }

        public void OnNext(ElementReference value)
        {
            JS.InvokeAsync<object>("setElementClass", new object[] { value, "red" });
        }

        public void Dispose()
        {
            _subscription?.Dispose();
        }
    }
}

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants