It seems like the issue you're facing is due to Livewire's DOM diffing engine. When Livewire re-renders the component, it updates the DOM based on the changes, which can cause JavaScript-initialized elements like Stripe Elements to be lost if they are within the part of the DOM being updated.
To solve this, you can use Livewire's wire:ignore directive to tell Livewire to ignore changes to the Stripe Element's container during DOM diffing. However, since you've mentioned that wire:ignore hasn't solved the issue, you might need to reinitialize the Stripe Element after Livewire updates the DOM.
Here's a solution that uses Livewire's wire:ignore directive along with Alpine.js to reinitialize the Stripe Element after Livewire updates:
- Add
wire:ignoreto the container of the Stripe Element to prevent Livewire from modifying it during updates. - Use Alpine.js to detect when Livewire has finished re-rendering and then reinitialize the Stripe Element.
Here's how you can modify your stripe-elements.blade.php:
<div x-data="{ stripeLoaded: false }"
x-init="$watch('stripeLoaded', value => {
if (value) {
initializeStripe();
}
})"
x-on:livewire-load.window="stripeLoaded = true"
wire:ignore
>
<label for="card">
Credit Card Information<b>•</b>
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
This text will be replaced by the Stripe card element.
</div>
<div class="smaller text-danger mt-2" id="card-errors">
<!-- Card errors inserted here -->
</div>
</div>
@push('scripts.body')
<script>
function initializeStripe() {
// Create a Stripe client.
let stripe = Stripe('{{ config('services.stripe.key') }}');
// Create an instance of Elements.
let elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
let style = {};
// Create an instance of the card Element.
let card = elements.create('card', { style: style });
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
console.log('Card element mounted.');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
console.log('Card element changed.');
});
}
// Handle form submission.
function generateStripeToken() {
console.log('Generating Stripe token.');
stripe.createToken(card).then(function(result) {
console.log(result);
if (result.error) {
// Inform the user if there was an error.
let errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
@this.set('stripe_token', result.token.id);
Livewire.emit('stripeTokenReceived');
}
});
}
document.addEventListener('livewire:load', () => {
initializeStripe();
});
</script>
@endpush
In this solution, we're using Alpine.js to watch for a stripeLoaded property change. When Livewire finishes loading, we set stripeLoaded to true, which triggers the initializeStripe function to re-mount the Stripe Element.
Additionally, we've added a livewire:load event listener to call initializeStripe when the Livewire component is first loaded.
This approach ensures that the Stripe Element is properly initialized after Livewire updates without being overwritten by Livewire's DOM diffing.