Focus an input on select change with Alpine.js - alpine.js

I have three tabs and three corresponding select options. When I click on a tab I select its corresponding option. I'd like to be able to select an option and focus its corresponding tab. Thanks in advance.
function tabs(){
return{
select: 'focus-a',
tabs:[
{
name: 'focus-a'
},
{
name: 'focus-b'
},
{
name: 'focus-c'
}
]
}
}
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
<div
x-data="tabs()"
>
<div>
<template x-for="btn in tabs">
<input type="button" x-ref="btn.name" x-on:click="select = btn.name" :value="btn.name" />
</template>
</div>
<div>
<select name="test">
<template x-for="tab in tabs" x-on:change="buttons.focus()">
<option :selected="tab.name === select" :value="tab.name" x-text="tab.name"></option>
</template>
</select>
</div>
</div>

I got help over on Github. Working code is below with comments. I lost the trail of who actually fixed it for me. It may have been you Hugo?
function data() {
return {
activeTab: 0,
tabs: [
{
id: 0,
title: "tab-1",
text: "all the text"
},
{
id: 1,
title: "tab-2",
text: "all the tab 2 text"
}
]
};
}
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
<div class="" x-data="data()">
<div>
<template x-for="tab in tabs" :key="tab.id">
<button x-text="tab.title" #click="activeTab = tab.id; console.log(activeTab)"></button>
</template>
</div>
<div class="">
<template x-for="tab in tabs" :key="tab.id">
<p x-text="tab.text" x-show="tab.id === activeTab"></p>
</template>
</div>
<div>
<select x-model="activeTab" name="" id="" #change="activeTab = $event.target.options[$event.target.selectedIndex].index; ">
<template x-for="tab in tabs" :key="tab.id">
<option :key="tab.id" :value="tab.id" :selected="activeTab === tab.id" x-text="tab.title"></option>
</template>
</select>
</div>
</div>

What about binding x-ref (:x-ref="btn.name") so that the x-ref and the option.value are the same?
The other issue I found is that the x-on:change was on the template instead of the select, you can then use the $event.target.value to look up the correct $refs value.
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
<div
x-data="tabs()"
>
<div>
<template x-for="btn in tabs">
<input type="button" :x-ref="btn.name" x-on:click="select = btn.name" :value="btn.name" />
</template>
</div>
<div>
<select name="test" x-on:change="$refs[$event.target.value].focus()">
<template x-for="tab in tabs" >
<option :selected="tab.name === select" :value="tab.name" x-text="tab.name"></option>
</template>
</select>
</div>
</div>

Related

Laravel Vue, pass data in form from component after submit

i need a little help about how to get values from a component that is in a form.
This is the form also a photo: form
`
<form #submit.prevent="form.patch(route('profile.update.account'))" class="mt-6 space-y-6">
<div>
<InputLabel for="adresa" value="Adresa" />
<TextInput
id="adresa"
type="text"
class="mt-1 block w-full"
v-model="form.adresa"
required
autofocus
autocomplete="adresa"
/>
<InputError class="mt-2" :message="form.errors.adresa" />
</div>
<DropdownCountries
id="tara"
v-model="form.tara"
required
autocomplete="Romania"
/>
<div class="flex items-center gap-4">
<PrimaryButton :disabled="form.processing">Save</PrimaryButton>
<Transition enter-from-class="opacity-0" leave-to-class="opacity-0" class="transition ease-in-out">
<p v-if="form.recentlySuccessful" class="text-sm text-gray-600">Saved.</p>
</Transition>
</div>
</form>
`
There is the component also a photo: dropdown
`
<template>
<div class="container">
<div class="row justify-content-center" style="margin: 20px 0px 20px 0px;">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<div class="form-group">
<select name="tara" class='form-control' v-model='country' #change='getStates()'>
<option value='0' >Select Country</option>
<option v-for='data in countries' :value='data.id'>{{ data.name }}</option>
</select>
</div>
<div class="form-group">
<select name="judet" class='form-control' v-model='state' #change='getCities()'>
<option value='0' >Select State</option>
<option v-for='data in states' :value='data.id'>{{ data.name }}</option>
</select>
</div>
<div class="form-group">
<select name="oras" class='form-control' v-model='city'>
<option value='0' >Select City</option>
<option v-for='data in cities' :value='data.id'>{{ data.name }}</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
country: 180,
countries: [],
state: 0,
states: [],
city: 0,
cities: [],
}
},
methods:{
getCountries: function(){
axios.get('/api/tari')
.then(function (response) {
this.countries = response.data;
}.bind(this));
},
getStates: function() {
axios.get('/api/judete/' + this.country).then(function(response){
this.states = response.data;
}.bind(this));
},
getCities: function() {
axios.get('/api/orase/' + this.state).then(function(response) {
this.cities = response.data
}.bind(this));
}
},
created: function(){
this.getCountries()
}
}
</script>
`
My problem is that when i press Save button for submitting the form the values from dropdowns are not applied: response
I've been looking on internet for 2 hours can someone explain me why is that.
You should check this: v-model and child components?
This should answer your question. Let me know if you need more informaiton of how passing v-model values works.

