Get an element by its ref in vue and give it focus - javascript

I'd like to give focus to a vue input when a button is pressed. My understanding is that I need the html element, which I get by setting a ref value, then saying:
this.$refs.theRefName.$el.focus();
But I'm getting tripped up on the refs part of that expression. See the MRE below where refs contains the correct keys, but the values are empty. As a result .$el is undefined and the expression errs.
I've seen SO articles that deal with this being undefined (not a problem for me) or $refs being empty because the element was conditionally rendered (also, not a problem for me). I've got $refs, but why does it contain empty objects?
Vue.config.productionTip = false;
Vue.config.devtools = false;
var app = new Vue({
el: '#app',
data: {
message: 'World'
},
methods: {
click() {
alert(JSON.stringify(this.$refs))
// here's what I want to do, but I can't because inp2 is empty
// this.$refs.inp2.$el.focus();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1>Why are refs empty objects?</h1>
<input ref="inp1"></input><br/><br/>
<input ref="inp2" placeholder="i want focus on click"></input><br/><br/>
<button #click="click">Click me</button>
</div>
If you can help me get to the html element and give it focus, I'd be much obliged.

Try without $el :
Vue.config.productionTip = false;
Vue.config.devtools = false;
var app = new Vue({
el: '#app',
data: {
message: 'World'
},
methods: {
click() {
this.$refs.inp2.focus();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="app">
<h1>Why are refs empty objects?</h1>
<input ref="inp1" /><br/><br/>
<b-form-input ref="inp2" placeholder="i want focus on click"></b-form-input>
<button #click="click">Click me</button>
</div>

Related

Vue v-model and #keydown in one element

<input
v-model="ticker"
#keydown.enter="addClick($event)"
#keydown="getNames"
I need to solve 1 problem, my v-model ticker I guess works after keydown, therefore when function is called I have into it ticker without 1 symbol, can anyone explain how it works?
getNames() {
console.log(this.ticker);
let filtred = this.coinNames.filter(filtered => filtered.toUpperCase().startsWith(this.ticker.toUpperCase()))
console.log(filtred);
return filtred;
}
Examle:
input: something
output: somethin
You should use #input event to get the current input value :
new Vue({
el: '#app',
data() {
return {
ticker: ''
}
},
methods: {
getNames() {
console.log(this.ticker);
},
addClick(e) {
console.log('enter')
}
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app" class="container">
<input v-model="ticker" #keydown.enter="addClick($event)" #input="getNames" />
</div>

v-on:click is not working for a button on simple Vue app

This is a basic vue.js project where I have added a button on the page. When the button is clicked, the showProduct property is set to either true or false.
The html part is as below
<div id="app">
<button v-on:click="showCheckout">Click Me</button>
</div>
The Vue object is as follows
<script>
const store = new Vue({
el: '#app',
data: {
showProduct:true,
},
methods: {
showCheckout: function() {
this.showProduct = this.showProduct ? false: true;
}
}
});
</script>
Problem: The property is not updated when the button is clicked. I have tried to use return this.showProduct = this.showProduct ? false: true; but the issue remained unsolved.
If I open the Vue dev tools and in the Components tab I click the Root component the value changes once. If the button is clicked again several times the value does not change. It only changes when I first click the button on the page and then click the Root component in dev tools. If the root Root component is clicked twice, the value does not change.
Its working fine. You can run this code snippet here as well to verify
const store = new Vue({
el: '#app',
data: {
showProduct:true,
},
methods: {
showCheckout: function() {
this.showProduct = this.showProduct ? false: true;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-on:click="showCheckout">Click Me</button>
{{showProduct}}
</div>
You actually don't need a method for single assignment. See below snippet
new Vue({
el: "#app",
data() {
return {
showProduct: false
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1 v-if="showProduct" > My product </h1>
<button v-on:click="showProduct = !showProduct">Click Me</button>
</div>

Vue.JS: Why won't parent element receive event? Does parent have to be a custom component?

I am noob to Vue.js. I thought the Events made sense: I assumed they bubbled up through the DOM tree until caught by a parent. But I must be missing something. The following code doesn't work, doesn't generate any errors or warnings, and in the Vue developer tools, I see the event being emitted. What am I missing?
index.html:
<!doctype html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
</head>
<body>
<div id="app" #button-clicky="gotevent">
<div #button-clicky="gotevent">
<h1>{{ info }} {{ counter }}</h1>
<button-component />
</div>
</div>
<script src="./main.js"></script>
</body>
</html>
main.js:
Vue.component('button-component', {
data: function() {
return {
count: 1
}
},
methods:
{
clicky() {
this.$data.count++;
this.$emit('button-clicky');
}
},
template: `<button #click="clicky">Go {{ count }}</button>`
});
const vue = new Vue({
el: '#app',
data: {
info: "this is title.",
counter: 0
},
methods: {
gotevent() {
this.$data.counter++;
alert('The event was heard!'); }
}
});
Can't I listen for an event on any parent level above the component emitting it?
I added the listener on both the direct parent <div> and also the 'app' container, but nothing happens and no errors or warnings are thrown.
I don't think Vue events bubble like DOM ones, you need to catch them on the emitting component, so <button-component #button-clicky="gotevent" />

Bind a vue input that doesn't exist yet to a data property

We are using Vex in our application for all the dialog messages. I have an input in one of the dialogs that is used to filter a list of items.
In my Vue instance, I want to listen for the changes on a data property.
The problem is that Vue doesn't get the changes as I type them in the input box. I am thinking this is due to the fact that the input is added to the DOM after the Vue initialization.
How would I approach this so my Vue instance can listen to the input changes? I've done my best to recreate the scenario in it's most simple form in the snippet below.
new Vue({
el: '#app',
data: {
filter: ''
},
methods: {
openModal: function() {
vex.dialog.open({
input: [
'<h2>Filter</h2>',
'<input v-model="filter" />',
'<p>{{filter}}</p>'
].join('')
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/js/vex.combined.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/css/vex.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-on:click="openModal">Open Modal</button>
</div>
The issue is that you are providing plain html in strings to the vex.dialog.open method. Vue won't have any idea that you have done this and so none of the Vue syntax that you've included in those strings will be interpreted as it would in a Vue component definition.
What you should do is make a Vue component for that input, and then pass that input's element via a ref to the vex.dialog.open method. That way, Vue will have compiled the template before it's used in the dialog.
Here's a simple example:
Vue.component('my-input', {
template: `
<div>
<h2>Filter</h2>
<input v-model="filter"/>
<p>{{filter}}</p>
</div>
`,
data() {
return {
filter: ''
}
}
});
new Vue({
el: '#app',
data() {
return {
modalOpened: false
}
},
methods: {
openModal() {
vex.dialog.open({
input: this.$refs.input.$el
});
this.modalOpened = true;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/js/vex.combined.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/css/vex.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="openModal">Open Modal</button>
<my-input v-show="modalOpened" ref="input"/>
</div>

Why doesn't RxJS Observable work in a Vue.js app div?

In the example below, why is the RxJS Observable in the "whatever" div working, while the one in the app div for Vue.js does not work?
(I know there are modules available at NPM that bridge the Vue.js and RxJS worlds, but I'd still like to know why the simple case below fails while using the "plain old scripts").
You can run this at https://jsfiddle.net/3n1jw35x/ - the debug output is printed to the JS console so you'll have to have that open to observe.
<!DOCTYPE html>
<html>
<head>
<title>Why doesn't RxJS Observable work in Vue.js app div?</title>
<script src="https://unpkg.com/vue"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.3/Rx.js"></script>
</head>
<body>
RxJS Observable works in a random div:
<BR>
<div id="whatever">
<input id="input_in_div_whatever">
</div>
<div id="app">
{{ message }}
<BR>
<input id="input_in_div_app">
</div>
<script>
const input_in_div_whatever = $('#input_in_div_whatever');
const content_whatever$ = Rx.Observable.fromEvent(input_in_div_whatever, 'keyup');
content_whatever$.subscribe(e => {
console.log('input_in_div_whatever works: ' + e.target.value.trim())
});
const input_in_div_app = $('#input_in_div_app');
const content_app$ = Rx.Observable.fromEvent(input_in_div_app, 'keyup');
content_app$.subscribe(e => {
console.log('input_in_div_app - you will never see this: ' + e.target.value.trim())
});
var app = new Vue({
el: '#app',
data: {
message: 'Why does RxJS Observable not work in the Vue.js app div?'
}
})
</script>
</body>
</html>
Vue uses the element you give it as a template and renders it. You're attaching the Rx functions to the element before Vue (re)renders them. If you do the attachment in mounted, it works.
As a general rule, if you're going to have anything besides Vue do DOM manipulation, you should make a wrapper component.
const input_in_div_whatever = $('#input_in_div_whatever');
const content_whatever$ = Rx.Observable.fromEvent(input_in_div_whatever, 'keyup');
content_whatever$.subscribe(e => {
console.log('input_in_div_whatever works: ' + e.target.value.trim())
});
var app = new Vue({
el: '#app',
data: {
message: 'Why does RxJS Observable not work in the Vue.js app div?'
},
mounted() {
const input_in_div_app = $('#input_in_div_app');
const content_app$ = Rx.Observable.fromEvent(input_in_div_app, 'keyup');
content_app$.subscribe(e => {
console.log('input_in_div_app - you will never see this: ' + e.target.value.trim())
});
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.3/Rx.js"></script>
RxJS Observable works in a random div:
<BR>
<div id="whatever">
<input id="input_in_div_whatever">
</div>
<div id="app">
{{ message }}
<BR>
<input id="input_in_div_app">
</div>

Categories

Resources