apexcharts / Blazor-ApexCharts

A blazor wrapper for ApexCharts.js
https://apexcharts.github.io/Blazor-ApexCharts
MIT License
784 stars 91 forks source link

Suddenly JSInvoke error #393

Closed peterboccia closed 7 months ago

peterboccia commented 7 months ago

After a week of working on this library I encountered a strange error:

[2024-01-30T09:10:14.770Z] Error: Microsoft.JSInterop.JSException: Failed to fetch dynamically imported module: http://localhost:4000/_content/Blazor-ApexCharts/js/blazor-apexcharts.js
TypeError: Failed to fetch dynamically imported module: http://localhost:4000/_content/Blazor-ApexCharts/js/blazor-apexcharts.js
   at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
   at ApexCharts.ApexChart`1.OnAfterRenderAsync(Boolean firstRender)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

The problem occurs suddenly and randomly when I restart the application even with some changes in another part of the application (not the front-end part, let me say like a change in a controller for the API) but the app broke and doesn't show the graphs anymore.

I search all the web: tried already to remove the compression, to copy the folder _content/blazor-apexchart/... manually, cleaned up the cache. Nothing. Another strange thing: I cloned this repo and started the demo application ServerSide and it works like a charm. I restarted my application and again this error. It seems that the problem is that the library don't copy the file on the publishing folder (wwwroot).

I struggle and I struggle... after an hour it starts working again, like nothing.

I am amused. What kind of configuration I did wrong?

I am developing a Blazor application .NET 8, with this starting configuration (I anonymize all the sensitive information):

 public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddRazorComponents()
                .AddInteractiveServerComponents();

            builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
            {
                loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration);
            });

            ConfigureReportingServices(builder.Services, builder.Configuration);

            builder.Services.AddServerSideBlazor().AddCircuitOptions(option =>
            {
                var localEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

                if (localEnv == "Local" || localEnv == "Development")
                {
                    option.DetailedErrors = true;
                }
            });

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment() || app.Environment.EnvironmentName == "Local")
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            ConfigureAPIApp(app);

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.MapRazorComponents<App>()
                .AddInteractiveServerRenderMode();

            app.Run();
        }

        private static void ConfigureReportingServices(IServiceCollection services, ConfigurationManager configuration)
        {
            services.AddApplicationInsightsTelemetry();
            services.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>((module, o) => { module.EnableSqlCommandTextInstrumentation = true; });

            services.AddSingleton<ReadinessHealthCheck>();
            services.AddSingleton<LivenessHealthCheck>();

            services.AddResponseCompression(options =>
            {
                options.EnableForHttps = true;
                options.MimeTypes =
                    ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json; charset=utf-8" });
            });

            services.AddHealthChecks()
            .AddLivenessHealthCheck("Liveness", HealthStatus.Unhealthy, new List<string>() { "Liveness" })
            .AddReadinessHealthCheck("Readiness", HealthStatus.Unhealthy, new List<string> { "Readiness" });

            // Register the Swagger generator, defining 1 or more Swagger documents
            services.AddSwaggerGen(c =>
            {

                c.SwaggerDoc("report", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "Here I register the swagger etc"
                });

             ...... etcetera
            });
            services.AddSwaggerGenNewtonsoftSupport();

            // Services for report generation
            services.AddScoped<HtmlRenderer>();
            services.AddScoped<BlazorRendererService>();

            // other services
        }

        private static void ConfigureAPIApp(WebApplication app)
        {
            app.UseSerilogRequestLogging();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseAntiforgery();

            app.UseResponseCompression();

            app.UseEndpoints(options =>
            {
                options.MapHealthChecks("/health/live", new HealthCheckOptions()
                {
                    Predicate = check => check.Name == "Liveness"
                });
                options.MapHealthChecks("/health/ready", new HealthCheckOptions()
                {
                    Predicate = check => check.Name == "Readiness",
                });

                options.MapControllers();
            });

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/report/swagger.json", "");
                ... etcetera
            });
        }

I'm using this launch profile:

"BlazorApplication": {
  "commandName": "Project",
  "launchBrowser": true,
  "launchUrl": "http://localhost:4000",
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Local",
    other envs
  },
  "applicationUrl": "http://localhost:4000"
}

Really, I would never have opened an issue if I hadn't already banged my head a thousand times on all the possible problems.

Thanks in advance for all your help and comments on this!

image

joadan commented 7 months ago

Hi,

Just to be clear, do you don't get this error when cloning this repo and run the samples?

peterboccia commented 7 months ago

Hi,

Just to be clear, do you don't get this error when cloning this repo and run the samples?

Right Jordan. It's something in my repo but I can't figure it out. Just wondering if someone else got this behaviour.

Yesterday I found a solution: when this happens (the app not copying the _content/blazor-apexcharts folder and its content), I run again the samples, stop it and run immediately my app again and it starts copying file again.

So strange. So sorry to bother you with this...

peterboccia commented 7 months ago

Hi all, any other suggestions/considerations about this issue? Still getting this error. I was struggling to make it works with puppeteer and graphis were not generated even if in the browser edge (that I'm using to preview charts and testing the application) everything is working fine. After a lot of debuggin I found out the same error in chrome, that is preventing the rendering of the charts: image Thanks again in advance for any comments 🙏

peterboccia commented 7 months ago

After a sleepless night pondering what the issue might be, I tried changing the environment variable from Local to Development (which isn't applicable to my case since Development is a deployment environment). Switching to Development no longer produces the aforementioned errors. Perhaps the issue isn't even with ApexCharts but with Blazor. I'll keep you updated.

peterboccia commented 7 months ago

UPDATE - SOLUTION: As I mentioned earlier, by focusing all attention on the issue of renaming environment variables, I quickly arrived at the solution. It's a problem that other people in my situation, namely those with a local environment named differently from Development, have encountered multiple times in the past.

The problem is caused by the fact that apparently the underlying Asp.NET Core framework makes some service registrations when in Development mode. Unfortunately, in many cases like mine, this name cannot be used, hence the problem: by changing the name, certain registrations, (in this case namely the dynamically added JS and CSS files), do not occur.

I found a response to an issue that addresses .net6 and .net7 and greater (I am using .net8 as said). Issue on UseStaticWebAssets

In the end, I resolved it with this simple check in the Program.cs file:

if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Local")
{
    builder.WebHost.UseStaticWebAssets();
}

Hope this could help other people around the globe! Peter