Skip to content

Influence HTML head from Blazor #23833

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

Merged
merged 21 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -6739,6 +6753,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
Expand Down Expand Up @@ -7502,6 +7588,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}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
67 changes: 67 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/HeadTagBase.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
14 changes: 14 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/Link.cs
Original file line number Diff line number Diff line change
@@ -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";
}
}
14 changes: 14 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/Meta.cs
Original file line number Diff line number Diff line change
@@ -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";
}
}
22 changes: 22 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/TagElement.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
37 changes: 37 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/Title.cs
Original file line number Diff line number Diff line change
@@ -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)}-->");
}
}
}
17 changes: 17 additions & 0 deletions src/Components/Web.Extensions/src/HeadManagement/TitleElement.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
Expand All @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.JSInterop" />
</ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/Components/Web.Extensions/src/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Web.Extensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Loading