To ensure that the external tokenizer script can always find the container when the modal is opened, you need to make sure that the script is loaded and the tokenizer is initialized every time the modal is opened. Instead of relying solely on the onMounted lifecycle hook, you can use a combination of Vue's watch function to detect when the modal is opened and then load the script and initialize the tokenizer.
Here's a revised version of your code that uses a watch function to detect when the modal is opened and then loads the tokenizer script and initializes the tokenizer:
<template>
<DialogPanel
class="w-full max-w-md transform rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all"
>
<DialogTitle
as="h3"
class="text-lg font-bold leading-6 text-gray-900"
>
Make Online Payment
</DialogTitle>
<p>Amount to be charged: <span class="font-bold">${{ total }}</span></p>
<div class="mt-4" id="container"></div>
<button class="mt-4 px-2 py-1 bg-indigo-600 text-white rounded-md ml-2"
@click="tokenizer.submit()"
>
Submit Payment
</button>
<button
type="button"
class="py-1 px-2 text-white font-bold bg-red-600 rounded-md ml-4"
@click="$emit('close-modal')"
>
Cancel
</button>
<div class="mt-2">
<strong>Note: </strong> If credit card fields do not display, please click <strong>Cancel</strong>, refresh the page, and then click <strong>Online Payment</strong> again.
</div>
</DialogPanel>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { useRouter } from 'vue-router';
const emit = defineEmits(['close-modal']);
const props = defineProps(['userId', 'duesId', 'total', 'route', 'isOpen']);
const apiUrl = import.meta.env.VITE_PAYMENT_GATEWAY_URL;
const publicApiKey = import.meta.env.VITE_PUBLIC_API_KEY;
let tokenizer = {};
function doTokenize() {
const tokenizerObj = new Tokenizer({
url: apiUrl,
apikey: publicApiKey,
container: '#container',
submission: (resp) => {
switch (resp.status) {
case 'success':
router.post(`/users/${props.userId}/dues/${props.duesId}/online-payment`, {
token: resp.token,
total: props.total,
route: props.route
});
emit('close-modal');
break;
case 'error':
console.error(resp.msg);
break;
case 'validation':
console.log(resp.invalid);
break;
}
}
});
tokenizer = tokenizerObj;
}
function loadTokenizerScript() {
const existingScript = document.querySelector('script[src="' + apiUrl + '/tokenizer/tokenizer.js"]');
if (existingScript) {
existingScript.remove();
}
const tokenizerScript = document.createElement("script");
tokenizerScript.setAttribute("src", apiUrl + '/tokenizer/tokenizer.js');
tokenizerScript.setAttribute('language', 'javascript');
tokenizerScript.defer = true;
tokenizerScript.onload = () => {
doTokenize();
};
document.head.appendChild(tokenizerScript);
}
watch(() => props.isOpen, (newVal) => {
if (newVal) {
loadTokenizerScript();
}
});
</script>
Explanation:
-
Props and Emits: The
isOpenprop is used to detect when the modal is opened. -
Tokenizer Initialization: The
doTokenizefunction initializes the tokenizer. -
Script Loading: The
loadTokenizerScriptfunction loads the tokenizer script and initializes the tokenizer once the script is loaded. -
Watch Function: The
watchfunction monitors theisOpenprop. When the modal is opened (isOpenbecomestrue), it callsloadTokenizerScriptto load the script and initialize the tokenizer.
This approach ensures that the tokenizer script is loaded and the tokenizer is initialized every time the modal is opened, preventing the issue where the container is not found.