Alpine.js bind the change of select back to x-data

1.9k views Asked by At

I have two anchor tags whose @click directives? update my select options. I would like when the options are changes to update the value of activeTab to be either 0 or 1. I've been trying @change but no joy. Thanks in advance

<script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"></script>
<div x-data="{activeTab : window.location.hash ? window.location.hash.substring(1) : 0, lessons:[{id:0,room:'online',description:'Online description'},{id:1,room:'in class',description:'in class description'}]}" x-init="select = lessons[0].room" class="w-full">
  <nav class="w-full flex flex-no-wrap justify-between mb-8">
    <template x-for="lesson in lessons">
      <a href="#" @click.prevent="activeTab = lesson.id; window.location.hash = 0; select = lesson.room" class="focus:outline-none focus:text-teal-800 hover:text-teal-800 meta bold py-1 uppercase mr-1 flex items-center justify-between text-lg w-1/2 border-b-4 focus:border-teal-800 hover:border-teal-800 border-teal-600 tracking-widest text-teal-600"><span x-text="lesson.room"></span><svg class="w-6 h-6" width="6" height="6" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg">
          <path d="m8.5.5-4 4-4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" transform="translate(6 8)" /></svg></a>
    </template>
  </nav>
  <template x-for="lesson in lessons" :key="lesson.id">
    <div x-show="activeTab === lesson.id">
      <p x-text="lesson.description" class="text-gray-800 mb-6">Online classes are streaemed to your device. You can atned a yoga class wherever there is a why-fi</p>
    </div>
  </template>

  <form action="">
    <fieldset class="border p-4">
      <legend class="text-center text-xs uppercase tracking-widest text-orange-800 px-2">choose a classroom</legend>
      <select class="relative uppercase text-lg tracking-widest text-teal-800 w-full border border-teal-800 px-5 py-4 focus:outline-none focus:border-shadow rounded" name="" id="" x-model="select">
        <template x-for="lesson in lessons" :key="lesson.id">
          <option :id="lesson.id"><span x-text="lesson.room"></span></option>
        </template>
      </select>
    </fieldset>
  </form>
</div>

1

There are 1 answers

0
Kevin E On

Here is a simplified version of your code that works. I didn't really want to change your code, but I had to simplify it a bit to comprehend it. For example, activeTab and select seemed redundant, and I didn't understand the part about window.location.hash.

<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
<div
  x-data="{
    activeTab: 0,
    lessons: [
      { id: 0, room: 'online', description: 'Online description' },
      { id: 1, room: 'in class', description: 'In class description' }
    ],
  }"
>
  <nav>
    <template x-for="lesson in lessons">
      <a
        href="#"
        @click.prevent="window.location.hash = activeTab = lesson.id"
        :style="activeTab === lesson.id ? 'font-weight:bold' : 'text-decoration:none'"
      >
        <span x-text="lesson.room"></span></a>
    </template>
  </nav>
  
  <template x-for="lesson in lessons">
    <div x-show="activeTab === lesson.id">
      <p x-text="lesson.description"></p>
    </div>
  </template>

  <form>
    <fieldset>
      <legend>choose a classroom</legend>
      <select x-model.number="activeTab">
        <template x-for="lesson in lessons">
          <option :value="lesson.id" x-text="lesson.room"></option>
        </template>
      </select>
    </fieldset>
  </form>
</div>

JSFiddle

I believe the problem was a strict equality comparison (===) between values which were sometimes strings and sometimes integers. For example, "1" === 1 is never true.

I think there were two problems with your original code:

  1. select wasn't declared part of x-data, thus it wasn't christened a "reactive" property, thus changing the value of the <select> had no actual effect
    • fix this by adding something like select: null to your x-data
  2. that aside, there was still nothing to trigger the update of activeTab when the <select> was changed
    • binding lesson.id to the <option> values and the <select>'s value to activeTab with x-model.number (forgoing the select variable entirely) is how I chose to address this problem
    • this way the x-shows for the active tab trigger directly from a change to the <select>'s value

This didn't apply in your code as originally written, but keep in mind that an x-model on a <select> will always return strings, unless you use x-model.number to coerce to integer.