Vue Связывание однокомпонентного элемента SPA с элементами Stripe Elements - PullRequest
0 голосов
/ 01 февраля 2020

Нужно посмотреть, смогу ли я использовать Stripe Elements для одного файлового компонента Vue SPA. Тем не менее, меня оспаривает следующая ошибка:

IntegrationError: Missing argument. Make sure to call mount() with a valid DOM element or selector.
    at new t (https://js.stripe.com/v3/:1:10765)
    at t.<anonymous> (https://js.stripe.com/v3/:1:97008)
    at t.<anonymous> (https://js.stripe.com/v3/:1:26038)
    at VueComponent.createCardElement (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/stripe/card-modal.vue?vue&type=script&lang=js&:143:17)
    at VueComponent.stripePubKey (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/stripe/card-modal.vue?vue&type=script&lang=js&:177:14)
    at Watcher.run (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4562:19)
    at flushSchedulerQueue (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4304:13)
    at Array.eval (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1979:12)
    at flushCallbacks (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1905:14)

Код выполняется с ошибкой прямо в функции .mount (), жалуясь на несуществующий элемент / селектор DOM.

Ниже приведены следующие методы, которые я пытался:

  1. Код выполняется после монтирования компонента жизненный цикл (Мой компонент представляет собой Buefy модальный , а монтирование элемента полосы в виде logi c выполняется, когда модальный виден пользователю).
  2. Код выполняется немедленно в соответствии с Официальный пример Stripe Vue
  3. Код выполняется сразу после того, как свойство stripePubKey не имеет значение null (обратите внимание, что 'stripePubKey' обновляется в подключенном сегменте Vue lifecycle).

Вот компонент по сценарию 3

<template>
    <div>
        <b-button class="button is-info is-rounded"
                  size="is-medium"
                  icon-left="credit-card"
                  :loading="isLoading" 
                  @click="isModalActive = true" v-if="!cardId">
            Add a card
        </b-button>
        <button class="button is-warning"
                :loading="isLoading"
                @click="isModalActive = true" v-else>
            Edit
        </button>

        <b-modal has-modal-card trap-focus :active.sync="isModalActive">
            <b-loading :active.sync="isModalLoading" :can-cancel="false" />
            <!--https://stackoverflow.com/questions/48028718/using-event-modifier-prevent-in-vue-to-submit-form-without-redirection-->
            <form v-on:submit.prevent="create()" class="has-text-justified">
                <div class="modal-card">
                    <header class="modal-card-head">
                        <p class="modal-card-title" v-if="!cardId">Add a card</p>
                        <p class="modal-card-title" v-else>Edit a card</p>
                    </header>
                    <section class="modal-card-body">
                        <div ref="cardo"></div>
                        <p v-show="elementsError" id="card-errors" v-text="elementsError" />
                    </section>

                    <footer class="modal-card-foot">
                        <button class="button" type="button" @click="isModalActive = false">Close</button>
                        <button class="button is-primary" type="submit" :disabled="!complete">Add</button>
                    </footer>
                </div>
            </form>
        </b-modal>
    </div>
</template>

<script>
    import {mapActions, mapGetters} from 'vuex';
    import {NotificationProgrammatic as Notification} from 'buefy';
    import PaymentService from "@/services/auth/PaymentService";

    export default {
        name: "stripe-card-modal",
        props: {
            currentRoute: window.location.href, // https://forum.vuejs.org/t/how-to-get-path-from-route-instance/26934/2
            cardId: {
                type: String,
                default: null
            }
        },
        computed: {
            ...mapGetters('oidcStore', [
                'oidcUser'
            ])
        },
        data: function () {
            return {
                isLoading: true,
                isModalActive: false,
                isModalLoading: false,
                complete: false,
                // Stripe variables
                card: null,
                elementsError: null,
                paymentMethod: 'card',
                stripe: null,
                stripePubKey: ''
            }
        },
        methods: {
            ...mapActions('oidcStore', ['authenticateOidc', 'signOutOidc']),
            createCardElement: function() {
                let self = this;

                // Check if stripe is up, else set it up
                if (!self.stripe && self.stripePubKey) {
                    self.stripe = Stripe(self.stripePubKey);
                } else {
                    return;
                }

                // Get stripe elements up
                const elements = self.stripe.elements({
                    // Use Roboto from Google Fonts
                    fonts: [
                        {
                            cssSrc: 'https://fonts.googleapis.com/css?family=Roboto',
                        },
                    ],
                    // Detect the locale automatically
                    locale: 'auto',
                });
                // Define CSS styles for Elements
                const style = {
                    base: {
                        fontSize: '15px',
                        fontFamily: 'Roboto',
                        fontSmoothing: 'antialiased',
                        color: '#525f7f',
                        '::placeholder': {
                            color: '#AAB7C4',
                        },
                    },
                    // Styles when the Element has invalid input
                    invalid: {
                        color: '#cc5b7a',
                        iconColor: '#cc5b7a'
                    }
                };
                // Create the card element, then attach it to the DOM
                self.card = elements.create('card', {style});
                self.card.mount(this.$refs.cardo);
                // Add an event listener: check for error messages as we type
                self.card.addEventListener('change', ({error}) => {
                    if (error) {
                        self.elementsError = error.message;
                    } else {
                        self.elementsError = '';
                    }
                });
            },
            create: function () {
                this.isModalLoading = true;

                let self = this;

            },
        },
        mounted: function() {
            let self = this;

            PaymentService.getStripePubKey()
                .then(function(res) {
                    self.stripePubKey = res.data;
                })
            .finally(function() {
                self.isLoading = false;
            });
        },
        watch: {
            stripePubKey(newVal, oldVal) {
                let self = this;

                if (newVal && !oldVal) { // If the modal is up
                    self.createCardElement();
                }
            }
        },
    }
</script>

Кроме того, приведенный выше файл компонента пытается подключить элемент карты с помощью $ refs в то время как официальный образец Stripe VueJS использует для этого '# card-element'. Это уже было предпринято!

Вот мой индекс. html для моего SPA на всякий случай (я также пытался поместить его в конец тега.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link href="https://fonts.googleapis.com/css?family=Roboto:100:300,400,500,700,900|Material+Icons" rel="stylesheet">
    <title>Nozomi</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but Nozomi doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
  <script src="https://js.stripe.com/v3/"></script>
</html>

1 Ответ

1 голос
/ 01 февраля 2020

То, что происходит, - то, что buefy модель фактически не существует в DOM, пока она не будет открыта и визуализирована.

Это действительно простое исправление - вместо прямой установки isModalActive в значение true создайте метод для его открытия и создания экземпляра элемента stripe в следующем тике. Это обеспечит визуализацию элемента dom.

methods: {
    openModal() {
        this.isModalActive = true;
        this.$nextTick(function () {
            this.createCardElement()
        })
    }
}
...