Posted at

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

More than 1 year has passed since last update.

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

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

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

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

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


普通にやるとどうなるの?

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

<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です。

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


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要素のコレクション(配列みたいなもの)なんですね。ここもちょっとハマったので気をつけて下さい。