Angularで、scriptタグを普通にHTMLに書いても、描画時にscriptタグの部分が消えてしまって処理が実行されません。
それでもscriptタグを埋め込みたい場合の方法を解説します。
Angular6でも多分大丈夫な方法です。
今回は、Stripeのタグを例に解説します。
stripe-angularっていうパッケージがあるけど、これは最近のバージョンだと使えない。
基本方針
・Stripe用のパッケージやモジュールには頼らない。良いのが無いので。
・AfterViewInitを用いて、再描画されるたびにDOM操作でStripeのタグを生成し、Javascriptを実行する。
・完成時はこんな感じ。ngForで商品の数だけStripeのボタンを生成する。

購入ボタンのところがStripeのタグです。
今作ってるのをキャプってきたけど、これより下に書くコードは、これよりもっと簡素なものです。
必要なモジュール
import { Component, OnInit, AfterViewInit, ViewChildren, QueryList } from '@angular/core';
コンポーネントのHTML側
<div #productsElement class="product" *ngFor="let product of this.products">
<h3>{{ product['product_name'] }}</h3>
<p>{{ product['description'] }}</p>
<p>¥{{product['price']}}-</p>
<div>
<form class="stripe-form" action="[POST先URL]" method="POST" id="stripe_{{product['product_id']}}"
[attr.amount]="product['price']" // 商品によって変わる、動的な値はここで指定
[attr.name]="product['product_name']" // 商品によって変わる、動的な値はここで指定
[attr.product_id]="product['product_id']" // 商品によって変わる、動的な値はここで指定
enctype="application/x-www-form-urlencoded">
</form>
</div>
</div>
tsファイル側
import { Component, OnInit, AfterViewInit, ViewChildren, QueryList } from '@angular/core';
import { ProductService } from '../domain/product/product.service';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit, AfterViewInit {
@ViewChildren('productsElement') // #productsElementを付与したngFor要素を取得
productsElement: QueryList<any>;
private productElement: HTMLElement;
public products = [];
constructor(public productService: ProductService) { }
ngOnInit() {
// 商品一覧情報を取得、私の場合は商品情報を取得するサービスクラスを作って使ってます。
// サービスクラスの実装は割愛します。そこで躓いてるわけじゃないと思うので。
this.products = [];
this.productService.getProducts().subscribe(response => {
const responseJson = response.json();
// 私の実装では、APIサーバーから取得しているので、レスポンスコードを確認しています。
// 人によってこのあたりの実装は違うと思いますので、ここは参考にし過ぎないでください。
if (responseJson['statusCode'] === 200) {
this.products = JSON.parse(responseJson['body']);
console.log(this.products[0]['product_name']);
console.log(this.products[0]);
}
});
}
// 描画後の処理を定義
ngAfterViewInit() {
// productsElementに変更があるたびに実行する処理を定義
this.productsElement.changes
.subscribe(element => {
console.log('element');
// ngForによって生成された、全ての要素について、ループで処理します。
for (const e of element.toArray()) {
console.log(e);
this.productElement = e.nativeElement;
// formタグに付与した、商品情報の属性の値を取得して、商品ごとのstripeタグを生成します。
const stripeForm = this.productElement.querySelectorAll('.stripe-form').item(0);
const productId = stripeForm.getAttribute('product_id');
const name = stripeForm.getAttribute('name');
const amount = stripeForm.getAttribute('amount');
// afterbeginは、要素内部の、最初の子要素の前に挿入するという意味。詳しく知りたければぐぐってください。
stripeForm.insertAdjacentElement('afterbegin', this.createStripeButton(productId, name, amount));
}
});
}
// Stripeのタグを生成
private createStripeButton(product_id: string, name: string, amount: string): HTMLScriptElement {
const script = document.createElement('script'); // scriptタグを作ります
// stripeタグに必要な属性をsetAttributeで付与する。
script.setAttribute('src', 'https://checkout.stripe.com/checkout.js');
script.setAttribute('class', 'stripe-button');
script.setAttribute('data-key', '[StripeのAPIキー]');
script.setAttribute('data-amount', amount);
script.setAttribute('data-name', name);
script.setAttribute('data-description', product_id);
script.setAttribute('data-image', 'https://stripe.com/img/documentation/checkout/marketplace.png');
script.setAttribute('data-locale', 'ja');
script.setAttribute('data-currency', 'jpy');
script.setAttribute('data-label', '購入する');
script.setAttribute('data-panel-label', '購入する');
script.setAttribute('data-zip-code', 'true');
script.setAttribute('data-billing-address', 'true');
return script;
}
}こんな感じ
重要なところ
・AfterViewInitを使って、再描画に対応している
・ViewChildrenを使って、changes.subscribeで、要素の内容変更時の処理を定義している
・HTMLScriptElement でScriptタグを生成している
・querySelectorAll で要素を検索している
・getAttributeやinsertAdjacentElementで要素を参照したりDOMの操作をしているところ
上記の概念が知識としてあれば、この記事は必要ないですね。