質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

Q&A

0回答

1290閲覧

モーダルでno DOM elements that are currently on the page. Make sure the element exists on the page before

mikeko0901

総合スコア227

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

0グッド

0クリップ

投稿2021/09/13 14:50

Vue.jsと決済APIのStripeを用いて決済処理を実装しています。
Stripeのカード用フォームを、通常のページに表示させると問題ないのですが、
モーダルに出そうとすると、カードNoを入力するところが表示されません。
以下のようなエラーもでます。

■正常:カードNoが入力できる
イメージ説明

■問題:モーダルにするとカードNoが入力できない
イメージ説明

▼その時出ているエラー

IntegrationError: The selector you specified (#card-element) applies to no DOM elements that are currently on the page. Make sure the element exists on the page before calling mount().

イメージ説明

###以下コードです
■ShopOrderCheck.vue(モーダルの呼び出し元のVue)

<template> <div> <main class="page-form"> <h1 class="page-heading">ご注文ページ</h1> <div class="bg-secondary-lighten-1 py-10"> <div class="container"> <div class="grouping shadow-md mb-6 md:w-10/12 max-w-screen-sm mx-auto"> <form class="form form--check" action="../epsilon/order_ep.php" method="POST" id="order_form"> <div class="form__group"> <label class="form__label">メールアドレス</label> <div> <div>{{ request.buy_email }}<p style="color:#D31004;" class="text-sm">※正しいメールアドレスかをご確認ください。</p></div> </div> </div> </form> <div class="mt-8" v-if="request.payment_method == 1"> <button type="button" class="form__button w-full button " id="order_card" @click="show">クレジットカード</button> </div> <div class="mt-8" v-else> <button type="button" class="form__button w-full button" id="order_bank">銀行振込</button> </div> <div class="mt-4 ml-2 font-bold" style="color: #285399;"> <a :href="getBackLink" class="back-link">≪ 戻る</a> </div> </div> </div> </div> </main> <stripe-card :product="product" :request="request" :public_key="public_key" :csrf = "csrf" ></stripe-card> </div> </template> <script> export default { props: { csrf: { type: String, }, product: { type: Object, }, request: { type: Object }, public_key: { type: Object, } }, data() { return { showContent: false, errors: [], } }, computed: { getBackLink: function() { let back_url = "/shop/order?id=" + this.product.id; return back_url; } }, methods: { openModal : function() { this.showContent = true; }, closeModal () { this.showContent = false }, show : function() { this.$modal.show('stripe-card'); }, hide : function () { this.$modal.hide('stripe-card'); }, regist: function() { [まだ未実装] }, }, } </script>

■モーダルコンポーネント StripeCard.vue

<template> <modal name="stripe-card"> <div> <input type="hidden" name="_token" v-bind:value="this.csrf"> <label for="exampleInputEmail1">お名前</label> <input type="test" class="form-control col-sm-5" id="card-holder-name" required v-model="Form.cardHolderName"> <label for="exampleInputPassword1">カード番号</label> <div class="form-group MyCardElement col-sm-5" id="card-element"></div> <div id="card-errors" role="alert" style='color:red'></div> <button class="btn btn-primary" id="card-button" >送信する</button> </div> </modal> </template> <script> import {loadStripe} from '@stripe/stripe-js'; export default { props: { product: { type: Object, }, request: { type: Object, }, public_key: { type: Object, }, csrf: { type: String, } }, data() { return { stripe: null, stripeCard: null, Form: { cardHolderName: null, }, } }, async mounted() { this.stripe = await loadStripe(this.public_key.value); const elements = this.stripe.elements(); var style = { base: { color: "#32325d", fontFamily: '"Helvetica Neue", Helvetica, sans-serif', fontSmoothing: "antialiased", fontSize: "16px", "::placeholder": { color: "#aab7c4" } }, invalid: { color: "#fa755a", iconColor: "#fa755a" } }; this.stripeCard = elements.create('card', {style: style, hidePostalCode: true}); this.stripeCard.mount('#card-element'); this.stripeCard.on('change', function(event) { var displayError = document.getElementById('card-errors'); if (event.error) { displayError.textContent = event.error.message; } else { displayError.textContent = ''; } }); }, methods: { } } </script>

mountが呼び出されている時にDOMがないというエラーぽく、
モーダルでカードのフォームを表示させず、ShopOrderCheck.vueの中に普通に表示させるとこの問題は起きませんでした。

ググってみましたら、こちらのページが回答に近いのかなと思いましたが、
Vue SPA Single File Component Element binding with Stripe Elements
元々のVue.jsの知識が浅く、私にコードに置き換えてどのように変更すればよいかがわかりません…

アドバイスいただけますと幸いです。


Laravel8系を使ってます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

neko_daisuki

2021/09/13 18:38

StripeCard.vue の async mounted をただの mounted に変更するとどうなりますか?
mikeko0901

2021/09/14 15:16

メッセージありがとうございます。 ただのmountedにしますと、その下のthis.stripe = await loadStripe(this.public_key.value);でエラーになってしまうので、 mounted() { this.stripe = loadStripe(this.public_key); const elements = this.stripe.elements(); var style = { base: { color: "#32325d", にしました。すると、 Error in mounted hook: "TypeError: this.stripe.elements is not a function" Error in mounted hook (Promise/async): "IntegrationError: The selector you specified (#cardNumber) applies to no DOM elements that are currently on the page. Make sure the element exists on the page before calling mount()." と出ます…
neko_daisuki

2021/09/14 15:27

mounted のときには DOM が用意されているはずなので、 #card-element がないのはおかしい、async のせいだと思ったのですが、 async を外そうとすると mounted を全体的に書き換えないといけなくなってしまいますね。 async mounted のままで、 this.stripeCard.mount('#card-element'); を 以下のように this.$nextTick で囲むとどうなるでしょうか? this.$nextTick(() => { this.stripeCard.mount('#card-element'); })
mikeko0901

2021/09/15 01:16 編集

ありがとうございます。$nextTickで囲んでみました。 async mounted() { this.stripe = await loadStripe(this.public_key); const elements = this.stripe.elements(); var style = { 省略 }; this.stripeCard = elements.create('card', {style: style, hidePostalCode: true}); this.$nextTick(() => { this.stripeCard.mount('#card-element'); }); $nextTickを調べてみて、ビュー全体がレンダリングされるまで待つとありましたので、これならできるかもと思いましたが、以下のようなエラーが出ています… [Vue warn]: Error in nextTick: "IntegrationError: The selector you specified (#card-element) applies to no DOM elements that are currently on the page. Make sure the element exists on the page before calling mount()." found in ---> <StripeCard> ----------------------------------- IntegrationError: The selector you specified (#card-element) applies to no DOM elements that are currently on the page. Make sure the element exists on the page before calling mount().
neko_daisuki

2021/09/15 02:10

使っているライブラリは vue-js-modal でしょうか? これは v-show ではなく v-if で表示状態を制御しているので、 mounted が走る状態では v-if="false" になっており DOM が存在しないのかもしれません。 @opened というイベントが用意されているので、 これが発火したタイミングで初期化処理を実行させます。 1. StripeCard.vue の mounted を methods.setupStripe に移動させる 2. <modal name="stripe-card" @opened="setupStripe"> とする こうすると opened のたびに setupStripe が実行されるので、 data に initialized: false とかを作り、スキップするようにする methods: { setupStripe () { if (this.initialized) return true ... } }
mikeko0901

2021/09/15 05:45 編集

ありがとうございます。 確かに、vue-js-modalを使っています。仰る通り、mounted が走る段階ではDOMが存在しないのかもしれません。 >2. <modal name="stripe-card" @opened="setupStripe"> とする >こうすると opened のたびに setupStripe が実行されるので、~ までは理解ができましたが、なぜ、initialized変数を作り、setupStripeメソッドの中で分岐をさせているのでしょうか?initialized変数はどこでTrueになりますでしょうか??T if(this.initialized == true) { ここに this.stripe = await loadStripe(this.public_key); const elements = this.stripe.elements(); …のようにasync montedの処理を入れるのでしょうか?? } 何から何まで申し訳ございません…
neko_daisuki

2021/09/15 06:37

initialized はモーダルを開くたびに opened が発火し、何度も setupStripe が実行されるのを防ぐためです。 setupStripe () { if (this.initialized) { // true なら処理をやめる return } else { this.initialized = true } // 以下に mounted にあった内容 }
mikeko0901

2021/09/15 07:51

なるほどですね!ありがとうございます。やってみます!
mikeko0901

2021/09/21 14:37

neko_daisuki様 こちらありがとうございます。できました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問