Blazor で、タイトルや OGP 用メタ要素を設定する


# document.title を設定したい!

Blazor アプリを開発してて、ページ (ドキュメント) のタイトル (ブラウザのタブに表示されるテキストですね) を、コンポーネントごとに設定したいことってありませんか?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/5b590f37-b597-a893-af58-2fdde152afb2.png)

残念ながら、Blazor 上の標準的な C# コードだけでは、これは実現できないはず、です。

というのも、Blazor というかそのコンポーネントのレンダリング対象は、`<app>` 要素の内部の DOM に限られているためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/9cfdec20-f210-ebc3-8201-8e020c7b868a.png)

なので、`<app>` 要素の外側というか親側というか、とにかく `<head>` 要素は普通には Blazor で扱えません。

ではどうするかといえば、Blazor 上の C# コードから JavaScript 相互運用機能を利用して、JavaScript に頼る必要があります。

```javascript
document.title = "ここにC#コードから渡された値が来るように実装する(詳細略)"
```

とはいえ、自前でこれを実装するのは、不可能ではないにせよ、面倒な仕事です。

ということで、内部的には上記の仕組みでタイトルを変更することのできる Blazor コンポーネント "Blazor Head Element Helper" を、NuGet パッケージとして公開しております!

- ["NuGet Gallery | Blazor Head Element Helper"](https://www.nuget.org/packages/Toolbelt.Blazor.HeadElement/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/11e8ef32-780a-1fd1-382d-dc3ce30ec661.png)

# "Blazor Head Element Helper" の使い方

まずは  "Blazor Head Element Helper" NuGet パッケージ (パッケージ ID は "Toolbelt.Blazor.HeadElement") をプロジェクトに追加します。

```shell
> dotnet add package Toolbelt.Blazor.HeadElement --version 1.0.0
```

次に、対象の Blazor アプリの `Startup.ConfigureServices()` にて、"Head Element Helper" のサービスを DI 機構に登録します。

```csharp
using Toolbelt.Blazor.Extensions.DependencyInjection; // <- この行を追加し...

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddHeadElementHelper(); // <- この行を追加する。
    ...
```

ここまで準備できたらいよいよコンポーネント側のコーディングに移るのですが、その前に、`_Import.razor` ファイルで、"Toolbelt.Blazor.HeadElement" 名前空間を既定で開いておくとよいです。

```csharp
@* This is "_Imports.razor" *@
...
@using Toolbelt.Blazor.HeadElement
```

こうしておけば、見事、いずれのコンポーネントでも、下記のように `<Title>` コンポーネントを置いておくだけで、そのコンポーネントがレンダリングされるときは、そのページのタイトルが設定されるようになります!


```html
@* This is "Pages/Counter.razor" *@
@page "/counter"

<Title>Counter (@currentCount)</Title>
```
![movie-001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/b6e9e80e-bdb5-c06d-45f4-1ab212444764.gif)

さらにおまけで、タイトルだけでなく、`<head>` 要素内の `<meta>` 要素も、設定可能です!

```html
<Meta Property="ogp:title" Content="Counter" />
```

これで OGP 対応もバッチリですね!  
...?

# サーバー側事前レンダリングは...?

ちょっと待って下さい。

各種検索クローラや SNS サーバーからは、結局、最初の HTTP GET 要求でサーバーから返されたコンテンツしか見えません。

ですので冒頭で説明したとおり、JavaScript で動的にタイトルやメタ要素を変更しているだけなら、まったく OGP 対応になってませんね!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/a5e5b0a9-dc2d-716c-9108-cafdc940c5eb.png)


しかしご安心を。

サーバー側 (クライアントサイド Blazor の場合は、ASP.NET Core ホスティングが必要) に "Toolbelt.Blazor.HeadElement.ServerPrerendering" NuGet パッケージを追加してみて下さい。


```shell
> dotnet add package Toolbelt.Blazor.HeadElement.ServerPrerendering
```


その上で、サーバー側 `Startup.Configure()` メソッド内にて、`app.UseStaticFiles()` より前の行に、"Head Element Server Prerendering" ミドルウェアの登録行を追加します。

```csharp
using Toolbelt.Blazor.Extensions.DependencyInjection; // <- この行を追加し...

public class Startup
{
  public void Configure(IApplicationBuilder app)
  {
    app.UseHeadElementServerPrerendering(); // <- この行を "app.UseStaticFile" より前に追加。
    ...
    app.UseStaticFiles()
    ...
```

すると、あら不思議!

HTTP GET 要求で返される HTML コンテンツ内の `<title>` 要素や `<meta>` 要素群が、.razor ファイル内で `<Title>` コンポーネントや `<Meta>` コンポーネントで実装したとおりの内容になっている
はずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/866a70cc-1712-121a-e5f2-933644e5359e.png)


よかったよかった。

# デモンストレーションサイト

下記に、"Blazor Head Element Helper" とそのサーバー側事前レンダリング機能を利用したデモンストレーションサイトを公開してます。

- **Live Demo** - https://demo-blazor-headelement.azurewebsites.net/

実際にこのデモンストレーションサイトに掲載している各観光ポイントページの URL を Twiiter にツイートしたりしてみると、ちゃんと Twitter カード形式で表示されるはず、です (下記)。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">[This is a demonstration of &quot;Blazor Head Element Helper&quot;] Museum of Pop Culture - The Museum of Pop Culture, or MoPOP (previously called EMP Museum) is a nonprofit museum dedicated to contemporary popular culture. <a href="https://t.co/08i4OwuOqS">https://t.co/08i4OwuOqS</a> <a href="https://twitter.com/hashtag/blazor?src=hash&amp;ref_src=twsrc%5Etfw">#blazor</a></p>&mdash; @jsakamoto (@jsakamoto) <a href="https://twitter.com/jsakamoto/status/1190280814170267648?ref_src=twsrc%5Etfw">November 1, 2019</a></blockquote>

このデモンストレーションサイトのソースコードは下記で閲覧可能です。

- https://github.com/jsakamoto/Toolbelt.Blazor.HeadElement/tree/master/_SampleSites/ServerSideApp

"Blazor Head Element Helper" は、サーバーサイド Blazor (Blazor Server App)、クライアントサイド Blazor (Blazor WebAssembly App) のいずれのホスティングモデルでも動作します。

これで Blazor アプリでも、OGP 対応できますね!

Happy Coding! :)