Angularのコンポーネントにツイートボタンを載せる

今時ホームページにツイートボタン載せるだけでハマることなんてあります？？？？？？(執筆日2018年4月23日)

だって、 https://publish.twitter.com/ にアクセスして、生成したコードを自分のページに貼り付けるだけじゃないですか！！！！！

……って思ってたんですけどね。

ハマるんですよ、それが。そう、Angularならね。

ちなみにAngularのバージョンは5.2.9です。

#普通にやるとどうなるの？

上記のサイトで生成したコードがこちら。

```html
<a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-size="large" data-text="シェアしたいテキスト！" data-url="http://example.com" data-hashtags="hogehoge" data-show-count="false">Tweet</a>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
```

タグが2つ並んでます。

属性がいろいろついた`a`タグと、外部JSを実行する`script`タグです。

仕組みとしては、上から順にページを読み込んでいく時に、

 - まず`a`タグが読み込まれて、
 - 次に`script`タグが読み込まれて`widgets.js`が実行され、
 - 先に読み込まれていた`a`タグがツイートボタンに書き換えられる、

という感じです。

だから、要するに

 - `a`タグを読み込み
 - `widgets.js`を実行する

の2点さえやればいいんですが

Angularのコンポーネントにこのコードをただ記述すると、これがうまくいかないんですね。

なんと2点とも、うまくいきません。

それぞれ説明します。

#問題1 `script`タグが読み込まれない

コンポーネントに`script`タグが含まれていると、そのまま出力してくれないみたいです。

開発サーバーで動かしてブラウザで確認すると、`script`タグがまるまるなくなってました。

これではいけませんね。`a`タグを書き換えてボタンにする作業が行われなくなってしまいます。

##解決策1

参考にした質疑
https://teratail.com/questions/70842

ngAfterViewInitについて
https://angular.io/guide/lifecycle-hooks#afterview

要素の追加について
https://qiita.com/kouh/items/dfc14d25ccb4e50afe89

これらを参考にしました。

つまり、`a`タグは普通に書いておいて、後から動的に`script`タグを追加して、追加した瞬間即実行してもらうことで、`a`タグを書き換えるというものです。

この`script`タグの追加は、`a`タグを書いた後で行われる必要があるので、

Angularコンポーネントのライフサイクルフックのうち`AfterViewInit`で実行させましょう。

これでOKですね…………？？？？？


はい。これだけでは、だめなんですね。

#問題2 `a`タグの中身が書き換わる


同じ状況の人(上と同じ人)
https://teratail.com/questions/71021

こちらの質問と同じ状況です。(回答が付いてなかったので、解決策は自力で考えました。)

Angularのコンポーネントに記述したタグは、そのタグに付与している属性によっては書き換えが行われるんですね。`*ngFor`とかありますからね。そりゃそうなんですけど

どうも、このツイートボタンの`a`タグに付与する属性の名前とバッティングしてるのか

`a`タグが書き換わっちゃうんですよね。

`<a data-text="hoge"></a>`が`<a>hoge</a>`に変わっちゃう、みたいなことが起こります。

この状態では、後から`script`タグを追加して`widget.js`を実行しても、元が書き換わってるので、ツイートボタンへの変身がうまくいきません。

##解決策2

ということで

だったら、その`a`タグ自体も、あとから動的に追加してやればいいですね。

言われてみればなるほどという感じ。

#最終的な解決策

最終的な解決策は

「ツイートボタン関連のコードを、全部あとから動的に追加する」ということになります。

テンプレートの`html`ファイルに直接書くのではなく、ということです。

使うのはコンポーネントのライフサイクルフックのうち`ngAfterViewInit`です。

ということで、コードはこんな感じ。

```typescript:src/app/fuga/fuga.component.ts
 ngAfterViewInit(){

    var element = document.createElement('a');//aタグを作ります
    element.setAttribute('href',"https://twitter.com/share?ref_src=twsrc%5Etfw");
    element.setAttribute('class',"twitter-share-button");
    element.setAttribute('data-size',"large");
    element.setAttribute('data-text',"シェアしたいテキスト！");
    element.setAttribute('data-url',"http://example.com");
    element.setAttribute('data-hashtags',"hogehoge");
    element.setAttribute('data-show-count',"false");

    var script = document.createElement('script');//scriptタグを作ります
    script.async = true;
    script.setAttribute('src',"https://platform.twitter.com/widgets.js");
    script.setAttribute('charset','utf-8');

    //aタグ、scriptタグの順で設置します
    var div = document.getElementById("foobaa");//ボタンを置きたい場所の手前の要素を取得
    div.parentNode.insertBefore(element,div.nextSibling);//ボタンを置きたい場所にaタグを追加
    div.parentNode.insertBefore(script,div.nextSibling);//scriptタグを追加してJSを実行し、aタグをボタンに変身させる
  }
```


本来ただコードを貼り付けるだけでいいのに、随分面倒になりましたね。もしもっと簡単になる方法があれば教えて下さい。directiveとかをうまく使えるのかなあ？

あとあんまり関係ないですが、最後にボタンを置きたい場所周辺の要素を取得する時に、`document.getElementsByClass'を使ったら、返ってくるのはDOM要素のコレクション(配列みたいなもの)なんですね。ここもちょっとハマったので気をつけて下さい。