javascriptdrop-down-menulocal-storagealpine.jspersist

AlpineJS Dropdown with Default Option using Persist for Local Storage


The following code contains a drop down menu with two options for "Paris" and for "Rome". The drop down itself is working perfectly and storing the selected option in local storage is also working just fine. Neither of those are the problems. Let me explain what I am trying to achieve.

Users may end up on this page before choosing an option. I would like the default option to be "Add Destination" if they land on a page with this dropdown before selecting anything. Once they select/change an option, it would be added/updated to local storage and replace the default option with what is stored.

Currently, if you land on a page with this drop down before making a selection from a different page, the drop down is blank. Once you make a selection of "Paris" or "Rome", the data is stored using "x-data" and is placed into the by innerHTML using "x-html".

"x-text" in allowing for the default to be "Add destination" because "x-html" is looking for the local storage item: "chosenDestination".

<div
    x-data="{ open: false, selected: '' }"
    @click.away="open = false"
    class="relative flex-1"
    >
    <!-- Button -->

    <button
        @click="open = !open"
        class="flex items-center justify-between flex-1 w-full gap-2 p-4 text-left border border-gray-500 rounded-lg"
        :class="{'text-black': selected !== '', 'text-gray-500': selected === ''}"
        x-data="{ destination: localStorage.getItem('chosenDestination') }"
    >
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16">
            <path d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10m0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6"/>
        </svg>
        <span
        class="w-full overflow-hidden text-gray-600 whitespace-nowrap"
        x-text="selected === '' ? 'Add destination' : selected"
        x-html="destination"
        id="destination"
        ></span>

    </button>

    <!-- Dropdown Menu -->

    <div
        x-show="open"
        class="absolute left-0 p-4 mt-2 bg-white rounded-md shadow-md"
        x-cloak
    >
        <ul
        class="max-h-[140px] overflow-auto [&>li]:text-gray-500 [&>li]:px-4 [&>li]:py-2 hover:[&>li]:bg-gray-100 [&>li]:cursor-pointer space-y-2"
        @click="chosenDestination();"
        >
        <li @click="selected = $el.textContent; open = false;">Paris</li>
        <li @click="selected = $el.textContent; open = false;">Rome</li>
        </ul>
    </div>
</div>

<script>
    function chosenDestination() {
        var destination = document.getElementById('destination').innerHTML;
        localStorage.setItem("chosenDestination", destination);
    }
</script>

Not a deal breaker but a would be nice: Currently I using a javascript function to perform local storage but I would much rather prefer to use AlpineJS Persist to handld this, however, I cannot seem to figure out how to make that work.

Any help would be greatly appreciated.


Solution

  • Using Persist you can simplfy a bit your code (here how to include Persist in your project):

    <div
            x-data="{ open: false, selected: $persist('').using(sessionStorage).as('chosenDestination') }"
            @click.away="open = false"
            class="relative flex-1"
    >
        <!-- Button -->
    
        <button @click="open = !open"
                class="flex items-center justify-between flex-1 w-full gap-2 p-4 text-left border border-gray-500 rounded-lg"
                :class="{'text-black': selected !== '', 'text-gray-500': selected === ''}"
    
        >
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16">
                <path d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10m0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6"/>
            </svg>
    
            <span class="w-full overflow-hidden text-gray-600 whitespace-nowrap"
                  x-text="selected === '' ? 'Add destination' : selected"
                  id="destination"
            ></span>
    
        </button>
    
        <!-- Dropdown Menu -->
    
        <div x-show="open"
             class="absolute left-0 p-4 mt-2 bg-white rounded-md shadow-md"
             x-cloak
        >
            <ul class="max-h-[140px] overflow-auto [&>li]:text-gray-500 [&>li]:px-4 [&>li]:py-2 hover:[&>li]:bg-gray-100 [&>li]:cursor-pointer space-y-2"
            >
                <li @click="selected = $el.textContent; open = false;">Paris</li>
                <li @click="selected = $el.textContent; open = false;">Rome</li>
            </ul>
        </div>
    
    </div>
    

    I removed the destination variable (and also x-data) from the button: the selected variable is enough to store the chosen destination.
    In x-data the destination variable is initialized via $persist, defaulting to an empty string and choosing sessionStorage, so when you close the browser and reopen it, the destination is reset.
    x-html="destination" is not necessary.
    Now @click="chosenDestination();" is also no longer necessary, with Persist the stored value is automatically updated