TailBlazor / HeroIcons

MIT License
3 stars 3 forks source link

Exception on load #1

Open uniquelau opened 3 years ago

uniquelau commented 3 years ago

Running .NET 5.0, Blazor PWA Installed in client project and updated _Import. Referencing the component directly SVG is requested by browser. However, exception is thrown.

image

Throw Exception at TailBlazor.HeroIcons.TailBlazorHeroIconBase.OnInitialized()

      Unhandled exception rendering component: Cannot wait on monitors on this runtime.
System.PlatformNotSupportedException: Cannot wait on monitors on this runtime.
   at System.Threading.Monitor.ObjWait(Boolean exitContext, Int32 millisecondsTimeout, Object obj)
   at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout, Boolean exitContext)
   at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout)
   at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy)
   at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
   at System.Xml.XmlTextReaderImpl.FinishInitUriString()
   at System.Xml.XmlTextReaderImpl..ctor(String uriStr, XmlReaderSettings settings, XmlParserContext context, XmlResolver uriResolver)
   at System.Xml.XmlReaderSettings.CreateReader(String inputUri, XmlParserContext inputContext)
   at System.Xml.XmlReader.Create(String inputUri, XmlReaderSettings settings, XmlParserContext inputContext)
   at System.Xml.XmlReader.Create(String inputUri, XmlReaderSettings settings)
   at System.Xml.Linq.XDocument.Load(String uri, LoadOptions options)
   at System.Xml.Linq.XDocument.Load(String uri)
   at TailBlazor.HeroIcons.TailBlazorHeroIconBase.OnInitialized()
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()

SVG requested 3 times (as expected) image

TaylorWatson commented 3 years ago

looks related to this.

PlatformNotSupportedException on Blazor Client Side

I will take a look to see if I can work around WASM's limitations.

DanteDeRuwe commented 2 years ago

Any updates on this?

badHax commented 2 years ago

This seems to be related to a thread blocking call in TailBlazorHeroIcon.razor.cs which is definitely not supported in Blazor.

 var document = await Task.Run(() => {
                    return XDocument.Load($"{baseUri}_content/TailBlazor.HeroIcons/icons/{EnumExtension.GetEnumDescription(IconStyle)}/{EnumExtension.GetEnumDescription(Icon)}.svg");
            });

One workaround that I have found is to override this class and load the SVG through an HTTP request and use async method to load it instead:

       //.....
       // Use non blocking HTTP request to stream the SVG
        var client = new HttpClient();
        var icon = (await client.GetAsync(NavigationManager.BaseUri + "_content/TailBlazor.HeroIcons/icons/" +
                                          IconStyle.GetEnumDescription() + "/" + Icon.GetEnumDescription() + ".svg"))
            .Content;
        await using var stream = await icon.ReadAsStreamAsync();

        // load asynchronously
        var root = (await XDocument.LoadAsync(stream, LoadOptions.PreserveWhitespace, CancellationToken.None)).Root;

        root?.SetAttributeValue("width", Width);
        root?.SetAttributeValue("height", Height);
        root?.SetAttributeValue("stroke", "");
        root?.SetAttributeValue("class", _classStroke);
        foreach (var xelement in root?.Descendants().Select((Func<XElement, XElement>)(path => path)) ??
                                 new List<XElement>())
        {
            if (xelement.Attribute("stroke-width") != null)
                xelement.SetAttributeValue("stroke-width", StrokeWidth);
            if (xelement.Attribute("fill") != null)
                xelement.SetAttributeValue("fill", StrokeWidth);
        }

        _svgIcon = root?.ToString();
    //...

Then I created a CustomHeroIcons.cs

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace Custom.Components.HeroIcons
{
    public class CustomHeroIcon : CustomHeroIconBase
    {
        protected override void BuildRenderTree(RenderTreeBuilder __builder)
        {
            if (this.EnableComments)
                __builder.AddContent(0, this._svgIconComment);
            __builder.AddContent(1, (MarkupString)this._svgIcon);
        }
    }
}

Finally, I just use this in my code:

        <button @onclick="NavigateToHome">
          Home
          <CustomHeroIcon Class="tw-h-6 tw-w-6"
                              Icon="HeroIcon.Home"></CustomHeroIcon>
        </button>
TaylorWatson commented 2 years ago

Thanks, if you want submit a PR or I'll update using this method instead