ASP.NETでTypeScriptとTagHelperを組み合わせて簡単なコンポーネントを作成する

#概要
この記事では前回に引き続きサイトを改良していきます。
サイト
http://golf.higty.com

ソースコード
https://github.com/higty/WebSite

[■VisualStudioでC#＋ASP.NETでWEBサイトを作ってGitで管理する](https://qiita.com/higty/items/14aa8074b03b4cbf6da4)
[■WEBアプリをAzureへデプロイして公開し独自ドメインを設定する](https://qiita.com/higty/items/995597cf055cf8e2f951)
[■ASP.NETで共通のHTMLを部品化する方法＆SCSSファイルの導入方法](https://qiita.com/higty/items/d2deea4430966ae95d8d)

これらの記事はプログラミング初心者向けにWEBサイトを作る方法を紹介します。

- 学生（中学生、高校生、大学生）でWEBサイト作ってみたい
- ITコンサルやっててプログラムを触って軽く理解を深めてみたい
- 建設業界だけどIoTとかあるしITの勉強をしたい
- WEB広告やってるけどフロントだけじゃなくてもっと深く理解したい
- Macは持ってなくてWindowsを使っている

みたいな感じの人対象です。

前回までの記事で動画をiframeで埋め込み表示する形にしましたが、iframeが1つの画面にたくさんあるとページの表示完了まで時間がかかってしまいます。表示が速くなるように改良していきます。

#コンポーネントの設計
既存のiframeの部分を

```html
<div class="video-panel">
    <iframe type="text/html" frameborder="0"
src="//www.youtube.com/embed/LeOI3L3Af3o?start=449&end=468&rel=0"></iframe>
</div>
```
こんな感じで置き換えます。

```html
<div class="video-image-panel" url="//www.youtube.com/embed/LeOI3L3Af3o?start=449&end=468&rel=0&autoplay=1" video-image-panel="true">
    <img src="/Image/Hole7_Video_Tiger1.png">
</div>
```
画像を表示してクリックしたら別ウィンドウで動画を再生するように改良します。クリックしたときの動作はTypeScriptファイルに記述します。


#TagHelperの概要
TagHelperはHTMLを部品として使いまわしができるようにするための仕組みです。
上記のこのHTMLをTagHelperで部品化します。

```html
<div class="video-image-panel" url="//www.youtube.com/embed/LeOI3L3Af3o?start=449&end=468&rel=0&autoplay=1" video-image-panel="true">
    <img src="/Image/Hole7_Video_Tiger1.png">
</div>
```

HTMLをよく見てみましょう。個別に設定したいのは

- VideoID
- Start
- End
- ImageUrl
- 
の4つです。タグヘルパーを定義することで

```html
<video-image-panel video-id="LeOI3L3Af3o" start="449" end="468" image-url="/Image/Hole7_Video_Tiger1.png"></video-image-panel>
```
といった感じに書けるようになります。

TagHelperを定義していきます。C#のクラスとしてMicrosoft.AspNetCore.Razor.TagHelpers.TagHelperクラスを継承すれば独自のカスタムタグヘルパーが作成できます。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace WebSite.Core
{
    [HtmlTargetElement("video-image-panel")]
    public class VideoImagePanelTagHelper : TagHelper
    {
        public String VideoID { get; set; }
        public Int32 Start { get; set; }
        public Int32 End { get; set; }
        public String ImageUrl { get; set; }

        public VideoImagePanelTagHelper()
        {
        }
        public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "div";
            output.Attributes.Add("class", "video-image-panel");
            output.Attributes.Add("video-image-panel", "true");
            output.Attributes.Add("url", CreateVideoUrl(this.VideoID, this.Start, this.End));
            {
                var img = CreateImage(this.ImageUrl);
                output.Content.AppendHtml(img);
            }
            await base.ProcessAsync(context, output);
        }
        public static String CreateVideoUrl(String id, Int32 start, Int32 end)
        {
            var url = String.Format("//www.youtube.com/embed/{0}?start={1}&end={2}&rel=0&autoplay=1"
                , id, start, end);
            return url;
        }
        private TagBuilder CreateImage(String url)
        {
            var img = new TagBuilder("img");
            img.Attributes.Add("src", url);
            return img;
        }
    }
}
```
Viewsフォルダに_ViewImports.cshtmlファイルを追加します。

```html
@using WebSite.Core;
@addTagHelper "*, WebSite_Golf"
```
WebSite_Golfはアセンブリ名です。名前空間の名前ではないので注意してください。*で全てのTagHelperを追加することができます。

これで.cshtmlファイル内で<video-image-panel...という感じで書けるようになります。インテリセンスも効くので非常に便利です。
![VS_TagHelper_Intellisense.png](https://qiita-image-store.s3.amazonaws.com/0/96483/fc97b5db-9923-8202-657e-e6186ef86e75.png)

HTMLの属性とC#のプロパティ名はkebab-caseとPascalCaseにそれぞれ対応します。
video-id → VideoID 
image-url → ImageUrl
といった具合にマッピングされます。.cshtmlファイルの中に

```html
<video-image-panel video-id="5bBcS5fc5FY" start="2715" end="2730" image-url="/Image/Hole2_Video_RolyMcllroy1.png"></video-image-panel>
```

といった感じで
video-id=LeOI3L3Af3o
start=449
end=468
image-url=/Image/Hole7_Video_Tiger1.png
とセットすると対応するC#のプロパティがセットされてProcessAsyncメソッドが実行され

```html
<div class="video-image-panel" url="//www.youtube.com/embed/LeOI3L3Af3o?start=449&end=468&rel=0&autoplay=1" video-image-panel="true">
    <img src="/Image/Hole7_Video_Tiger1.png">
</div>
```
というHTMLが生成されます。

生成されたHTMLにvideo-image-panel="true"という属性があることに注目してください。次のチャプターではこの属性がついている要素にTypeScriptでイベントを割り当てていきます。

#TypeScriptの概要
TypeScriptはJavaScriptの欠点を補う言語です。JavaScriptは型が合ってなくても柔軟に動作してしまうのですが、TypeScriptはより堅牢な型チェックをしてくれるので意図しないプログラムの動作を防ぐことができます。

JavaScriptフォルダを作成しファイルを追加します。
![VS_JavaScript_Folder.png](https://qiita-image-store.s3.amazonaws.com/0/96483/bc049425-cf80-f72c-27b9-5e808fdfd129.png)

VideoImagePanel.tsはこんな感じです。

```typescript
/// <reference path="jquery/jquery.d.ts" />
module UI {

    export class VideoImagePanel {
        public Initialize() {
            $(document).on("click", "[video-image-panel]", this.VideoImagePanel_Click.bind(this));
        }
        private VideoImagePanel_Click(e: JQueryEventObject) {
            var div = $(e.currentTarget);
            var url = div.attr("url");
            window.open(url);
            //div.append("<iframe type=\"text/html\" frameborder=\"0\" src=\"" + div.attr("url") + "\">");
        }
    }

    $(function () {
        var panel = new UI.VideoImagePanel();
        panel.Initialize();
    });
}
```
1行目に/// &lt;reference path="jquery/jquery.d.ts" />を記述するとjQueryのインテリセンスが効くようになります。

jQueryの機能で

```typescript
    $(function () {
        //...ページのロードが完了したら実行
    });
```
というのを使用して

```typescript
$(function () {
    var panel = new UI.VideoImagePanel();
    panel.Initialize();
});

```

ページのロードが完了したらページの中でvideo-image-panelという属性を持っている要素に対してクリックされた時のイベントを割り当てます。

```typescript
public Initialize() {
    $(document).on("click", "[video-image-panel]", this.VideoImagePanel_Click.bind(this));
}
```
クリックされるとVideoImagePanel_Clickメソッドが実行されます。

```typescript
private VideoImagePanel_Click(e: JQueryEventObject) {
    var div = $(e.currentTarget);
    var url = div.attr("url");
    window.open(url);
}
```
クリックすると別ウィンドウで動画ページが開き、動画の再生が自動で始まります。


全てのページでこのTypeScriptから生成されるVideoImagePanel.jsへの参照を追加します。_Layout.cshtmlを以下のように修正します。

```html
...（省略）...
<body>
    ...（省略）...
    <script src="~/JavaScript/jquery/jquery.js"></script>
    <script src="~/JavaScript/VideoImagePanel.js"></script>
</body>
</html>
```

#まとめ
今回の改良で少しページの表示が速くなりました。TagHelperを使用すると部品化が容易にできます。積極的に使用していきましょう。またTypeScriptはより強い型チェックをしてくれるのでプログラムの間違いに気づきやすくなります。こちらは必ず使用するくらいでも良いと思います。

VideoImagePanelTagHelperをHTMLに配置し、TypeScriptファイルを読み込むと画像をクリックしたら動画の再生が別ウィンドウで始まるコンポーネントを作ることができました。応用すると様々な機能をコンポーネントとして提供しそれを使いまわすことができるようになります。

あと今回は3番ホールの解説が追加されています。また次回以降でサイトを改良していきます。フォローしてね。

ソースコード
https://github.com/higty/WebSite

以前の記事はこちら↓
[■VisualStudioでC#＋ASP.NETでWEBサイトを作ってGitで管理する](https://qiita.com/higty/items/14aa8074b03b4cbf6da4)
[■WEBアプリをAzureへデプロイして公開し独自ドメインを設定する](https://qiita.com/higty/items/995597cf055cf8e2f951)
[■ASP.NETで共通のHTMLを部品化する方法＆SCSSファイルの導入方法](https://qiita.com/higty/items/d2deea4430966ae95d8d)