Merge branch 'release/5.0-preview8'

This commit is contained in:
Mackinnon Buck 2020-07-22 15:32:19 -07:00
commit 48d73434fe
29 changed files with 881 additions and 66 deletions

View File

@ -1427,6 +1427,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions.Tests", "src\Components\Web.Extensions\test\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "{157605CB-5170-4C1A-980F-4BAE42DB60DE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{2531F00A-54EB-4074-9C0B-9AF9FB3679DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicTestApp", "src\Components\test\testassets\BasicTestApp\BasicTestApp.csproj", "{85D67E40-4B11-48ED-8C43-34590A1FB9ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LazyTestContentPackage", "src\Components\test\testassets\LazyTestContentPackage\LazyTestContentPackage.csproj", "{C0EF53A5-5A94-4849-86B0-2297EA08D649}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComponentsApp.App", "src\Components\test\testassets\ComponentsApp.App\ComponentsApp.App.csproj", "{4FDD820F-8397-41B7-956E-F257DD044BD8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComponentsApp.Server", "src\Components\test\testassets\ComponentsApp.Server\ComponentsApp.Server.csproj", "{CA7C7A53-446F-453A-A57B-78BB1443B8A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestContentPackage", "src\Components\test\testassets\TestContentPackage\TestContentPackage.csproj", "{B32C5882-2313-40D0-A003-2FF33724CFE6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components.TestServer", "src\Components\test\testassets\TestServer\Components.TestServer.csproj", "{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{FED4267E-E5E4-49C5-98DB-8B3F203596EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly", "src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", "{6B2734BF-C61D-4889-ABBF-456A4075D59B}"
@ -6763,6 +6777,78 @@ Global
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.Build.0 = Release|Any CPU
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.ActiveCfg = Release|Any CPU
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.Build.0 = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|x64.ActiveCfg = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|x64.Build.0 = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|x86.ActiveCfg = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Debug|x86.Build.0 = Debug|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|Any CPU.Build.0 = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|x64.ActiveCfg = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|x64.Build.0 = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|x86.ActiveCfg = Release|Any CPU
{85D67E40-4B11-48ED-8C43-34590A1FB9ED}.Release|x86.Build.0 = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|x64.Build.0 = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Debug|x86.Build.0 = Debug|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|Any CPU.Build.0 = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|x64.ActiveCfg = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|x64.Build.0 = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|x86.ActiveCfg = Release|Any CPU
{C0EF53A5-5A94-4849-86B0-2297EA08D649}.Release|x86.Build.0 = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|x64.ActiveCfg = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|x64.Build.0 = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|x86.ActiveCfg = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Debug|x86.Build.0 = Debug|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|Any CPU.Build.0 = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|x64.ActiveCfg = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|x64.Build.0 = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|x86.ActiveCfg = Release|Any CPU
{4FDD820F-8397-41B7-956E-F257DD044BD8}.Release|x86.Build.0 = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|x64.ActiveCfg = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|x64.Build.0 = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|x86.ActiveCfg = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Debug|x86.Build.0 = Debug|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|Any CPU.Build.0 = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|x64.ActiveCfg = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|x64.Build.0 = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|x86.ActiveCfg = Release|Any CPU
{CA7C7A53-446F-453A-A57B-78BB1443B8A8}.Release|x86.Build.0 = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|x64.ActiveCfg = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|x64.Build.0 = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|x86.ActiveCfg = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Debug|x86.Build.0 = Debug|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|Any CPU.Build.0 = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|x64.ActiveCfg = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|x64.Build.0 = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|x86.ActiveCfg = Release|Any CPU
{B32C5882-2313-40D0-A003-2FF33724CFE6}.Release|x86.Build.0 = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|x64.ActiveCfg = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|x64.Build.0 = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|x86.ActiveCfg = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Debug|x86.Build.0 = Debug|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|Any CPU.Build.0 = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|x64.ActiveCfg = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|x64.Build.0 = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|x86.ActiveCfg = Release|Any CPU
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00}.Release|x86.Build.0 = Release|Any CPU
{6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -7634,6 +7720,13 @@ Global
{F71FE795-9923-461B-9809-BB1821A276D0} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF}
{8294A74F-7DAA-4B69-BC56-7634D93C9693} = {F71FE795-9923-461B-9809-BB1821A276D0}
{157605CB-5170-4C1A-980F-4BAE42DB60DE} = {F71FE795-9923-461B-9809-BB1821A276D0}
{2531F00A-54EB-4074-9C0B-9AF9FB3679DC} = {0508E463-0269-40C9-B5C2-3B600FB2A28B}
{85D67E40-4B11-48ED-8C43-34590A1FB9ED} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{C0EF53A5-5A94-4849-86B0-2297EA08D649} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{4FDD820F-8397-41B7-956E-F257DD044BD8} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{CA7C7A53-446F-453A-A57B-78BB1443B8A8} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{B32C5882-2313-40D0-A003-2FF33724CFE6} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{26F88A06-319C-43F3-9FD9-8BC2D29F8C00} = {2531F00A-54EB-4074-9C0B-9AF9FB3679DC}
{FED4267E-E5E4-49C5-98DB-8B3F203596EE} = {562D5067-8CD8-4F19-BCBB-873204932C61}
{6B2734BF-C61D-4889-ABBF-456A4075D59B} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE}
{83371889-9A3E-4D16-AE77-EB4F83BC6374} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE}

