When the user double-clicks this:
<div
class="open-select"
v-if="!editable"
#dblclick="editable=true">{{ name }}
</div>
I'd like this multiselect to be open and focused:
<multiselect
v-else
v-model="name"
:options="names"
track-by="id"
tabindex="0"
autofocus
#select="editable=false"
></multiselect>
The double-click event shows the multiselect element fine, but the multiselect still requires the user to click it to open. I'd like it to open automatically after appearing.
Things I've tried:
focusing the multiselect:
tabindex="0"
autofocus
When I try to select the focused item in jQuery, $(':focus')[0], I get 'undefined'
Heyo!
You can put a ref on the component and then trigger focus which will open the dropdown.
<multiselect ref="vms" v-bind="yourAttributes" />
And then in your created hook you add
this.$refs.vms.$el.focus()
A simplest solution - Toggle Vue Multiselect dropdown
You can use 2 events (#open, #close) on VueMultiselect
anda ref in multiselect
like
ref="multiselect"
keep in
data(){
isOpen: false
}
then add to the 2 events
#close="isOpen = false"
#open="isOpen = true"
and use a method
toggle() {
if (this.isOpen) {
this.$refs.multiselect.$el.blur()
this.isOpen = false
} else {
this.$refs.multiselect.$el.focus()
this.isOpen = true
}
}
Finally figured out how to do this (ref didn't work for me):
STEP 1: I was focusing on the wrong element.
So a vue-multiselect element is structured like this (shorthand to only show important parts):
<div class="multiselect"> // <= This is the element to focus on
<div class="multiselect__tags">
<input> // <= Not the input
</div>
</div>
Typically you want to put your tabindex on the input, instead you should put it on the parent with a class of multiselect. This also goes for things like jQuery's focus(). So...
No: $('.multiselect input').focus()
Yes: $('.multiselect').focus()
STEP 2: Correcting tabindex.
Another issue is when vue-multiselect puts a tabindex="-1" on all .multiselect elements. This removes them from the natural order of tabindexes, so you need to reassign all the tabindexes:
In mounted and updated (if necessary) you need code to reassign all the tabindexes:
mounted: function() {
$(document).ready(()=>{
// you may need to delete all tabindexes first.
$('[tabindex]').each( (i,v) => {
$(v).removeAttr('tabindex');
return true;
});
// add new tabindexes
$('input, textarea, button').each(( i,v ) => {
var isMultiSelect = $(v).hasClass('multiselect__input');
if(isMultiSelect){
$(v).parents('.multiselect:first').attr('tabindex', i + 1);
else{
$(v).attr('tabindex', i + 1);
}
});
});
}
Related
When I have a click handler in a div that is repeated with v-for, it seems like changes made in that click handler will not be updated in the DOM.
Why?
https://jsfiddle.net/AndersBillLinden/109uzsx7/27/
html:
<div id="vue">
<input type="checkbox" v-model="force_update"/> force update
<div v-for="e in arr">
{{e.id}} = {{e.text}}
click
<span v-show="e.clicked"> clicked</span>
</div>
</div>
js:
new window.Vue(
{
el: '#vue',
data:
{
arr: [{id:1,text:"one"}, {id:2, text:"two"}, {id:3, text:"three"}],
force_update: true
},
methods:
{
on_link_clicked(e)
{
e.clicked = true;
if (this.force_update)
this.$forceUpdate();
}
}
});
clicking link 1
unchecking force update
clicking the 2nd link
(nothing happens)
checking "force update"
Now the changes in the previous step are rendered.
The conclusion is that we sometimes needs to force the update, but it is unclear why.
Change e.clicked = true; to this.$set(e, 'clicked', true) so it adds reactivity to the property which is not already in the model.
I have a simple Vue component that displays an address, but converts into a form to edit the address if the user clicks a button. The address field is an autocomplete using Google Maps API. Because the field is hidden (actually nonexistent) half the time, I have to re-instantiate the autocomplete each time the field is shown.
<template>
<div>
<div v-if="editing">
<div><input ref="autocomplete" v-model="address"></div>
<button #click="save">Save</button>
</div>
<div v-else>
<p>{{ address }}</p>
<button #click="edit">Edit</button>
</div>
</div>
</template>
<script>
export default {
data() {
editing: false,
address: ""
},
methods: {
edit() {
this.editing = true;
this.initAutocomplete();
},
save() {
this.editing = false;
}
initAutocomplete() {
this.autocomplete = new google.maps.places.Autocomplete(this.$refs.autocomplete, {});
}
},
mounted() {
this.initAutocomplete();
}
}
I was getting errors that the autocomplete reference was not a valid HTMLInputElement, and when I did console.log(this.$refs) it only produced {} even though the input field was clearly present on screen. I then realized it was trying to reference a nonexistent field, so I then tried to confine the autocomplete init to only when the input field should be visible via v-if. Even with this, initAutocomplete() is still giving errors trying to reference a nonexistent field.
How can I ensure that the reference exists first?
Maybe a solution would be to use $nextTick which will wait for your DOM to rerender.
So your code would look like :
edit() {
this.editing = true;
this.$nextTick(() => { this.initAutocomplete(); });
},
Moreover if you try to use your this.initAutocomplete(); during mounting it cannot work since the $refs.autocomplete is not existing yet but I'm not sure you need it since your v-model is already empty.
I think it's because your "refs" is plural
<input refs="autocomplete" v-model="address">
It should be:
<input ref="autocomplete" v-model="address">
This is my first Vue app so go easy on the code :P
I have a list of users on one side and a component to edit those user details on the other. When selecting a user you see their details and can then click 'edit details'. I want to hide the edit details and show the user details when a new user is selected. Inside the component, I have a ShowEdit variable that is true or false and will either show or hide the edit area. I am sending a prop from the parent into this component when a new user is selected to hide the edit if it is open. I feel I am close as it is currently working perfect but I would like to get rid of the error
"Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders...."
Here are the important bits:
Home
<transition name="component-fade" mode="out-in">
<sidebar-client-content :theClient="selectedUser" :showEditHP="showEditHP"></sidebar-client-content>
</transition>
activate: function(el) {
this.showEditHP = true // Hide edit if new user selected
},
Component
<div id="sidebar-client-content">
<div class="staff_header">
<a v-if="showEdit" v-on:click="showEdit = !showEdit"><i class="fal fa-edit"></i>Edit</a>
<a v-if="!showEdit" v-on:click="showEdit = !showEdit"><i class="far fa-times"></i>Close</a>
</div>
<transition-group name="fadeHeight" mode="out-in">
<div class="client_information" v-if="showEdit">
<!-- The Client Details -->
</div>
<div class="client_information" v-if="!showEdit">
<!-- Client Edit Form -->
</div>
</transition-group>
</div>
export default {
props: [
'showEditHP', // Close edit if new user selected
],
computed: {
showEdit: {
get: function() {
return this.showEditHP
},
set: function (newValue) {
this.showEditHP = newValue
}
}
},
I understand the this.showEditHP = newValue line is where I make the edit but I can't seem to get it to work any other way. I want the parent to be able to overwrite it as the error says. Is there a way to achieve this and have the error removed?
Thanks.
As Nguyun said you can use $emit to send the value back to your parent to keep the values in sync. You can only change your parent data with emit otherwise it will just remain either true or false while the child continues to make it's changes. Keep the values in sync with emit and then use watch to check if the parent makes it's change.
<transition name="component-fade" mode="out-in">
<sidebar-client-content #clicked="onClickChild" :theClient="selectedUser" :showEditHP="showEditHP"></sidebar-client-content>
</transition>
onClickChild (value) {
// I am the child value inside the parent!
},
Then in the child
<div class="staff_header">
<a v-if="showEdit" v-on:click="showEdit = !showEdit; $emit('clicked', showEdit)"><i class="fal fa-edit"></i>Edit</a>
<a v-if="!showEdit" v-on:click="showEdit = !showEdit; $emit('clicked', showEdit)"><i class="far fa-times"></i>Close</a></i>Close</a>
</div>
The warning is really clear
In your child component, you're trying to mutate the value of showEditHP:
set: function (newValue) {
this.showEditHP = newValue
}
However, this kind of mutating is discouraged. Instead, we should emit an event from the child component and then let the parent component listen to that event and mutate the property itself.
In other words, the property showEditHP belongs to the parent component, well then the parent component should be the one which mutates it.
I am struggling with the following, trying to get a div to show up underneath a text input after someone begins typing in the input:
https://jsfiddle.net/chadcf/3vjn71ap/
The template is:
<div id="app">
<input id="foo"
name="foo"
:value="localValue"
type="text"
placeholder=""
autocomplete="off"
#input="handleInput"
>
<div v-if="show">Testing</div>
</div>
With the following vue code:
new Vue({
el: "#app",
data() {
return {
show: false,
localValue: null
}
},
methods: {
handleInput(e) {
this.show = true;
},
}
});
When you run this, if you type a character in the text input, indeed the div underneath shows up. But in addition the character you just typed vanishes. After that first character though everything works fine.
I think what's going on here is that when the input starts and sets this.show = true, that's happening before the value actually updates. I think... And thus vue re-renders the input but with no value. But I'm not actually sure what to do to handle this properly...
This is happening because localValue isn't being updated by your input. When you start typing show will be set to true, so Vue will update the DOM to show your hidden div. But since localValue is null when the DOM updates your input will be blank since its value is bound to localValue. You can verify this by making handleInput toggle show's value instead of setting it to true and you'll see that every time you type something in the input field the hidden div's visibility will be toggled when the DOM updates but the input will be cleared ..
methods: {
handleInput(e) {
this.show = !this.show;
},
}
So to solve this you'll have to make sure that localValue is being updated by your input. The easiest way is to use v-model ..
<div id="app">
<input id="foo"
name="foo"
v-model="localValue"
type="text"
placeholder=""
autocomplete="off"
#input="handleInput"
>
<div v-if="show">Testing</div>
</div>
JSFiddle
Alternatively you can manually handle the input in your handleInput method and set localValue to the typed value like Austio mentioned in his answer.
Hey so you are pretty close on this thought wise. When you handle input yourself, you have to set the new value when you have new input. In your specific case localValue will always be null which is not what i think you want. I think you are wanting something more like this.
methods: {
handleInput(e) {
this.localValue = e.target.value;
this.show = true;
},
}
About the problem
I am using Laravel 5.6.7, Vue.js. I have modal div which being opened and closed on button click. I type something. Validation fires. I close the modal div. Then clicking button to open it. I see that the validation messages still there.
Component Template
<template>
<div>
<form role="form">
<input name="LastName" type="text" ref="LastName" v-validate
data-vv-rules="required" v-model="createForm.LastName">
<p v-if="errors.has('LastName')">{{ errors.first('LastName') }}</p>
<button v-else type="button" #click="validateBeforeSubmit()">
Create
</button>
</form>
</div>
</template>
Component Script
<script>
export default {
data() {
return {
createForm: {
LastName: ''
}
};
},
created() {
this.InitializeForm();
},
methods: {
InitializeForm() {
this.createForm.LastName = "";
},
validateBeforeSubmit() {
this.$validator.validateAll();
}
}
}
</script>
My findings
if you check the input type text above, I added ref attribute. Tried the below code to set the value to false for aria-invalid attribute.
this.$refs.LastName.setAttribute("aria-invalid", "false");
It sets the attribute value but validation errors are still there. Is there any proper way to get rid of workarounds like above?
I think, when I set the first value or I click on it...some attribute value is being set and due to that form errors occur.
Assuming that you are using vee-validate,
To clear all errors,
this.$validator.errors.clear();
To clear 1 single error only,
this.$validator.errors.remove('LastName');
Add 1 of the code above to the modal close event listener and the error would be gone the next time you opened it..