Add and Remove dom elements using alpine JS

I am trying to build a custom add and remove entire div element from an array using alpine JS, here is my code which is working but instead of removing from the exact remove button click it will remove the last one on the array.
HTML
<div x-data="addRemove()">
<template x-for="(field, index) in fields" :key="index">
<div>
<input type="text" name="txt1[]" class="form-input">
<button type="button" class="btn btn-danger btn-small" #click="removeField(index)">×</button>
</div>
</template>
<button type="button" #click="addNewField()">+ Add Row</button>
</div>
JAVASCRIPT
return {
fields: [],
addNewField() {
this.fields.push({});
},
removeField(index) {
this.fields.splice(index, 1);
}
}
Found a solution, this is what I did.
HTML
<div x-data="addRemove()">
<template x-for="(field, index) in fields" :key="field.id">
<div>
<input type="text" name="txt1[]" class="form-input">
<button type="button" class="btn btn-danger btn-small" #click="removeField(field)">×</button>
</div>
</template>
<button type="button" #click="addNewField()">+ Add Row</button>
</div>
JAVASCRIPT
function addRemove() {
return {
fields: [],
addNewField() {
this.fields.push({id: new Date().getTime() + this.fields.length});
},
removeField(field) {
this.fields.splice(this.fields.indexOf(field), 1);
}
}
}
You've to set the 'key' with a unique value in the looping.
<div x-data="{data: []}">
<div #click="data.push({ randomNumber: new Date().getTime()})">(Click Here To Add New Data)</div>
<template x-for="(item, index) in data" :key="index">
<div>
<span x-text="item.randomNumber"></span>
<span #click="data.splice(index, 1)">Remove</span>
</div>
</template>
</div>
online test: https://codepen.io/yuxufm/pen/YzLoxvE

$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>

Recover item id in v-for loop

