Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

gerardw85's avatar

DOM issue when performing a fetch using alpine.js

I'm validating a URL to see if it's already in my database. When I run the following code, it's very quickly flashing the Invalid link message before switching to the valid message. I think this is happening because the the fetch response is completing after x-text is running.

My solution was to create an in progress state so that x-text would only run after the fetch was completed. But I'm unable to make this work. I've tired applying x-cloak on the <p> tag but that also didn't work.

The rest of the code seems to work as intended, but I'm trying to iron out this UX issue. Does anyone see what's wrong with this code, or have a better approach?

<div x-data="{
                        formData: {
                            apply_link: ''
                        },
                        validLink: false,
                        linkValue: '',
                        fetchInProgress: true,
                        verifyDupe() {
                            this.fetchInProgress = true;
                            this.formData.apply_link = document.querySelector('input[name=apply_link]').value;
                            fetch('{{ route('listings.verifyapplylink') }}', {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                    'Accept': 'application/json',
                                    'X-CSRF-Token': document.querySelector('input[name=_token]').value
                                },
                                body: JSON.stringify(this.formData)
                            })
                            .then((response) => {
                                if (response.status == '200')
                                    this.validLink = true;
                                else
                                    this.validLink = false;
                            })
                            .catch((e) => {
                                this.validLink = false;
                            })
                            .finally(() => {
                                this.fetchInProgress = false;
                            });
                        }
                    }"
                    class="mb-4 mx-2"
                    x-init="verifyDupe()"
                >
                    <x-label for="apply_link" value="Link To Apply"/>
                    <p class="block text-xs">(Ex: https://www.edtech.com)</p>
                    <x-input
                        @change="verifyDupe()"
                        @keyup="verifyDupe()"
                        x-model="linkValue"
                        class="block mt-1 w-full"
                        id="apply_link"
                        type="text"
                        name="apply_link"
                        :value="old('apply_link')"
                        required />
                    <p
                        class="block text-s"
                        :class="validLink ? 'text-green-800' : 'text-red-800'"
                        x-text="linkValue && !fetchInProgress ? (validLink ? 'Link is valid.' : 'Invalid or duplicate link.'): ''"></p>
                </div>
0 likes
1 reply
LaryAI's avatar
Level 58

It looks like the issue is that the fetchInProgress state is not being used to prevent the "Invalid link" message from flashing before the fetch request completes. To fix this, you can modify the x-text directive to only display the message after the fetch has completed, by checking the fetchInProgress state.

Here's the updated code snippet:

<div x-data="{
                formData: {
                    apply_link: ''
                },
                validLink: false,
                linkValue: '',
                fetchInProgress: false,
                verifyDupe() {
                    this.fetchInProgress = true;
                    this.formData.apply_link = document.querySelector('input[name=apply_link]').value;
                    fetch('{{ route('listings.verifyapplylink') }}', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Accept': 'application/json',
                            'X-CSRF-Token': document.querySelector('input[name=_token]').value
                        },
                        body: JSON.stringify(this.formData)
                    })
                    .then((response) => {
                        if (response.ok)
                            this.validLink = true;
                        else
                            this.validLink = false;
                    })
                    .catch((e) => {
                        this.validLink = false;
                    })
                    .finally(() => {
                        this.fetchInProgress = false;
                    });
                }
            }"
            class="mb-4 mx-2"
            x-init="verifyDupe()"
        >
            <x-label for="apply_link" value="Link To Apply"/>
            <p class="block text-xs">(Ex: https://www.edtech.com)</p>
            <x-input
                @change="verifyDupe()"
                @keyup="verifyDupe()"
                x-model="linkValue"
                class="block mt-1 w-full"
                id="apply_link"
                type="text"
                name="apply_link"
                :value="old('apply_link')"
                required />
            <p
                class="block text-s"
                :class="validLink ? 'text-green-800' : 'text-red-800'"
                x-text="!fetchInProgress && linkValue ? (validLink ? 'Link is valid.' : 'Invalid or duplicate link.') : ''"></p>
</div>

Changes made:

  1. Set fetchInProgress to false initially.
  2. Updated the x-text directive to check !fetchInProgress before displaying any message. This ensures that the message is only displayed after the fetch has completed.
  3. Changed the response.status == '200' check to response.ok to handle the response status more robustly.

With these changes, the "Invalid link" message should only appear after the fetch request has completed, and if the link is indeed invalid or a duplicate. The "Link is valid" message will appear if the fetch request completes successfully and the link is valid.

Please or to participate in this conversation.