Skip to content

Commit dbd6a51

Browse files
Blazor Web template updates (#49801)
1 parent b403634 commit dbd6a51

File tree

66 files changed

+690
-6497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+690
-6497
lines changed

src/Components/Components/src/Routing/Router.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Reflection.Metadata;
88
using System.Runtime.ExceptionServices;
99
using Microsoft.AspNetCore.Components.HotReload;
10+
using Microsoft.AspNetCore.Components.Rendering;
1011
using Microsoft.Extensions.Logging;
1112
using Microsoft.Extensions.DependencyInjection;
1213

@@ -67,7 +68,6 @@ static readonly IReadOnlyDictionary<string, object> _emptyParametersDictionary
6768
/// Gets or sets the content to display when no match is found for the requested route.
6869
/// </summary>
6970
[Parameter]
70-
[EditorRequired]
7171
public RenderFragment NotFound { get; set; }
7272

7373
/// <summary>
@@ -130,13 +130,6 @@ public async Task SetParametersAsync(ParameterView parameters)
130130
throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(Found)}.");
131131
}
132132

133-
// NotFound content is mandatory, because even though we could display a default message like "Not found",
134-
// it has to be specified explicitly so that it can also be wrapped in a specific layout
135-
if (NotFound == null)
136-
{
137-
throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(NotFound)}.");
138-
}
139-
140133
if (!_onNavigateCalled)
141134
{
142135
_onNavigateCalled = true;
@@ -255,7 +248,7 @@ internal virtual void Refresh(bool isNavigationIntercepted)
255248
// We did not find a Component that matches the route.
256249
// Only show the NotFound content if the application developer programatically got us here i.e we did not
257250
// intercept the navigation. In all other cases, force a browser navigation since this could be non-Blazor content.
258-
_renderHandle.Render(NotFound);
251+
_renderHandle.Render(NotFound ?? DefaultNotFoundContent);
259252
}
260253
else
261254
{
@@ -265,6 +258,17 @@ internal virtual void Refresh(bool isNavigationIntercepted)
265258
}
266259
}
267260

