-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add a built-in EnvironmentBoundary component for Blazor #64616
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
base: main
Are you sure you want to change the base?
Conversation
|
Greetings! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo! |
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
…sembly adapter Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot This should live in the Components.Web assembly, not in the Components assembly directly. Also, the name is not great. As an exception, we can name it EnvironmentBoundary to begin with. We will decide on the final name later. As a result of this rename, there will be many unnecessary changes. Make sure you undo those. Also follow the guidance in src/Components/AGENTS.md to add an E2E test for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the component to Components.Web assembly as EnvironmentBoundary, reverted unnecessary System.Environment changes, and added E2E test assets. Commit: 0b21f28
…nd add E2E tests Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
javiercn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot some more feedback.
| <Reference Include="Microsoft.AspNetCore.Components" /> | ||
| <Reference Include="Microsoft.AspNetCore.Components.Web" /> | ||
| <Reference Include="Microsoft.Extensions.DependencyInjection" /> | ||
| <Reference Include="Microsoft.Extensions.Hosting.Abstractions" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed since it comes in from Components.Web transitively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed. It comes transitively from Components.Web. Commit: 4b1e0c7
| /// <summary> | ||
| /// An implementation of <see cref="IHostEnvironment"/> that wraps <see cref="IWebAssemblyHostEnvironment"/>. | ||
| /// </summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// <summary> | |
| /// An implementation of <see cref="IHostEnvironment"/> that wraps <see cref="IWebAssemblyHostEnvironment"/>. | |
| /// </summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the XML doc comment. Commit: 4b1e0c7
| <Reference Include="Microsoft.AspNetCore.Components.Web" /> | ||
| <Reference Include="Microsoft.Extensions.Configuration.Json" /> | ||
| <Reference Include="Microsoft.Extensions.Configuration.Binder" /> | ||
| <Reference Include="Microsoft.Extensions.Hosting.Abstractions" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed since it will come in from Components.Web?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed. It comes transitively from Components.Web. Commit: 4b1e0c7
… and XML doc comments Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a new EnvironmentBoundary component to Blazor for conditional rendering based on the hosting environment, addressing the need for a unified approach that works across both Server and WebAssembly scenarios. The implementation follows the MVC EnvironmentTagHelper semantics with Include and Exclude parameters for environment-based rendering control.
Key Changes
- Added
EnvironmentBoundarycomponent inMicrosoft.AspNetCore.Components.Webwith case-insensitive environment matching - Implemented WebAssembly support via
WebAssemblyHostEnvironmentAdapterthat bridgesIWebAssemblyHostEnvironmenttoIHostEnvironment - Added comprehensive unit tests and E2E tests covering various environment matching scenarios
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Components/Web/src/EnvironmentBoundary.cs | Core component implementation with Include/Exclude logic and environment name parsing |
| src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj | Added reference to Microsoft.Extensions.Hosting.Abstractions |
| src/Components/Web/src/PublicAPI.Unshipped.txt | Public API surface additions for EnvironmentBoundary |
| src/Components/Web/test/EnvironmentBoundaryTest.cs | Comprehensive unit tests covering matching logic, case-insensitivity, and edge cases |
| src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj | Added reference to Microsoft.Extensions.Hosting.Abstractions for tests |
| src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostEnvironmentAdapter.cs | Adapter implementing IHostEnvironment by wrapping IWebAssemblyHostEnvironment |
| src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs | Registered IHostEnvironment adapter in WebAssembly DI container |
| src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj | Added reference to Microsoft.Extensions.Hosting.Abstractions |
| src/Components/test/testassets/BasicTestApp/EnvironmentBoundaryContainer.razor | Test component demonstrating various usage patterns |
| src/Components/test/E2ETest/Tests/EnvironmentBoundaryTest.cs | E2E tests verifying component behavior in Development environment |
| var currentEnvironmentName = HostEnvironment.EnvironmentName?.Trim(); | ||
|
|
||
| if (string.IsNullOrEmpty(currentEnvironmentName)) | ||
| { |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior when EnvironmentName is null or empty differs from the MVC EnvironmentTagHelper. The MVC tag helper renders content when the environment name is not set (lines 82-86 of EnvironmentTagHelper.cs return early without suppressing output). This component returns false (hiding content) in the same scenario.
For consistency with the existing MVC tag helper, consider changing this to return true (render content) when the environment name is null/empty and no Include/Exclude are specified. Alternatively, if hiding content is the desired behavior, this should be documented as an intentional deviation from the MVC tag helper.
| { | |
| { | |
| // For consistency with MVC EnvironmentTagHelper, render content when environment name is not set | |
| // and no Include/Exclude are specified. | |
| if (string.IsNullOrWhiteSpace(Include) && string.IsNullOrWhiteSpace(Exclude)) | |
| { | |
| return true; | |
| } |
javiercn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot seems like you have failing E2E tests.
Check failure on line 90 in Build log
@azure-pipelines
azure-pipelines
/ aspnetcore-components-e2e
Build log #L90
Bash exited with code '1'.
Check failure on line 1 in Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.DisplaysCurrentEnvironment
@azure-pipelines
azure-pipelines
/ aspnetcore-components-e2e
Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.DisplaysCurrentEnvironment
OpenQA.Selenium.NoSuchElementException : Cannot locate option with value: BasicTestApp.EnvironmentBoundaryContainer; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#nosuchelementexception
Check failure on line 1 in Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.RendersContentWhenEnvironmentMatches
@azure-pipelines
azure-pipelines
/ aspnetcore-components-e2e
Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.RendersContentWhenEnvironmentMatches
OpenQA.Selenium.NoSuchElementException : Cannot locate option with value: BasicTestApp.EnvironmentBoundaryContainer; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#nosuchelementexception
Check failure on line 1 in Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.HidesContentWhenEnvironmentDoesNotMatch
@azure-pipelines
azure-pipelines
/ aspnetcore-components-e2e
Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.HidesContentWhenEnvironmentDoesNotMatch
OpenQA.Selenium.NoSuchElementException : Cannot locate option with value: BasicTestApp.EnvironmentBoundaryContainer; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#nosuchelementexception
Follow the instructions in src/Components/Agents.md to manually validate the scenarios and run the test locally until you fix them.
…d align null environment behavior with MVC Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Fixed. Added |
javiercn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot tests still failing.
@azure-pipelines
azure-pipelines
/ aspnetcore-components-e2e
Microsoft.AspNetCore.Components.E2ETest.Tests.EnvironmentBoundaryTest.HidesContentWhenEnvironmentDoesNotMatch
OpenQA.Selenium.BrowserAssertFailedException : Xunit.Sdk.EmptyException: Assert.Empty() Failure: Collection was not empty
Collection: [Element (id = f.042FF0182B862ADB4C4BCFB0313DA977.d.B0FF95B3D479CBC4752C29CB2AB8BDFB.e.13)]
at Xunit.Assert.Empty(IEnumerable collection) in /_/src/xunit.assert/Asserts/CollectionAsserts.cs:line 466
at Microsoft.AspNetCore.E2ETesting.WaitAssert.<>c__DisplayClass21_0`1.b__0(IWebDriver _) in /home/vsts/work/1/s/src/Shared/E2ETesting/WaitAssert.cs:line 109
Screen shot captured at '/home/vsts/work/1/s/src/Components/test/E2ETest/bin/screenshots/9d7e83583dd047d5869f298bb238d6bf.png'
Page content:
<base href="/subdir/">
<link href="style.css" rel="stylesheet">
<link rel="icon" href="data:,">
<!-- Used by ExternalContentPackage -->
<link href="_content/TestContentPackage/styles.css" rel="stylesheet">
<!-- App bundle that contains a reference to the scoped css bundle created by TestContentPackage -->
<link href="BasicTestApp.styles.css" rel="stylesheet">
…e assertions Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Add a built-in EnvironmentBoundary component for Blazor
Add EnvironmentBoundary component for conditional rendering based on hosting environment
Description
Adds a built-in
EnvironmentBoundarycomponent similar to the MVC environment tag helper. Currently, Blazor developers must injectIHostEnvironmentorIWebAssemblyHostEnvironmentand write conditional logic manually—which is error-prone for components that run on both Server and WebAssembly.Changes
New
EnvironmentBoundarycomponent (Microsoft.AspNetCore.Components.Web.EnvironmentBoundary)Include: comma-separated environments to render inExclude: comma-separated environments to suppress rendering (takes precedence)Components.WebassemblyWebAssembly support
WebAssemblyHostEnvironmentAdapterimplementingIHostEnvironmentMicrosoft.Extensions.Hosting.Abstractionsreference added toComponents.Web(flows transitively to dependent projects)Unit and E2E tests
Microsoft.AspNetCore.Components.Web.TestsEnvironmentBoundaryContainer.razor) in BasicTestAppEnvironmentBoundaryTest.cswith proper environment detection and element existence assertionsUsage
Original prompt
This section details on the original issue you should resolve
<issue_title>Add a built-in Environment component</issue_title>
<issue_description>MVC & Razor Pages have an environment tag helper for rendering content based on the current environment. In Blazor, you have to inject either the
IHostEnvironmentorIWebAssemblyHostEnvironmentservices and then write conditional rendering logic. This is tricky to do correctly for components that run on WebAssembly and are prerendered because you don't have a common way to get the environment that works for server and client. We have complicated explanations in the Blazor docs on how to handle this. We should consider just adding an Environment component, similar to the environment tag helper, that makes it easy to render content based on the current environment.</issue_description><agent_instructions>Here's a sketch of the
Environmentcomponent implementation:Implementation
Usage Examples
Key Design Decisions
IHostEnvironmentfromMicrosoft.Extensions. Hosting.Abstractions- available in both Server and WebAssembly hosting scenariosEnvironmentTagHelpersemantics -Include/Excludeparameter names and behavior match the existing MVC tag helper. Exclude takes precedence over Include.Includeinstead...💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.