I am trying to build response to comment. Here is what I am trying to do:
I take the comment ID from the v-for loop and put it in a span that is invisible. Then I want to recover the value of id in 'pm' variable In function call have(), then send it to my controller to insert data in DB. But nothing happens and it shows an error that he can recover the id.
this my vue ProjectDeatil.vue:
<div class="form-group" style="display: inline;" >
<form #submit.prevent="ajouterCommentaire()">
<label>Votre Commentaire:</label>
<div class="input-group input-group-sm mb-0">
<input class="form-control form-control-sm" v-model="form.body" type="text" name="body"
:class="{ 'is-invalid': form.errors.has('body') }" placeholder="commenter.." style="width:450px;">
<has-error :form="form" field="body"></has-error>
<div class="input-group-append">
<button type="submit" class="btn btn-success " >Commenter</button>
</div>
</div>
</form>
</div>
<div v-for="comment in comments" :key="comment.id" class=" align-items mt-3">
<span id="pm" :value="comment.id" style="">{{ comment.id }}</span>
<hr>
<span class="badge badge-primary"> {{ comment.comment_user_name }}</span > {{ comment.body }}
<b> <small class="badge badge-success" style="float:right; color:#2d132c " >Posté le {{ comment.created_at || date }} </small></b>
<br>
<button #click="showbtn(comment.id)" class="btn btn-default" >Répondre</button>
<form #submit.prevent="ajouterCommentaireReponse()" v-bind:id="comment.id " class="d-none">
<div class="input-group input-group-sm mb-0">
<input class="form-control form-control-sm " type="text" name="body"
placeholder="commenter.." style="width:450px;">
<div class="input-group-append">
<button type="submit" class="btn btn-success " >Commenter</button>
</div>
</div>
</form>
<hr>
</div>
</div>
</div>
<!-- /.form-group -->
</div>
<!-- /.col -->
</div>
</div>
</div>
<!-- /.row -->
<!-- /.row -->
</div>
<!-- /.card-body -->
</div>
</div>
</template>
<script>
export default {
data(){
return{
key: this.$route.params.id,
projets:[],
projet:{
id:'',
name:'',
durre:'',
description:'',
budget:'',
owner:'',
},
membres:[], membre:{
id :'',
membre:'',
projet_id:'',
},
form : new Form({
id:'',
body:'',
user:'',
}),
comments:[],
comment:{
id:'',
body:'',
created_at:''
}
}
},
methods:{
afficherProjets(){
axios.get('/api/getProjects')
.then(({data}) => {this.projets=data.data});
},
afficherMembre(){
axios.get('/api/membreid').then(({data})=> {this.membres =data.data});
},
ajouterCommentaire(){
this.form.post('/api/comments/'+this.key).then(()=>{
this.form.reset()})
},
ajouterCommentaireReponse(){
axios.post('/api/commentsreponse/'+ this.have()).then(()=>{
})
},
afficherComments(){
axios.get('/api/comments').then(({data})=> {this.comments =data.data});
},
showbtn(id){
let element= document.getElementById(id);
element.classList.toggle('d-none');
},
have(){
var key = document.getElementById('pm').value;
return key;
}
},
mounted() {
console.log('Component mounted.')
this.afficherProjets();
this.afficherMembre();
this.afficherComments();
}
}
</script>
this is my function controller:
public function storereply($key){
//$data =$request->all();
$commentaire=Commentaire::find($key);
$commentairereply =new Commentaire;
$commentairereply->user_id= auth()->user()->id;
$commentairereply->body= request('body');
$commentairereply->comment_user_name=$commentaire->user->name;
$commentaire->comments()->save($commentairereply);
}
and this is my route:
Route::post('/commentsreponse/{key}', 'API\CommentController#storereply');
You need to make sure comment.id can actually reach the have function. Simply pass it as an argument to ajouterCommentaireReponse:
<form #submit.prevent="ajouterCommentaireReponse(comment.id)" v-bind:id="comment.id " class="d-none">
Then, you shouldn't need the help function at all:
module.exports = {
methods:{
ajouterCommentaireReponse(commentId){
axios.post('/api/commentsreponse/'+ commentId)
.then(()=>{
// do things
})
},
},
}
</script>

vee-validate: Required only if a condition is met