261+
private static void DefaultNotFoundContent(RenderTreeBuilder builder)
262+
{
263+
// This output can't use any layout (none is specified), and it can't use any web-specific concepts
264+
// such as <p role="alert">, and we can't localize the output. However none of that matters because
265+
// in all cases we expect either:
266+
// 1. The app to be hosted with MapRazorPages, and then it will never use any NotFound content
267+
// 2. Or, the app to supply its own NotFound content
268+
// ... so this is just a fallback for badly-set-up apps.
269+
builder.AddContent(0, "Not found");
270+
}
271+
268272
internal async ValueTask RunOnNavigateAsync(string path, bool isNavigationIntercepted)
269273
{
270274
// Cancel the CTS instead of disposing it, since disposing does not

src/Components/Components/test/Routing/RouterTest.cs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public async Task UsesCurrentRouteMatchingIfSpecified()
182182
// Arrange
183183
// Current routing prefers exactly-matched patterns over {*someWildcard}, no matter
184184
// how many segments are in the exact match
185-
_navigationManager.NotifyLocationChanged("https://www.example.com/subdir/a/b", false);
185+
_navigationManager.NotifyLocationChanged("https://www.example.com/subdir/a/b/c", false);
186186
var parameters = new Dictionary<string, object>
187187
{
188188
{ nameof(Router.AppAssembly), typeof(RouterTest).Assembly },
@@ -224,6 +224,47 @@ await _renderer.Dispatcher.InvokeAsync(() =>
224224
Assert.Equal(1, refreshCalled);
225225
}
226226

227+
[Fact]
228+
public async Task UsesNotFoundContentIfSpecified()
229+
{
230+
// Arrange
231+
_navigationManager.NotifyLocationChanged("https://www.example.com/subdir/nonexistent", false);
232+
var parameters = new Dictionary<string, object>
233+
{
234+
{ nameof(Router.AppAssembly), typeof(RouterTest).Assembly },
235+
{ nameof(Router.NotFound), (RenderFragment)(builder => builder.AddContent(0, "Custom content")) },
236+
};
237+
238+
// Act
239+
await _renderer.Dispatcher.InvokeAsync(() =>
240+
_router.SetParametersAsync(ParameterView.FromDictionary(parameters)));
241+
242+
// Assert
243+
var renderedFrame = _renderer.Batches.First().ReferenceFrames.First();
244+
Assert.Equal(RenderTreeFrameType.Text, renderedFrame.FrameType);
245+
Assert.Equal("Custom content", renderedFrame.TextContent);
246+
}
247+
248+
[Fact]
249+
public async Task UsesDefaultNotFoundContentIfNotSpecified()
250+
{
251+
// Arrange
252+
_navigationManager.NotifyLocationChanged("https://www.example.com/subdir/nonexistent", false);
253+
var parameters = new Dictionary<string, object>
254+
{
255+
{ nameof(Router.AppAssembly), typeof(RouterTest).Assembly }
256+
};
257+
258+
// Act
259+
await _renderer.Dispatcher.InvokeAsync(() =>
260+
_router.SetParametersAsync(ParameterView.FromDictionary(parameters)));
261+
262+
// Assert
263+
var renderedFrame = _renderer.Batches.First().ReferenceFrames.First();
264+
Assert.Equal(RenderTreeFrameType.Text, renderedFrame.FrameType);
265+
Assert.Equal("Not found", renderedFrame.TextContent);
266+
}
267+
227268
internal class TestNavigationManager : NavigationManager
228269
{
229270
public TestNavigationManager() =>
@@ -260,9 +301,9 @@ public class FebComponent : ComponentBase { }
260301
[Route("jan")]
261302
public class JanComponent : ComponentBase { }
262303

263-
[Route("{*matchAnything}")]
304+
[Route("a/{*matchAnything}")]
264305
public class MatchAnythingComponent : ComponentBase { }
265306

266-
[Route("a/b")]
307+
[Route("a/b/c")]
267308
public class MultiSegmentRouteComponent : ComponentBase { }
268309
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
2+
3+
<PropertyGroup>
4+
<TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
8+
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
9+
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">BlazorWeb-CSharp.Client</RootNamespace>
10+
<AssemblyName Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">`$(AssemblyName.Replace(' ', '_'))</AssemblyName>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="${MicrosoftAspNetCoreComponentsWebAssemblyVersion}" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<NoDefaultLaunchSettingsFile Condition="'$(ExcludeLaunchSettings)' == 'True'">True</NoDefaultLaunchSettingsFile>
8+
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">BlazorWeb-CSharp</RootNamespace>
9+
<AssemblyName Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">`$(AssemblyName.Replace(' ', '_'))</AssemblyName>
10+
</PropertyGroup>
11+
<!--#if UseWebAssembly -->
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\BlazorWeb-CSharp.Client\BlazorWeb-CSharp.Client.csproj" />
15+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="${MicrosoftAspNetCoreComponentsWebAssemblyServerVersion}" />
16+
</ItemGroup>
17+
<!--#endif -->
18+
19+
</Project>

src/ProjectTemplates/Web.ProjectTemplates/Components-CSharp.csproj.in

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
<GeneratedContent Include="WebApi-FSharp.fsproj.in" OutputPath="content/WebApi-FSharp/Company.WebApplication1.fsproj" />
6363
<GeneratedContent Include="Worker-CSharp.csproj.in" OutputPath="content/Worker-CSharp/Company.Application1.csproj" />
6464
<GeneratedContent Include="Worker-FSharp.fsproj.in" OutputPath="content/Worker-FSharp/Company.Application1.fsproj" />
65-
<GeneratedContent Include="Components-CSharp.csproj.in" OutputPath="content/Components-CSharp/Components-CSharp.csproj" />
65+
<GeneratedContent Include="BlazorWeb-CSharp.csproj.in" OutputPath="content/BlazorWeb-CSharp/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj" />
66+
<GeneratedContent Include="BlazorWeb-CSharp.Client.csproj.in" OutputPath="content/BlazorWeb-CSharp/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj" />
6667
<GeneratedContent Include="ComponentsWebAssembly-CSharp.csproj.in" OutputPath="content/ComponentsWebAssembly-CSharp/ComponentsWebAssembly-CSharp.csproj" />
6768
<GeneratedContent Include="EmptyComponentsWebAssembly-CSharp.csproj.in" OutputPath="content/EmptyComponentsWebAssembly-CSharp/EmptyComponentsWebAssembly-CSharp.csproj" />
6869
</ItemGroup>
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
"longName": "no-restore",
66
"shortName": ""
77
},
8-
"PWA": {
9-
"longName": "pwa",
10-
"isHidden": true
11-
},
128
"UseServer": {
139
"longName": "use-server"
1410
},
1511
"UseWebAssembly": {
16-
"longName": "use-wasm",
12+
"longName": "use-wasm"
13+
},
14+
"Empty": {
15+
"longName": "empty"
16+
},
17+
"IncludeSampleContent": {
1718
"isHidden": true
1819
},
1920
"Framework": {

src/ProjectTemplates/Web.ProjectTemplates/content/Components-CSharp/.template.config/ide.host.json renamed to src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/.template.config/ide.host.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@
1111
},
1212
{
1313
"id": "UseWebAssembly",
14-
"isVisible": false,
14+
"isVisible": true,
1515
"persistenceScope": "templateGroup"
1616
},
1717
{
18-
"id": "PWA",
19-
"isVisible": false,
20-
"persistenceScope": "templateGroup"
18+
"id": "UseProgramMain",
19+
"isVisible": true,
20+
"persistenceScope": "shared",
21+
"persistenceScopeName": "Microsoft"
2122
},
2223
{
23-
"id": "UseProgramMain",
24+
"id": "IncludeSampleContent",
2425
"isVisible": true,
2526
"persistenceScope": "shared",
2627
"persistenceScopeName": "Microsoft"
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"symbols/UseWebAssembly/description": "Pokud je zadáno, nakonfiguruje projekt tak, aby vykreslovat komponenty interaktivně v prohlížeči pomocí WebAssembly.",
1515
"symbols/UseServer/displayName": "_Použít interaktivní serverové komponenty",
1616
"symbols/UseServer/description": "Pokud je zadáno, nakonfiguruje projekt tak, aby vykresloval komponenty interaktivně na serveru.",
17-
"symbols/PWA/displayName": "_Progresivní webová aplikace",
18-
"symbols/PWA/description": "Pokud je tato možnost zadaná, vytvoří progresivní webovou aplikaci (PWA), která podporuje instalaci a offline použití.",
17+
"symbols/IncludeSampleContent/displayName": "_Include sample pages",
18+
"symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.",
19+
"symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.",
1920
"symbols/NoHttps/description": "Určuje, jestli se má protokol HTTPS vypnout. Tato možnost platí jenom v případě, že se pro --auth nepoužívají Individual, IndividualB2C, SingleOrg ani MultiOrg.",
2021
"symbols/UseProgramMain/displayName": "Nepoužívat _příkazy nejvyšší úrovně",
2122
"symbols/UseProgramMain/description": "Určuje, jestli se má místo příkazů nejvyšší úrovně generovat explicitní třída Program a metoda Main.",
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"symbols/UseWebAssembly/description": "Bei Angabe dieser Option wird das Projekt so konfiguriert, dass Komponenten interaktiv mithilfe von WebAssembly im Browser gerendert werden.",
1515
"symbols/UseServer/displayName": "_Interaktive Serverkomponenten verwenden",
1616
"symbols/UseServer/description": "Bei Angabe dieser Option wird das Projekt so konfiguriert, dass Komponenten interaktiv auf dem Server gerendert werden.",
17-
"symbols/PWA/displayName": "_Progressive Webanwendung",
18-
"symbols/PWA/description": "Wenn angegeben, wird eine Progressive Web Application (PWA) erstellt, die die Installation und Offlineverwendung unterstützt.",
17+
"symbols/IncludeSampleContent/displayName": "_Include sample pages",
18+
"symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.",
19+
"symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.",
1920
"symbols/NoHttps/description": "Ob HTTPS deaktiviert werden soll. Diese Option gilt nur, wenn Individual, IndividualB2C, SingleOrg oder MultiOrg nicht für --auth verwendet werden.",
2021
"symbols/UseProgramMain/displayName": "Keine Anweisungen_der obersten Ebene verwenden",
2122
"symbols/UseProgramMain/description": "Gibt an, ob anstelle von Anweisungen der obersten Ebene eine explizite Programmklasse und eine Main-Methode generiert werden soll.",
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
"symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
1212
"symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used).",
1313
"symbols/UseWebAssembly/displayName": "_Use interactive WebAssembly components",
14-
"symbols/UseWebAssembly/description": "If specified, configures the project to render components interactively in the browser using WebAssembly.",
14+
"symbols/UseWebAssembly/description": "Configures whether to support rendering components interactively in the browser using WebAssembly. The default value is false.",
1515
"symbols/UseServer/displayName": "_Use interactive server components",
16-
"symbols/UseServer/description": "If specified, configures the project to render components interactively on the server.",
17-
"symbols/PWA/displayName": "_Progressive Web Application",
18-
"symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
16+
"symbols/UseServer/description": "Configures whether to support rendering components interactively on the server via a SignalR WebSocket connection. The default value is true.",
17+
"symbols/IncludeSampleContent/displayName": "_Include sample pages",
18+
"symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.",
19+
"symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.",
1920
"symbols/NoHttps/description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth.",
2021
"symbols/UseProgramMain/displayName": "Do not use _top-level statements",
2122
"symbols/UseProgramMain/description": "Whether to generate an explicit Program class and Main method instead of top-level statements.",
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"symbols/UseWebAssembly/description": "Si se especifica, esta opción configura el proyecto para representar los componentes de forma interactiva en el explorador mediante WebAssembly.",
1515
"symbols/UseServer/displayName": "_Use componentes de servidor interactivos",
1616
"symbols/UseServer/description": "Si se especifica, esta opción configura el proyecto para representar los componentes de forma interactiva en el servidor.",
17-
"symbols/PWA/displayName": "_Aplicación web progresiva",
18-
"symbols/PWA/description": "Si se especifica, produce una aplicación web progresiva (PWA) compatible con la instalación y el uso sin conexión.",
17+
"symbols/IncludeSampleContent/displayName": "_Include sample pages",
18+
"symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.",
19+
"symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.",
1920
"symbols/NoHttps/description": "Si se va a desactivar HTTPS. Esta opción solo se aplica si Individual, IndividualB2C, SingleOrg o MultiOrg no se usan para --auth.",
2021
"symbols/UseProgramMain/displayName": "No usar instrucciones de _nivel superior",
2122
"symbols/UseProgramMain/description": "Indica si se debe generar una clase Program explícita y un método Main en lugar de instrucciones de nivel superior.",
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"symbols/UseWebAssembly/description": "S’il est spécifié, configure le projet pour afficher les composants de manière interactive dans le navigateur à l’aide de WebAssembly.",
1515
"symbols/UseServer/displayName": "_Utiliser les composants serveur interactifs",
1616
"symbols/UseServer/description": "Si ce paramètre est spécifié, configure le projet pour afficher les composants de manière interactive sur le serveur.",
17-
"symbols/PWA/displayName": "_Application web progressive",
18-
"symbols/PWA/description": "Si ce paramètre est spécifié, produit une application web progressive (PWA) qui prend en charge l’installation et l’utilisation hors connexion.",
17+
"symbols/IncludeSampleContent/displayName": "_Include sample pages",
18+
"symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.",
19+
"symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.",
1920
"symbols/NoHttps/description": "Indique s’il faut désactiver HTTPS. Cette option s’applique uniquement si Individual, IndividualB2C, SingleOrg ou MultiOrg ne sont pas utilisés pour --auth.",
2021
"symbols/UseProgramMain/displayName": "N’utilisez pas _d’instructions de niveau supérieur.",
2122
"symbols/UseProgramMain/description": "Indique s’il faut générer une classe Programme explicite et une méthode Main au lieu d’instructions de niveau supérieur.",

0 commit comments

Comments
 (0)