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

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/alpine#v2.x.x/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>

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/alpinejs#3.x.x/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:
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
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.

Related

List recursive with checkbox component data into parent component update values

Help to list recursive with checkbox component data into parent component update values:
Parent File:
<TreeVue :items="props.allModules" :selectedItems="userForm.userModules"/>
TreeVue File:
<template>
<div v-for="item in items" :key="item.name" class="overflow-hidden rounded-lg bg-gray-50">
<div class="px-4 py-5 sm:p-6" >
<div class="flex items-center ">
<input :id="item.id" type="checkbox" :value="item.id" v-model="props.selectedItems" class="w-6 h-6 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="default-checkbox" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">{{ item.name }}</label>
</div>
<div v-if="item.children" class="ml-5">
<Tree :items="item.children" :selectedItems="selectedItems" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps } from "vue";
const props = defineProps({
items: Object,
selectedItems: Object,
});
</script>
ConsoleLog:
app.js:7839 [Vue warn] Set operation on key "selectedItems" failed: target is readonly. Proxy {items: Proxy, selectedItems: Proxy}
I need to update parent value after selected item on child TreeVue.
I read something about Emit, but I don't know how to implement it, because I'm new to Vue.

alpinejs #mouseover not working correctly

I want to trigger a dropdown menu via hover so use #mouseover. But when the cursor places between text and chevron, dropdown becomes hide. I use mouseover with a tag so it should contain text and chevron, shouldn't it?
here's my code
<header class="relative z-50" x-data="{ dropdown: '' }">
<nav class="overflow-hidden z-50">
<div class="container space-x-14 py-7 desktop:justify-start flex items-center z-50">
<nav class="space-x-14">
<a #mouseover="dropdown = dropdown === 'open' ? '' : 'open'" class="cursor-pointer font-semibold">
<span class="border-b-2 pb-2 transition duration-300" :class="dropdown === 'open' ? 'border-seaweed' : 'border-transparent'">{{ __('test1') }}</span> <span class="inline-block bg-caret-down bg-center bg-no-repeat w-4 h-2"></span>
</a>
what is the prolem?
To fix this, we need to embed the two <span> elements into a <div> where we add the pointer-events-none class (assuming you are using TailwindCSS) to disable the pointer events of the child elements. Furthermore we also add the #mouseleave event to change the open state.
<a #mouseover="dropdown = 'open'"
#mouseleave="dropdown = ''"
class="cursor-pointer font-semibold">
<div class="pointer-events-none">
<span class="border-b-2 pb-2 transition duration-300" :class="dropdown === 'open' ? 'border-seaweed' : 'border-transparent'">{{ __('test1') }}</span>
<span class="inline-block bg-caret-down bg-center bg-no-repeat w-4 h-2"></span>
<div>
</a>

Better solution instead of $wire.set to pass computed Alpine value for livewire

I am new in Alpine / Livewire. This tag script works well expect one thing: the form is sending continiusly query to the server. Is better way to pass the form data for livewire?
<div class="col-span-6 my-2" x-data="{tags: [], newTag: ''}" >
<x-jet-input id="tags"
type="text"
wire:model="tags"
:value="$wire.set('tags','JSON.stringify(tags)')"
class="mt-1 block w-full hidden"
/>
<div class=" w-full mx-auto ">
<div class="tags-input border-gray-300 shadow-sm focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
<template x-for="tag in tags" :key="tag">
<span class="tags-input-tag">
<span x-text="tag"></span>
<button type="button" class="tags-input-remove" #click="tags = tags.filter(i => i !== tag)">
×
</button>
</span>
</template>
<input class="tags-input-text w-full" placeholder="{{__('Add music tags eg.') }} Pop Trap Live..."
#keydown.enter.prevent="if (newTag.trim() !== '') tags.push(newTag.trim()); newTag = ''"
#keydown.space.prevent="if (newTag.trim() !== '') tags.push(newTag.trim()); newTag = ''"
#keydown.backspace="if (newTag.trim() === '') tags.pop()"
x-model="newTag"
/>
</div>
<x-jet-input-error for="tags" class="mt-2" />
<x-slot name="actions">
<x-jet-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-jet-action-message>
<x-jet-button
>
{{ __('Save') }}
</x-jet-button>
</x-slot>
For everyone who has problem with $wire.set:
I find the solution and is not in documentation.
$wire.set need third parameter true to skip watch function and it won't send data in the background if no need
$wire.set('param','value',true)