I'm using Vuejs2 and vee-validate for form validation. It's a great package, however I'm struggling to implement a conditional required field.
When a particular radio option is selected, I want two select fields to be required. And when that radio is not selected, I want the two select fields to be optional.
I've tried using the attach and detach methods. I can successfully detach the validation. And I can see when I attach a field it appears in the fields object. But it's not picked up by the validator.
Here is my code:
<template>
<form class="ui form" role="form" method="POST" action="/activate" v-on:submit.prevent="onSubmit" :class="{ 'error': errors.any() }">
<div class="ui segment">
<h4 class="ui header">Basic Company Information</h4>
<div class="ui message">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
<div class="field" :class="{ 'error': errors.has('name') }">
<div class="ui labeled input">
<label class="ui label" for="name">
Company
</label>
<input id="name" type="text" name="name" v-validate="'required'" v-model="name">
</div>
</div>
<div class="ui error message" v-show="errors.has('name')">
<p>{{ errors.first('name') }}</p>
</div>
<div class="grouped fields" :class="{ 'error': errors.has('organisation_type_id') }">
<label for="organisation_type_id">Organisation type</label>
<div class="field">
<div class="ui radio checkbox">
<input class="hidden" type="radio" name="organisation_type_id" value="1" data-vv-as="organisation type" v-validate="'required'" v-model="organisation_type">
<label>Buyer</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input class="hidden" type="radio" name="organisation_type_id" value="2" checked>
<label>Seller</label>
</div>
</div>
</div>
<div class="ui error message" v-show="errors.has('organisation_type_id')">
<p>{{ errors.first('organisation_type_id') }}</p>
</div>
<div v-show="organisation_type == '2'">
<div class="field" :class="{ 'error': errors.has('countries[]') }">
<label for="countries">Countries</label>
<select class="ui fluid search dropdown" id="countries" name="countries[]" multiple data-vv-as="countries" v-validate="'required'">
<option v-for="country in countries" :value="country.value">{{ country.text }}</option>
</select>
</div>
<div class="ui error message" v-show="errors.has('countries[]')">
<p>{{ errors.first('countries[]') }}</p>
</div>
<div class="ui message field-description">
<p>Select all the countries you export to.</p>
</div>
<div class="field" :class="{ 'error': errors.has('ciphers[]') }">
<label for="ciphers">Ciphers</label>
<select class="ui fluid search dropdown" id="ciphers" name="ciphers[]" multiple data-vv-as="ciphers" v-validate="'required'">
<option v-for="cipher in ciphers" :value="cipher.value">{{ cipher.text }}</option>
</select>
</div>
<div class="ui error message" v-show="errors.has('ciphers[]')">
<p>{{ errors.first('ciphers[]') }}</p>
</div>
<div class="ui message field-description">
<p>Select all the ciphers you support.</p>
</div>
</div> <!-- End organisation_type_id -->
<button class="ui fluid green button" type="submit">Continue</button>
</div> <!-- .ui.segment -->
</form>
</template>
<script>
export default {
props: ['countriesJson', 'ciphersJson'],
data() {
return {
name: null,
organisation_type: '2',
countries: [],
ciphers: [],
}
},
watch: {
organisation_type: function(value) {
var vm = this
if (value == '2') {
vm.$validator.attach('countries[]', 'required');
const select = document.getElementById('countries');
select.addEventListener('change', function() {
vm.$validator.validate('required', this.value);
});
vm.$validator.attach('ciphers[]', 'required');
const select = document.getElementById('ciphers');
select.addEventListener('change', function() {
vm.$validator.validate('required', this.value);
});
} else {
vm.$validator.detach('countries[]')
vm.$validator.detach('ciphers[]')
}
},
},
mounted() {
this.countries = JSON.parse(this.countriesJson)
this.ciphers = JSON.parse(this.ciphersJson)
},
methods: {
onSubmit: function(e) {
this.$validator.validateAll().then(success => {
e.target.submit()
}).catch(() => {
return
})
}
}
}
</script>
May be you mean something like this?
<input id="name"
type="text"
name="name"
v-validate="{ required: this.isRequired }"
v-model="name">
Where "isRequired" is computed field, which depend from condition
<input id="name"
type="text"
name="name"
v-validate=" isRequired ? 'required' : '' "
v-model="name">
In my case it worked by giving above condition.. Also it is helpful in case of multiple validation rules... e.g. 'required|integer|between:18,99'..
Adding {} will break the expression
you can simply use
v-validate="`required_if:${condition}`"

Resources