Reputation: 493
I have a select component. I have a input component. When the select component is changed I need to .select() the input component. These are two unrelated components i.e. siblings to the main Vue instance.
Both of these components are composed from templates so using the ref attribute is not an option as per the documentation https://v2.vuejs.org/v2/guide/components.html#Child-Component-Refs
How would I go about doing this? I've composed a simple example in codepen - https://codepen.io/anon/pen/MVmLVZ
HTML
<div id='my-app'>
<some-select></some-select>
<some-input></some-input>
</div>
<script type="text/x-template" id="vueTemplate-some-select">
<select @change="doStuff">
<option value='1'>Value 1</option>
<option value='2'>Value 2</option>
</select>
</script>
<script type="text/x-template" id="vueTemplate-some-input">
<input ref='text-some-input' type='text' value='Some Value'/>
</script>
Vue Code
Vue.component('some-input', {
template: `#vueTemplate-some-input`
});
Vue.component('some-select', {
methods: {
doStuff: function() {
// Do some stuff
//TODO: Now focus on input text field
//this.$refs['text-some-input'].focus(); // Cant use refs, refs is empty
}
},
template: `#vueTemplate-some-select`
});
var app = new Vue({
el: '#my-app'
});
Upvotes: 3
Views: 8438
Reputation: 461
You need to emit a custom event from you select-component to the parent : VueJS Custom Events
Parent component :
<select-component @valueChanged="this.$refs.myInput.focus()"></select-component>
<input type="text" ref="myInput"/>
Select Component :
<select v-model="mySelectValue" @change="$emit('valueChanged', mySelectValue)"></select>
Upvotes: 1
Reputation: 43899
There are several ways you could do this, and the right one depends on what the reasons are for implementing this behavior. It sounds like the value selected is significant to the app, rather than just being relevant to the select component, so it should come from the parent. The parent can also pass that value to the input, which can watch
it for changes and set focus.
That solution might look like this snippet:
Vue.component("some-input", {
template: `#vueTemplate-some-input`,
props: ['watchValue'],
watch: {
watchValue() {
this.$refs['text-some-input'].focus();
}
}
});
Vue.component("some-select", {
props: ['value'],
methods: {
doStuff: function(event) {
this.$emit('input', event.target.value);
}
},
template: `#vueTemplate-some-select`
});
var app = new Vue({
el: "#my-app",
data: {
selectedValue: null
}
});
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id='my-app'>
<some-select v-model="selectedValue"></some-select>
<some-input :watch-value="selectedValue"></some-input>
<div>Selected: {{selectedValue}}</div>
</div>
<script type="text/x-template" id="vueTemplate-some-select">
<select :value="value" @change="doStuff">
<option value='1'>Value 1</option>
<option value='2'>Value 2</option>
</select>
</script>
<script type="text/x-template" id="vueTemplate-some-input">
<input ref='text-some-input' type='text' value='Some Value' />
</script>
If, on the other hand, you really want to know that the select
changed, and not just that the value changed, you could pass an event bus to the input so that the parent could relay the change to its child. That might look like this snippet:
Vue.component("some-input", {
template: `#vueTemplate-some-input`,
props: ['changeBus'],
mounted() {
this.changeBus.$on('change', () => this.$refs['text-some-input'].focus());
}
});
Vue.component("some-select", {
methods: {
doStuff: function(event) {
this.$emit('change', event.target.value);
}
},
template: `#vueTemplate-some-select`
});
var app = new Vue({
el: "#my-app",
data: {
changeBus: new Vue()
}
});
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id='my-app'>
<some-select @change="changeBus.$emit('change')"></some-select>
<some-input :change-bus="changeBus"></some-input>
</div>
<script type="text/x-template" id="vueTemplate-some-select">
<select @change="doStuff">
<option value='1'>Value 1</option>
<option value='2'>Value 2</option>
</select>
</script>
<script type="text/x-template" id="vueTemplate-some-input">
<input ref='text-some-input' type='text' value='Some Value' />
</script>
This keeps things encapsulated: the parent knows only that one child changes and that the other child handles change events. Neither component knows about the other.
Upvotes: 2