$dispatch select change event to update button focus

Hello I have a select element I would like to set a buttons focus on change. My code is as follows. I have included a $dispatch but not sure I am doing it correctly
function data() {
return {
open: "tab 1",
tabs: [
{
value: "tab 1",
text: "Text for tab 1"
},
{
value: "tab 2",
text: "Text for tab 2"
},
{
value: "tab 3",
text: "Text for tab 3"
}
]
};
}
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
<div x-data="data()" x-init="select = open, $watch('select', value => console.log(value))" $watch="('select', value => console.log(select))" class="w-full h-screen bg-gray-200 flex items-center justify-center">
<div class="flex flex-wrap gap-2">
<template x-for="btn in tabs" :key="btn.value">
<button x-on:selectchange=":focus = $event.detail.value === btn.value" x-text="btn.value" #click="open = btn.value, select = btn.value" class="bg-blue-400 px-6 py-4 rounded-sm border-b-4 border-blue-800 uppercase tracking-widest font-bold " :class="{'bg-blue-800 text-blue-300' : btn.value === open}">toggle</button>
</template>
<div class="w-full">
<form action="">
<select class="w-full appearance-none px-6 py-4 border border-blue-800 rounded-sm" name="" id="" x-model="select">
<template x-for="opt in tabs" x-on:change="$dispatch='selectchange',{value:opt.value}" :key="opt.value">
<option x-text="opt.value"></option>
</template>
</select>
</form>
</div>
<div>
<template x-for="txt in tabs">
<p x-text="txt.text" x-show="open === txt.value"></p>
</template>
</div>
</div>
</div>
Thanks in advance
$dispatch is a function you need to call. You also want to use a ref so you can focus your button easily.
<div x-data="data()" x-init="select = open, $watch('select', value => console.log(value))" $watch="('select', value => console.log(select))" class="w-full h-screen bg-gray-200 flex items-center justify-center">
<div class="flex flex-wrap gap-2">
<template x-for="btn in tabs" :key="btn.value">
<button x-ref="button" x-on:selectchange.window="$refs.button.focus()" x-text="btn.value" #click="open = btn.value, select = btn.value" class="bg-blue-400 px-6 py-4 rounded-sm border-b-4 border-blue-800 uppercase tracking-widest font-bold " :class="{'bg-blue-800 text-blue-300' : btn.value === open}">toggle</button>
</template>
<div class="w-full">
<form action="">
<select class="w-full appearance-none px-6 py-4 border border-blue-800 rounded-sm" name="" id="" x-model="select">
<template x-for="opt in tabs" x-on:change="$dispatch('selectchange', {value:opt.value})" :key="opt.value">
<option x-text="opt.value"></option>
</template>
</select>
</form>
</div>
<div>
<template x-for="txt in tabs">
<p x-text="txt.text" x-show="open === txt.value"></p>
</template>
</div>
</div>
</div>

Change value of select option by clicking anchor tag with Alpine.js

I'm using x-data to dynamically build my HTML. I have two anchor tags which act as tab buttons to'x-show' a paragraph depending on which link is clicked. I also would like that anchor tag to select an option on the form's select element. It kind of works when you starts clicking on the buttons but initially the select options are empty?
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/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'}]}" 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">
<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="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">
<option x-text="lesson.room"></option>
</template>
</select>
</fieldset>
</form>
</div>
I added:
x-init="select = lessons[0].room"
to the parent of the component which set the select to the initial value
A better way would be to add this onto the select
<select x-model="foo" x-init="foo = $el.options[$el.selectedIndex || 0].value">

Resources