View File

@ -68,12 +68,12 @@ namespace Microsoft.AspNetCore.Components.Routing
/// <summary>
/// Get or sets the content to display when asynchronous navigation is in progress.
/// </summary>
[Parameter] public RenderFragment Navigating { get; set; }
[Parameter] public RenderFragment? Navigating { get; set; }
/// <summary>
/// Gets or sets a handler that should be called before navigating to a new page.
/// </summary>
[Parameter] public Func<NavigationContext, Task> OnNavigateAsync { get; set; }
[Parameter] public Func<NavigationContext, Task>? OnNavigateAsync { get; set; }
private RouteTable Routes { get; set; }
@ -195,10 +195,6 @@ namespace Microsoft.AspNetCore.Components.Routing
private async ValueTask<bool> RunOnNavigateAsync(string path, Task previousOnNavigate)
{
if (OnNavigateAsync == null)
{
return true;
}
// Cancel the CTS instead of disposing it, since disposing does not
// actually cancel and can cause unintended Object Disposed Exceptions.
@ -210,6 +206,11 @@ namespace Microsoft.AspNetCore.Components.Routing
// invocation.
await previousOnNavigate;
if (OnNavigateAsync == null)
{
return true;
}
_onNavigateCts = new CancellationTokenSource();
var navigateContext = new NavigationContext(path, _onNavigateCts.Token);
@ -227,14 +228,12 @@ namespace Microsoft.AspNetCore.Components.Routing
if (e.CancellationToken != navigateContext.CancellationToken)
{
var rethrownException = new InvalidOperationException("OnNavigateAsync can only be cancelled via NavigateContext.CancellationToken.", e);
_renderHandle.Render(builder => ExceptionDispatchInfo.Capture(rethrownException).Throw());
return false;
_renderHandle.Render(builder => ExceptionDispatchInfo.Throw(rethrownException));
}
}
catch (Exception e)
{
_renderHandle.Render(builder => ExceptionDispatchInfo.Capture(e).Throw());
return false;
_renderHandle.Render(builder => ExceptionDispatchInfo.Throw(e));
}
return false;

View File

