Blazor WebAssembly Environment Variables
Asked Answered
S

1

22

I'm currently working on a .NET Standard 2.1 Blazor WebAssembly application. I try to include or exclude Stylesheets according to an environment variable.

In .NET Core there are usually Environment Tag Helpers like in the following example:

<environment include="Development">
    <link rel="stylesheet" href="css/style.css" type="text/css" />
</environment>

<environment exclude="Development">
    <link rel="stylesheet" href="css/style.min.css" type="text/css" />
</environment>

This works perfectly fine in a Blazor Server application, but doesn't in Blazor WASm, as this is client-side code.

Thus I try to find a good solution to include/exclude Style sheets according to the Environment variable in Blazor WebAssembly.

My current approach is to call a JavaScript helper method from my Blazor WASm Program.cs file with JSInterop and remove the Stylesheets according to the environment variable:

await jsInterop.InvokeVoidAsync("helpers.setup", "Development");

My JavaScript on the client looks like this:

window.helpers = {
    setup: (environment) => {

        if (environment === "Development") {
            // remove production styles
        }

        if (environment !== "Development") {
            // remove development styles
        }
    }
};

The problem with this solution is, I want to put my styles into my header file and group them into a <section> element or something similar - which doesn't work in valid HTML5.

How do you handle your Development/Production environment in Blazor WebAssembly?

How can you exclude or include specific CSS files according to the set environment variable in the project settings (launchsettings.json)?

Springfield answered 23/7, 2020 at 10:42 Comment(0)
E
24

Disclaimer:

This is just something I tried that seems to work. I could not find any documentation supporting doing it this way, nor anything saying not to do it this way. if there is any official documentation please let me know.

The documentation state:

When running an app locally, the environment defaults to Development. When the app is published, the environment defaults to Production.

Further down it does mention how to set the environment via the web.config that gets generated when publishing the file to IIS. There are also references to Use multiple environments in ASP.NET Core. and Host and deploy ASP.NET Core Blazor WebAssembly

However this is what I did.

Looking at the Program.cs file that was generated by the new web assembly project template, the builder is created by WebAssemblyHostBuilder.CreateDefault(args); This must mean that all the default services must already be registered in the services container.

This would include the IWebAssemblyHostEnvironment configuration service.

The next line down builder.RootComponents.Add<App>("app"); adds the App <app></app> root component that is used in the index.html file.

So, Why not try to create a Head <head></head> component and see what happens.

I created a Head razor component and named it Head.razor containing all the html that would usually live between the <head></head> tags.

@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@inject IWebAssemblyHostEnvironment hostEnv

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />

@*Check the environment value*@
@if (hostEnv.IsDevelopment())
{
    <title>BlazorWasmApp - In Debug</title>
    <link href="css/debug.css" rel="stylesheet" />
}
else
{
    <title>BlazorWasmApp - Not Debug</title>
    <link href="css/live.css" rel="stylesheet" />
}

@code {}

Because it is a component you can inject the IWebAssemblyHostEnvironment and check the .IsDevelopment(),.IsProduction() etc.. extension method values.

I left the original <head> tag as is in the index.html file as the content of the <head>...gets overwritten...</head> seems to be completely overwritten.

<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorWasmApp</title>
    <base href="/" />
    <link href="css/app.css" rel="stylesheet" />
</head>
<body>
    <app>Loading...</app>    
...
...

Also leaving the <head>tag with the reference to the cs/app.css file does not change the way it looks when the app is Loading....

I registered the Head class to the builder.RootComponents collection in the Program class.

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);            
    builder.RootComponents.Add<App>("app");

    //Add the Head to root components
    builder.RootComponents.Add<Head>("head");            
            
    builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });           
    await builder.Build().RunAsync();
}

I added 2 css files to the wwwroot/css folder debug.css and live.css each containing a simple body { background-color:*red or blue* } style.

In the launchSettings.json file, in the profiles section, set the IIS Express : environmentVariables : ASPNETCORE_ENVIRONMENT to "Development" and under the [YourAppName] : environmentVariables : ASPNETCORE_ENVIRONMENT to "Production".

"profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
    },
    "BlazorWasmApp": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      },
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
    }
  }

When launching the app with the IIS Express profile (Development) the background is red and when launching the app with [YourAppName] profile (Production) the background is blue.

When looking at the <head></head> tags using the developer tools the content of the head tag contains the css references according to the environment.

IIS Express:

Red Dev

BlazorWasmApp (my app profile):

Blue Prod

Expressman answered 23/7, 2020 at 18:53 Comment(7)
thanks for your idea. This approach works very well in this Blazor WASm release. The only issue I found is, that the <head> component is rendered after the DOM of the index.html was setup. That means, I would still need a separate CSS for the Loading section <app>Loading...</app> or the blazor-error-ui div... Do you perhaps have another idea on how to solve this?Springfield
The <head> content is over written, so you could leave the original head tag as is, with the reference to app.css stylesheet. I'll update the answer to include this.Expressman
and what in case you host your application with static files on azure storage account / behind CDN, then nothing on server side is set... :/ it should detect during runtime using URL somehow if i see it correctly..Cassondra
Where would you put <script> tags, such as for google analytics? Gives an error saying that script tags cannot be used in components. But I can't leave it back in the original head tag as that will be overwritten?Montelongo
@Montelongo a possible solution could be to add the @((MarkupString)@"<script src=""dev.url""></script>") in the block as required. I have not worked with Blazor for quite some time now, so it's a best guess.Expressman
@user1227445, I have also not done any work with google analytics, so if this works please let me know and I can add it to the answerExpressman
Very promising, but failed for me on refresh.Itol

© 2022 - 2024 — McMap. All rights reserved.