Disable modal submit until criteria met (JS fetch response)

How would I disable an Action’s modal submit button until I have received a response from a fetch request inside an Alpine component in a custom field? The Pay button is a Filament button which makes a fetch request to a Laravel Controller. If this returns a success I want to enable the submit button. I don’t know how to communicate from my Alpine / Braintree JS back to the Filament modal. If that’s not possible then I guess two other options would be to populate a hidden field with a success value and validate it (but again I don’t know how to communicate back to that field) on submitting the modal. Or, on my fetch request, update a paid status in the database for the payment and check it’s value on submitting. If it’s not paid cancel the submit. I don’t know how to override the submit process and cancel it if paid is false. Custom field component
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div>
<div id="dropin-container"></div>

<x-filament::button id="braintree-submit-button">
Pay
</x-filament::button>

<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('test-component') }}"
x-data="testComponent({
state: {
amount: '79.00',
clientToken: '{{ $clientToken }}'
}
})"
>
</div>
</div>
</x-dynamic-component>
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div>
<div id="dropin-container"></div>

<x-filament::button id="braintree-submit-button">
Pay
</x-filament::button>

<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('test-component') }}"
x-data="testComponent({
state: {
amount: '79.00',
clientToken: '{{ $clientToken }}'
}
})"
>
</div>
</div>
</x-dynamic-component>
Fetch request inside Braintree’s dropin UI - this is just a part of the code....
...
const response = fetch('/braintree', {
method: 'POST',
body: JSON.stringify({
amount: amount,
nonce : payload.nonce
}),
headers: {
...
}
});
response.then(function (response) {
console.log(response);

// assume it worked for now and enable the submit button

});
...
...
const response = fetch('/braintree', {
method: 'POST',
body: JSON.stringify({
amount: amount,
nonce : payload.nonce
}),
headers: {
...
}
});
response.then(function (response) {
console.log(response);

// assume it worked for now and enable the submit button

});
...
No description
Solution:
For anyone finding this question here’s what I did: ```php ->actions([...
Jump to solution
4 Replies
Ric Le Poidevin
Ric Le PoidevinOP15mo ago
One other thought - can I remove the submit button? then perform the submission from my Alpine component if payment is successful?
let submitButton = document.querySelector('#braintree-submit-button');
submitButton.addEventListener('click', function (e) {
e.preventDefault();
console.log('clicked');
instance.requestPaymentMethod({
threeDSecure: threeDSecureParameters
}, function (err, payload) {

console.log('pressed');

if (err) {
console.log('error');
console.error(err);
// Handle errors in requesting payment method
}

// Send payload.nonce to your server
const response = fetch('/braintree', {
method: 'POST',
body: JSON.stringify({
amount: amount,
nonce : payload.nonce
}),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.querySelector('input[name=_token]').value
}
});
response.then(function (response) {
console.log(response);

// assume it worked for now and enable the submit button

});
});
});
let submitButton = document.querySelector('#braintree-submit-button');
submitButton.addEventListener('click', function (e) {
e.preventDefault();
console.log('clicked');
instance.requestPaymentMethod({
threeDSecure: threeDSecureParameters
}, function (err, payload) {

console.log('pressed');

if (err) {
console.log('error');
console.error(err);
// Handle errors in requesting payment method
}

// Send payload.nonce to your server
const response = fetch('/braintree', {
method: 'POST',
body: JSON.stringify({
amount: amount,
nonce : payload.nonce
}),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.querySelector('input[name=_token]').value
}
});
response.then(function (response) {
console.log(response);

// assume it worked for now and enable the submit button

});
});
});
cheesegrits
cheesegrits15mo ago
So just to be clear, the various snippets of Javascript you quoted are contained in your testComponent?
Ric Le Poidevin
Ric Le PoidevinOP15mo ago
Hi Hugh, yes they are. I don’t know how to ‘escape’ them to access Filament for submission etc Okay, amazing what a new day can bring... I had the concept wrong. I need to think of the component wrapping elements like I have in Vue... So I can change the state of the DOM pretty easily and add my own submit button. What I am missing now, is can I remove the Modal’s submit button until isPaid = true or remove it entirely and use my own submit?
<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('test-component') }}"
x-data="testComponent({
state: {
amount: '79.00',
clientToken: '{{ $clientToken }}'
}
})"
>
<div x-show="!isLoaded">
Loading...
</div>

<div x-show="isLoaded">
<div id="dropin-container"></div>

<button type="button" id="braintree-submit-button">Pay!!</button>
</div>

<div x-show="isPaid">
Paid!

<button type="submit">Submit!!</button>
</div>
</div>
<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('test-component') }}"
x-data="testComponent({
state: {
amount: '79.00',
clientToken: '{{ $clientToken }}'
}
})"
>
<div x-show="!isLoaded">
Loading...
</div>

<div x-show="isLoaded">
<div id="dropin-container"></div>

<button type="button" id="braintree-submit-button">Pay!!</button>
</div>

<div x-show="isPaid">
Paid!

<button type="submit">Submit!!</button>
</div>
</div>
Solution
Ric Le Poidevin
Ric Le Poidevin15mo ago
For anyone finding this question here’s what I did:
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('Publish')
->closeModalByClickingAway(false)
->slideOver()
->icon('heroicon-o-check-badge')
->form([
BraintreePayment::make('payment')
->viewData([])
])
->modalSubmitAction(false) // Removes the action button, all ‘actions’ are done with JS in the field so the button is not required
->modalCancelActionLabel('Close') // change the label from cancel to close so it makes sense if paid or unpaid
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('Publish')
->closeModalByClickingAway(false)
->slideOver()
->icon('heroicon-o-check-badge')
->form([
BraintreePayment::make('payment')
->viewData([])
])
->modalSubmitAction(false) // Removes the action button, all ‘actions’ are done with JS in the field so the button is not required
->modalCancelActionLabel('Close') // change the label from cancel to close so it makes sense if paid or unpaid
])
Want results from more Discord servers?
Add your server