@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
internal static class HeadManagementJSRuntimeExtensions
{
private const string JsFunctionsPrefix = "_blazorHeadManager";
public static ValueTask SetTitleAsync(this IJSRuntime jsRuntime, string title)
{
return jsRuntime.InvokeVoidAsync($"{JsFunctionsPrefix}.setTitle", title);
}
public static ValueTask AddOrUpdateHeadTagAsync(this IJSRuntime jsRuntime, TagElement tag, string id)
{
return jsRuntime.InvokeVoidAsync($"{JsFunctionsPrefix}.addOrUpdateHeadTag", tag, id);
}
public static ValueTask RemoveHeadTagAsync(this IJSRuntime jsRuntime, string id)
{
return jsRuntime.InvokeVoidAsync($"{JsFunctionsPrefix}.removeHeadTag", id);
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
/// <summary>
/// Serves as a base for components that represent tags in the HTML head.
/// </summary>
public abstract class HeadTagBase : ComponentBase, IDisposable
{
private readonly string _id = Guid.NewGuid().ToString("N");
private TagElement _tagElement;
private bool _hasRendered;
[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;
/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the meta element.
/// </summary>
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object>? Attributes { get; set; }
/// <summary>
/// Gets the name of the tag being represented.
/// </summary>
protected abstract string TagName { get; }
/// <inheritdoc />
protected override void OnParametersSet()
{
_tagElement = new TagElement(TagName, Attributes);
}
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
_hasRendered = true;
await JSRuntime.AddOrUpdateHeadTagAsync(_tagElement, _id);
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, $"<!--Head:{JsonSerializer.Serialize(_tagElement, JsonSerializerOptionsProvider.Options)}-->");
}
/// <inheritdoc />
public void Dispose()
{
if (_hasRendered)
{
_ = JSRuntime.RemoveHeadTagAsync(_id);
}
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
/// <summary>
/// A component that adds a link tag to the HTML head.
/// </summary>
public sealed class Link : HeadTagBase
{
/// <inheritdoc />
protected override string TagName => "link";
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
/// <summary>
/// A component that adds a meta tag to the HTML head.
/// </summary>
public sealed class Meta : HeadTagBase
{
/// <inheritdoc />
protected override string TagName => "meta";
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
internal readonly struct TagElement
{
public string Type => "tag";
public string TagName { get; }
public IReadOnlyDictionary<string, object>? Attributes { get; }
public TagElement(string tagName, IReadOnlyDictionary<string, object>? attributes)
{
TagName = tagName;
Attributes = attributes;
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
/// <summary>
/// A component that changes the title of the document.
/// </summary>
public sealed class Title : ComponentBase
{
[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;
/// <summary>
/// Gets or sets the value to use as the document's title.
/// </summary>
[Parameter]
public string Value { get; set; } = string.Empty;
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JSRuntime.SetTitleAsync(Value);
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, $"<!--Head:{JsonSerializer.Serialize(new TitleElement(Value), JsonSerializerOptionsProvider.Options)}-->");
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Web.Extensions.Head
{
internal readonly struct TitleElement
{
public string Type => "title";
public string Title { get; }
public TitleElement(string title)
{
Title = title;
}
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.JSInterop" />
</ItemGroup>

View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Web.Extensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,102 @@
(function () {
// Local helpers
const blazorIdAttributeName = '_blazor_id';
const headCommentRegularExpression = /\W*Head:[^{]*(.*)$/;
const prerenderedTags = [];
function createHeadTag({ tagName, attributes }, id) {
const tagElement = document.createElement(tagName);
// The id is undefined during prerendering
if (id) {
tagElement.setAttribute(blazorIdAttributeName, id);
}
if (attributes) {
Object.keys(attributes).forEach(key => {
tagElement.setAttribute(key, attributes[key]);
});
}
document.head.appendChild(tagElement);
return tagElement;
}
function resolvePrerenderedHeadComponents(node) {
node.childNodes.forEach((childNode) => {
const headElement = parseHeadComment(childNode);
if (headElement) {
applyPrerenderedHeadComponent(headElement);
} else {
resolvePrerenderedHeadComponents(childNode);
}
});
}
function applyPrerenderedHeadComponent(headElement) {
switch (headElement.type) {
case 'title':
setTitle(headElement.title);
break;
case 'tag':
const tag = createHeadTag(headElement);
prerenderedTags.push(tag);
break;
default:
throw new Error(`Invalid head element type '${headElement.type}'.`);
}
}
function parseHeadComment(node) {
if (!node || node.nodeType != Node.COMMENT_NODE) {
return;
}
const commentText = node.textContent;
if (!commentText) {
return;
}
const definition = headCommentRegularExpression.exec(commentText);
const json = definition && definition[1];
return json && JSON.parse(json);
}
function removePrerenderedHeadTags() {
prerenderedTags.forEach((tag) => {
tag.remove();
});
prerenderedTags.length = 0;
}
// Exported functions
function setTitle(title) {
document.title = title;
}
function addOrUpdateHeadTag(tag, id) {
removePrerenderedHeadTags();
removeHeadTag(id);
createHeadTag(tag, id);
}
function removeHeadTag(id) {
let tag = document.head.querySelector(`[${blazorIdAttributeName}='${id}']`);
tag && tag.remove();
}
window._blazorHeadManager = {
setTitle,
addOrUpdateHeadTag,
removeHeadTag,
};
resolvePrerenderedHeadComponents(document);
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -324,9 +324,18 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
const assembliesToLoad = BINDING.mono_array_to_js_array<System_String, string>(assembliesToLoadDotNetArray);
const lazyAssemblies = resourceLoader.bootConfig.resources.lazyAssembly;
if (lazyAssemblies) {
const resourcePromises = Promise.all(assembliesToLoad
.filter(assembly => lazyAssemblies.hasOwnProperty(assembly))
if (!lazyAssemblies) {
throw new Error("No assemblies have been marked as lazy-loadable. Use the 'BlazorWebAssemblyLazyLoad' item group in your project file to enable lazy loading an assembly.");
}
var assembliesMarkedAsLazy = assembliesToLoad.filter(assembly => lazyAssemblies.hasOwnProperty(assembly));
if (assembliesMarkedAsLazy.length != assembliesToLoad.length) {
var notMarked = assembliesToLoad.filter(assembly => !assembliesMarkedAsLazy.includes(assembly));
throw new Error(`${notMarked.join()} must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.`);
}
const resourcePromises = Promise.all(assembliesMarkedAsLazy
.map(assembly => resourceLoader.loadResource(assembly, `_framework/${assembly}`, lazyAssemblies[assembly], 'assembly'))
.map(async resource => (await resource.response).arrayBuffer()));
@ -345,8 +354,6 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
return resourcesToLoad.length;
}));
}
return BINDING.js_to_mono_obj(Promise.resolve(0));
}
});
module.postRun.push(() => {

View File

@ -132,8 +132,10 @@ export function removeLogicalChild(parent: LogicalElement, childIndex: number) {
// If it's a logical container, also remove its descendants
if (childToRemove instanceof Comment) {
const grandchildrenArray = getLogicalChildrenArray(childToRemove);
while (grandchildrenArray.length > 0) {
removeLogicalChild(childToRemove, 0);
if (grandchildrenArray) {
while (grandchildrenArray.length > 0) {
removeLogicalChild(childToRemove, 0);
}
}
}

View File

@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
/// <summary>
/// Gets the logging builder for configuring logging services.
/// </summary>
public ILoggingBuilder Logging { get; }
public ILoggingBuilder Logging { get; }
/// <summary>
/// Registers a <see cref="IServiceProviderFactory{TBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
Services.AddSingleton<IJSRuntime>(DefaultWebAssemblyJSRuntime.Instance);
Services.AddSingleton<NavigationManager>(WebAssemblyNavigationManager.Instance);
Services.AddSingleton<INavigationInterception>(WebAssemblyNavigationInterception.Instance);
Services.AddSingleton(provider => new LazyAssemblyLoader(provider));
Services.AddSingleton(new LazyAssemblyLoader(DefaultWebAssemblyJSRuntime.Instance));
Services.AddLogging(builder => {
builder.AddProvider(new WebAssemblyConsoleLoggerProvider(DefaultWebAssemblyJSRuntime.Instance));
});

View File

@ -9,7 +9,6 @@ using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Microsoft.JSInterop.WebAssembly;
@ -20,19 +19,18 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
///
/// Supports finding pre-loaded assemblies in a server or pre-rendering context.
/// </summary>
public class LazyAssemblyLoader
public sealed class LazyAssemblyLoader
{
internal const string GetDynamicAssemblies = "window.Blazor._internal.getLazyAssemblies";
internal const string ReadDynamicAssemblies = "window.Blazor._internal.readLazyAssemblies";
private List<Assembly> _loadedAssemblyCache = new List<Assembly>();
private readonly IJSRuntime _jsRuntime;
private readonly HashSet<string> _loadedAssemblyCache;
private readonly IServiceProvider _provider;
public LazyAssemblyLoader(IServiceProvider provider)
public LazyAssemblyLoader(IJSRuntime jsRuntime)
{
_provider = provider;
_loadedAssemblyCache = AppDomain.CurrentDomain.GetAssemblies().ToList();
_jsRuntime = jsRuntime;
_loadedAssemblyCache = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name + ".dll").ToHashSet();
}
/// <summary>
@ -55,37 +53,45 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
private Task<IEnumerable<Assembly>> LoadAssembliesInServerAsync(IEnumerable<string> assembliesToLoad)
{
var loadedAssemblies = _loadedAssemblyCache.Where(assembly =>
assembliesToLoad.Contains(assembly.GetName().Name + ".dll"));
var loadedAssemblies = new List<Assembly>();
if (loadedAssemblies.Count() != assembliesToLoad.Count())
try
{
var unloadedAssemblies = assembliesToLoad.Except(loadedAssemblies.Select(a => a.GetName().Name + ".dll"));
throw new InvalidOperationException($"Unable to find the following assemblies: {string.Join(",", unloadedAssemblies)}. Make sure that the appplication is referencing the assemblies and that they are present in the output folder.");
foreach (var assemblyName in assembliesToLoad)
{
loadedAssemblies.Add(Assembly.Load(Path.GetFileNameWithoutExtension(assemblyName)));
}
}
catch (FileNotFoundException ex)
{
throw new InvalidOperationException($"Unable to find the following assembly: {ex.FileName}. Make sure that the appplication is referencing the assemblies and that they are present in the output folder.");
}
return Task.FromResult(loadedAssemblies);
return Task.FromResult<IEnumerable<Assembly>>(loadedAssemblies);
}
private async Task<IEnumerable<Assembly>> LoadAssembliesInClientAsync(IEnumerable<string> assembliesToLoad)
{
var jsRuntime = _provider.GetRequiredService<IJSRuntime>();
// Only load assemblies that haven't already been lazily-loaded
var newAssembliesToLoad = assembliesToLoad.Except(_loadedAssemblyCache.Select(a => a.GetName().Name + ".dll"));
// Check to see if the assembly has already been loaded and avoids reloading it if so.
// Note: in the future, as an extra precuation, we can call `Assembly.Load` and check
// to see if it throws FileNotFound to ensure that an assembly hasn't been loaded
// between when the cache of loaded assemblies was instantiated in the constructor
// and the invocation of this method.
var newAssembliesToLoad = assembliesToLoad.Where(assembly => !_loadedAssemblyCache.Contains(assembly));
var loadedAssemblies = new List<Assembly>();
var count = (int)await ((WebAssemblyJSRuntime)jsRuntime).InvokeUnmarshalled<string[], object, object, Task<object>>(
GetDynamicAssemblies,
assembliesToLoad.ToArray(),
null,
null);
var count = (int)await ((WebAssemblyJSRuntime)_jsRuntime).InvokeUnmarshalled<string[], object, object, Task<object>>(
GetDynamicAssemblies,
newAssembliesToLoad.ToArray(),
null,
null);
if (count == 0)
{
return loadedAssemblies;
}
var assemblies = ((WebAssemblyJSRuntime)jsRuntime).InvokeUnmarshalled<object, object, object, object[]>(
var assemblies = ((WebAssemblyJSRuntime)_jsRuntime).InvokeUnmarshalled<object, object, object, object[]>(
ReadDynamicAssemblies,
null,
null,
@ -99,7 +105,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
// into the default app context.
var loadedAssembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(assembly));
loadedAssemblies.Add(loadedAssembly);
_loadedAssemblyCache.Add(loadedAssembly);
_loadedAssemblyCache.Add(loadedAssembly.GetName().Name + ".dll");
}
return loadedAssemblies;

View File

@ -81,6 +81,51 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
AssertLogDoesNotContainCriticalMessages("Could not load file or assembly 'Newtonsoft.Json");
}
[Fact]
public void CanInfluenceHeadDuringPrerender()
{
Navigate("/prerendered/prerendered-head");
var metaWithBindings = Browser.FindElement(By.Id("meta-with-bindings"));
var metaNoBindings = Browser.FindElement(By.Id("meta-no-bindings"));
// Validate updated head during prerender
Browser.Equal("Initial title", () => Browser.Title);
Browser.Equal("Initial meta content", () => metaWithBindings.GetAttribute("content"));
Browser.Equal("Immutable meta content", () => metaNoBindings.GetAttribute("content"));
BeginInteractivity();
// Wait for elements to be recreated with internal ids to permit mutation
metaWithBindings = WaitForNewElement(metaWithBindings, "meta-with-bindings");
metaNoBindings = WaitForNewElement(metaNoBindings, "meta-no-bindings");
// Validate updated head after prerender
Browser.Equal("Initial title", () => Browser.Title);
Browser.Equal("Initial meta content", () => metaWithBindings.GetAttribute("content"));
Browser.Equal("Immutable meta content", () => metaNoBindings.GetAttribute("content"));
// Change parameter of meta component
var inputMetaBinding = Browser.FindElement(By.Id("input-meta-binding"));
inputMetaBinding.Clear();
inputMetaBinding.SendKeys("Updated meta content\n");
// Wait for meta tag to be recreated with new attributes
metaWithBindings = WaitForNewElement(metaWithBindings, "meta-with-bindings");
// Validate new meta content attribute
Browser.Equal("Updated meta content", () => metaWithBindings.GetAttribute("content"));
IWebElement WaitForNewElement(IWebElement existingElement, string id)
{
var newElement = existingElement;
Browser.NotEqual(existingElement, () => newElement = Browser.FindElement(By.Id(id)) ?? newElement);
return newElement;
}
}
[Fact]
public void CanReadUrlHashOnlyOnceConnected()
{

View File

@ -0,0 +1,176 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using BasicTestApp;
using Microsoft.AspNetCore.Components.E2ETest;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using OpenQA.Selenium;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Components.E2ETests.Tests
{
public class HeadComponentsTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>>
{
public HeadComponentsTest(
BrowserFixture browserFixture,
ToggleExecutionModeServerFixture<Program> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
}
protected override void InitializeAsyncCore()
{
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
Browser.MountTestComponent<ModifyHeadComponent>();
}
[Fact]
public void Title_DoesChangeDocumentTitle()
{
var titleCount = 3;
var titleButtonsById = Enumerable.Range(0, titleCount)
.Select(i => (i, Browser.FindElement(By.Id($"button-title-{i}"))))
.ToList();
Assert.All(titleButtonsById, buttonById =>
{
var (id, button) = buttonById;
button.Click();
Browser.Equal($"Title {id}", () => Browser.Title);
});
}
[Fact]
public void Title_DeepestComponentHasPriority()
{
var nestedTitleButton = Browser.FindElement(By.Id("button-title-nested"));
nestedTitleButton.Click();
Browser.Equal("Layer 4", () => Browser.Title);
}
[Fact]
public void Meta_AddsAndRemovesElements()
{
var metaCount = 3;
var metaButtonsById = Enumerable.Range(0, metaCount)
.Select(i => (i, Browser.FindElement(By.Id($"button-meta-{i}"))))
.ToList();
// Validate adding elements
Assert.All(metaButtonsById, buttonById =>
{
var (id, button) = buttonById;
button.Click();
Browser.Exists(By.Id($"Meta {id}"));
});
// Validate removing elements
Assert.All(metaButtonsById, buttonById =>
{
var (id, button) = buttonById;
button.Click();
Browser.DoesNotExist(By.Id($"Meta {id}"));
});
}
[Fact]
public void Meta_UpdatesSameElementWhenComponentPropertyChanged()
{
var metaAttributeInput1 = Browser.FindElement(By.Id("meta-attr-input-1"));
var metaAttributeInput2 = Browser.FindElement(By.Id("meta-attr-input-2"));
var metaElement = FindMetaElement();
// Validate initial attribute values
Browser.Equal("First attribute", () => metaElement.GetAttribute("attr1"));
Browser.Equal("Second attribute", () => metaElement.GetAttribute("attr2"));
// Update the first parameter of the component
metaAttributeInput1.Clear();
metaAttributeInput1.SendKeys("hello\n");
metaElement = FindMetaElement();
// Validate first attribute updated
Browser.Equal("hello", () => metaElement.GetAttribute("attr1"));
Browser.Equal("Second attribute", () => metaElement.GetAttribute("attr2"));
// Update the second parameter of the component
metaAttributeInput2.Clear();
metaAttributeInput2.SendKeys("world\n");
metaElement = FindMetaElement();
// Validate second attribute updated
Browser.Equal("hello", () => metaElement.GetAttribute("attr1"));
Browser.Equal("world", () => metaElement.GetAttribute("attr2"));
IWebElement FindMetaElement() => Browser.FindElements(By.Id("meta-with-bindings")).Single();
}
[Fact]
public void Link_AddsAndRemovesElements()
{
var linkCount = 3;
var linkButtonsById = Enumerable.Range(0, linkCount)
.Select(i => (i, Browser.FindElement(By.Id($"button-link-{i}"))))
.ToList();
// Validate adding elements
Assert.All(linkButtonsById, buttonById =>
{
var (id, button) = buttonById;
button.Click();
Browser.Exists(By.Id($"Link {id}"));
});
// Validate removing elements
Assert.All(linkButtonsById, buttonById =>
{
var (id, button) = buttonById;
button.Click();
Browser.DoesNotExist(By.Id($"Link {id}"));
});
}
[Fact]
public void Link_UpdatesSameElementWhenComponentPropertyChanged()
{
var linkAttributeInput1 = Browser.FindElement(By.Id("link-attr-input-1"));
var linkAttributeInput2 = Browser.FindElement(By.Id("link-attr-input-2"));
var linkElement = FindLinkElement();
// Validate initial attribute values
Browser.Equal("First attribute", () => linkElement.GetAttribute("attr1"));
Browser.Equal("Second attribute", () => linkElement.GetAttribute("attr2"));
// Update the first parameter of the component
linkAttributeInput1.Clear();
linkAttributeInput1.SendKeys("hello\n");
linkElement = FindLinkElement();
// Validate first attribute updated
Browser.Equal("hello", () => linkElement.GetAttribute("attr1"));
Browser.Equal("Second attribute", () => linkElement.GetAttribute("attr2"));
// Update the second parameter of the component
linkAttributeInput2.Clear();
linkAttributeInput2.SendKeys("world\n");
linkElement = FindLinkElement();
// Validate second attribute updated
Browser.Equal("hello", () => linkElement.GetAttribute("attr1"));
Browser.Equal("world", () => linkElement.GetAttribute("attr2"));
IWebElement FindLinkElement() => Browser.FindElements(By.Id("link-with-bindings")).Single();
}
}
}

View File

@ -111,6 +111,21 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.True(renderedElement.Displayed);
}
[Fact]
public void ThrowsErrorForUnavailableAssemblies()
{
// Navigate to a page with lazy loaded assemblies for the first time
SetUrlViaPushState("/Other");
var app = Browser.MountTestComponent<TestRouterWithLazyAssembly>();
// Should've thrown an error for unhandled error
var errorUiElem = Browser.Exists(By.Id("blazor-error-ui"), TimeSpan.FromSeconds(10));
Assert.NotNull(errorUiElem);
AssertLogContainsCriticalMessages("DoesNotExist.dll must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.");
}
private string SetUrlViaPushState(string relativeUri)
{
var pathBaseWithoutHash = ServerPathBase.Split('#')[0];
@ -145,5 +160,18 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
});
}
}
void AssertLogContainsCriticalMessages(params string[] messages)
{
var log = Browser.Manage().Logs.GetLog(LogType.Browser);
foreach (var message in messages)
{
Assert.Contains(log, entry =>
{
return entry.Level == LogLevel.Severe
&& entry.Message.Contains(message);
});
}
}
}
}

View File

@ -57,6 +57,7 @@
<option value="BasicTestApp.LoggingComponent">Logging</option>
<option value="BasicTestApp.LongRunningInterop">Long running interop</option>
<option value="BasicTestApp.MarkupBlockComponent">Markup blocks</option>
<option value="BasicTestApp.ModifyHeadComponent">Modify head</option>
<option value="BasicTestApp.MouseEventComponent">Mouse events</option>
<option value="BasicTestApp.MovingCheckboxesComponent">Moving checkboxes diff case</option>
<option value="BasicTestApp.MultipleChildContent">Multiple child content</option>

View File

@ -0,0 +1,119 @@
@using Microsoft.AspNetCore.Components.Web.Extensions.Head
<p>
Multiple title elements:<br />
@for (int i = 0; i < 3; i++)
{
var titleId = i;
<button id="button-title-@i" @onclick="() => SetSelectedTitle(titleId)">
Title @titleId
</button>
if (selectedTitle == titleId)
{
<Title Value="@($"Title {titleId}")" />
}
}
</p>
<p>
Multiple meta elements:<br />
@for (int i = 0; i < metas.Length; i++)
{
var metaId = i;
<button id="button-meta-@i" @onclick="() => Toggle(metas, metaId)">
@GetToggleString(metas[metaId]) meta @metaId</button>
if (metas[metaId])
{
<Meta id="@($"Meta {metaId}")" />
}
}
</p>
<p>
Multiple link elements:<br />
@for (int i = 0; i < links.Length; i++)
{
var linkId = i;
<button id="button-link-@i" @onclick="() => Toggle(links, linkId)">
@GetToggleString(links[linkId]) link @linkId</button>
if (links[linkId])
{
<Link id="@($"Link {linkId}")" />
}
}
</p>
<p>
Nested title elements:<br />
<button id="button-title-nested" @onclick="() => SetSelectedTitle(3)">
Nested titles
</button>
@if (selectedTitle == 3)
{
<div>
<Title Value="Layer 1" />
<div>
<Title Value="Layer 2" />
<div>
<Title Value="Layer 3" />
<div>
<Title Value="Layer 4" />
</div>
</div>
</div>
</div>
}
</p>
<p>
Meta elements w/ bindings:<br />
<input id="meta-attr-input-1" @bind="@metaAttribute1" placeholder="Attribute 1" /><br />
<input id="meta-attr-input-2" @bind="@metaAttribute2" placeholder="Attribute 2" /><br />
<Meta id="meta-with-bindings" attr1="@metaAttribute1" attr2="@metaAttribute2" />
</p>
<p>
Link elements w/ bindings:<br />
<input id="link-attr-input-1" @bind="@linkAttribute1" placeholder="Attribute 1" /><br />
<input id="link-attr-input-2" @bind="@linkAttribute2" placeholder="Attribute 2" /><br />
<Link id="link-with-bindings" attr1="@linkAttribute1" attr2="@linkAttribute2" />
</p>
@code {
private readonly bool[] metas = Enumerable.Repeat(false, 3).ToArray();
private readonly bool[] links = Enumerable.Repeat(false, 3).ToArray();
private int selectedTitle = -1;
private string metaAttribute1 = "First attribute";
private string metaAttribute2 = "Second attribute";
private string linkAttribute1 = "First attribute";
private string linkAttribute2 = "Second attribute";
private void Toggle(bool[] states, int index)
{
states[index] = !states[index];
StateHasChanged();
}
private void SetSelectedTitle(int title)
{
selectedTitle = title;
StateHasChanged();
}
private string GetToggleString(bool b)
=> b ? "Disable" : "Enable";
}

View File

@ -0,0 +1,29 @@
@page "/prerendered-head"
@using Microsoft.AspNetCore.Components.Web.Extensions.Head
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<p>
This component demonstrates that head components (i.e. Title, Meta, etc.) can take effect during prerendering
and become updatable when the circuit connects.
</p>
<p>
Title:<br />
<input id="title-input" @bind="title" placeholder="Set the title" />
<Title Value=@title />
</p>
<p>
Meta:<br />
<input id="input-meta-binding" @bind="metaContent" placeholder="Set the meta content" />
<Meta id="meta-with-bindings" content="@metaContent" />
</p>
<Meta id="meta-no-bindings" content="Immutable meta content" />
@code {
private string title = "Initial title";
private string metaContent = "Initial meta content";
}

View File

@ -1,6 +1,6 @@
@using Microsoft.AspNetCore.Components.Routing
@using System.Reflection
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@inject LazyAssemblyLoader lazyLoader
@ -31,25 +31,24 @@
private async Task LoadAssemblies(string uri)
{
try
if (uri.EndsWith("WithLazyAssembly"))
{
if (uri.EndsWith("WithLazyAssembly"))
{
Console.WriteLine($"Loading assemblies for WithLazyAssembly...");
var assemblies = await lazyLoader.LoadAssembliesAsync(new List<string>() { "Newtonsoft.Json.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
if (uri.EndsWith("WithLazyLoadedRoutes"))
{
Console.WriteLine($"Loading assemblies for WithLazyLoadedRoutes...");
var assemblies = await lazyLoader.LoadAssembliesAsync(new List<string>() { "LazyTestContentPackage.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
Console.WriteLine($"Loading assemblies for WithLazyAssembly...");
var assemblies = await lazyLoader.LoadAssembliesAsync(new List<string>() { "Newtonsoft.Json.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
catch (Exception e)
if (uri.EndsWith("WithLazyLoadedRoutes"))
{
Console.WriteLine($"Error when loading assemblies: {e}");
Console.WriteLine($"Loading assemblies for WithLazyLoadedRoutes...");
var assemblies = await lazyLoader.LoadAssembliesAsync(new List<string>() { "LazyTestContentPackage.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
if (uri.EndsWith("Other")) {
Console.WriteLine($"Loading assemblies for Other...");
var assemblies = await lazyLoader.LoadAssembliesAsync(new List<string>() { "DoesNotExist.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
}

View File

@ -43,6 +43,8 @@
</script>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/Microsoft.AspNetCore.Components.Web.Extensions/headManager.js"></script>
<!-- Used by ExternalContentPackage -->
<script src="_content/TestContentPackage/prompt.js"></script>
</body>

View File

@ -19,6 +19,9 @@
<button id="load-boot-script" onclick="start()">Load boot script</button>
<script src="_framework/blazor.server.js" autostart="false"></script>
<script src="_content/Microsoft.AspNetCore.Components.Web.Extensions/headManager.js"></script>
<script>
// Used by InteropOnInitializationComponent
function setElementValue(element, newValue) {

View File

@ -39,6 +39,8 @@
<script src="_framework/blazor.server.js"></script>
<script src="_content/Microsoft.AspNetCore.Components.Web.Extensions/headManager.js"></script>
<!-- Used by ExternalContentPackage -->
<script src="_content/TestContentPackage/prompt.js"></script>
<script>

View File

@ -5,7 +5,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Components.WebAssembly.Services;
using Microsoft.JSInterop;
namespace TestServer
{