Vue - using refs to focus an element target - javascript

When span class="before-click" is clicked,
I want it hidden, and input class="after-click" show up instead.
And the showed up input tag must be on focused!
The problem is when I try to use $refs.afterClick to access that DOM and give it .focus(), an unexpected error shows that .focus() is not a function.
How to solve this issue?
Thanks.
var myApp = new Vue({
el: '#app',
data: {
onEdit: false,
msg: 'Something in here',
},
methods: {
switchAndFocus() {
if(!this.onEdit) {
this.onEdit = true;
this.$refs.afterClick.focus();
}
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<span class="before-click" #click="switchAndFocus()" v-show="!onEdit">{{msg}}</span>
<input type="text" class="after-click" ref="afterClick" :value="msg" v-show="onEdit">
</div>

Wrap the focus event in a nextTick - this will defer the focus event until the DOM has updated and displayed the input.
https://v2.vuejs.org/v2/api/#Vue-nextTick
var myApp = new Vue({
el: '#app',
data: {
onEdit: false,
msg: 'Something in here',
},
methods: {
switchAndFocus() {
if(!this.onEdit) {
this.onEdit = true;
this.$nextTick(function(){
this.$refs.afterClick.focus();
});
}
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<span class="before-click" #click="switchAndFocus()" v-show="!onEdit">{{msg}}</span>
<input type="text" class="after-click" ref="afterClick" :value="msg" v-show="onEdit">
</div>

Related

Vuejs - show autocomplete suggestions while user digit

I have this js fiddle code. I want to create a suggestions autocomplete using vuejs. At the moment I've achived only in part the scope, I have a problem with the suggestions. They will be placed under the user input chars and it's not exactly what I was expecting, I want to do something similar to the autocompleto of a smartphone keyboard where the suggested words will be displayed while the user digit a word. Can anyone help me?
<div id="app">
<textarea id="input" v-model="input" #input="predictWord()"></textarea>
<span id="suggestion" ref="suggestion"></span>
</div>
#app {
.input {
position: relative;
}
#suggestion {
position: absolute;
left: 0;
}
}
Vue prototype code
new Vue({
el: "#app",
data() {
return {
input: null,
t9: null,
words: []
}
},
mounted() {
this.init();
},
methods: {
init() {
axios({
method: 'GET',
url: 'https://raw.githubusercontent.com/napolux/paroleitaliane/master/paroleitaliane/660000_parole_italiane.txt'
}).then( (res) => {
this.words = res.data.split('\n');
this.t9 = Predictionary.instance();
this.t9.addWords(this.words);
});
},
predictWord() {
let suggestion;
this.countChars();
suggestion = this.t9.predict(this.input);
this.$refs.suggestion.innerText = suggestion[0];
},
countChars() {
console.log(this.input.length);
}
}
});
I created a working snippet: simplified it a bit, added a loading state (as the dictionary is quite large), updated the resulting output, so it's not dependent on any DOM element.
new Vue({
el: "#app",
data() {
return {
loading: false,
input: null,
t9: null,
suggestion: [],
}
},
mounted() {
this.init();
},
methods: {
async init() {
this.loading = true
try {
const {
data = ''
} = await axios({
method: 'GET',
url: 'https://raw.githubusercontent.com/napolux/paroleitaliane/master/paroleitaliane/660000_parole_italiane.txt'
})
this.t9 = Predictionary.instance();
const a = data.split('\n').filter(e => e)
this.t9.addWords(a)
} catch (err) {
console.error(err)
} finally {
this.loading = false
}
},
predictWord: _.debounce(function() {
this.suggestion = this.input ? this.t9.predict(this.input) : [];
}, 300),
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios#0.21.1/dist/axios.min.js"></script>
<script src="https://unpkg.com/predictionary/dist/predictionary.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
<div id="app">
<textarea id="input" v-model="input" #input="predictWord" :disabled="loading"></textarea><br />
<span id="suggestion">{{ suggestion.join(', ') }}</span>
</div>
Also added a debounce function, so the prediction doesn't have to run so many times - 300ms is a reasonable delay in my experience.

Vue component get and set conversion with imask.js

I am trying to use iMask.js to change 'yyyy-mm-dd' to 'dd/mm/yyyy' with my component however when I am setting the value I think it is taking the value before the iMask has finished. I think using maskee.updateValue() would work but don't know how to access maskee from my component.
I am also not sure if I should be using a directive to do this.
Vue.component("inputx", {
template: `
<div>
<input v-mask="" v-model="comp_date"></input>
</div>
`,
props: {
value: { type: String }
},
computed: {
comp_date: {
get: function () {
return this.value.split("-").reverse().join("/");
},
set: function (val) {
const iso = val.split("/").reverse().join("-");
this.$emit("input", iso);
}
}
},
directives: {
mask: {
bind(el, binding) {
var maskee = IMask(el, {
mask: "00/00/0000",
overwrite: true,
});
}
}
}
});
var app = new Vue({
el: "#app",
data: {
date: "2020-12-30"
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="https://unpkg.com/imask"></script>
<div id="app">
<inputx v-model="date"></inputx>
Date: {{date}}
</div>
The easiest way you can achieve this is by installing the external functionality on the mounted hook of your Vue component, instead of using a directive.
In this way you can store the 'maskee' object on your component's data object to later access it from the setter method.
Inside the setter method you can then call the 'updateValue' method as you hinted. Then, you can extract the processed value just by accessing the '_value' prop of the 'maskee' object.
Here is a working example:
Vue.component("inputx", {
template: `
<div>
<input ref="input" v-model="comp_date"></input>
</div>
`,
data: {
maskee: false,
},
props: {
value: { type: String },
},
computed: {
comp_date: {
get: function () {
return this.value.split("-").reverse().join("/");
},
set: function () {
this.maskee.updateValue()
const iso = this.maskee._value.split("/").reverse().join("-");
this.$emit("input", iso);
}
}
},
mounted(){
console.log('mounted');
const el = this.$refs.input;
this.maskee = IMask(el, {
mask: "00/00/0000",
overwrite: true,
});
console.log('maskee created');
}
});
var app = new Vue({
el: "#app",
data: {
date: "2020-12-30"
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="https://unpkg.com/imask"></script>
<div id="app">
<inputx v-model="date"></inputx>
Date: {{date}}
</div>

Vue js. Data fields not binding

I have the following definition for the Vue element:
new Vue({
el: "#app",
data: {
latitude1: 'a',
name: 'aa'
},
mounted() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
this.latitude1 = position.coords.latitude;
})
} else {
this.latitude1 = "WTF??"
// this doesn't work either:
// this.$nextTick(() => { this.latitude1 = "WTF??" })
}
},
methods: {
// button works... WTF?!?
doIt() {
this.latitude1 = "WTF??"
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div>{{ latitude1 }}: {{ name }}</div>
<button #click="doIt">Do it</button>
</div>
I can see the location data being populated. The alert displays the latitude but the 2 way binding for the data field latitude1 is not working.
I have tried storing the object state using this and that also did not work.
My html is as follows:
<div class="form-group" id="app">
<p>
{{latitude1}}
</p>
</div>
One of the things to do inside the Vue.js is to use the defined methods for reactive properties changes.
Here is a code I've provided for it:
function error(err) {
console.warn(`ERROR(${err.code}): ${err.message}`);
}
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
new Vue({
el: "#app",
data: {
latitude1: 'a',
name: 'aa'
},
mounted: function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
console.log(position.coords.latitude);
Vue.set(this, 'latitude1', position.coords.latitude);
}, error, options)
}
}
});
I also set error handler and options for the navigator query. For following the results please check the console.

How do I handle click event on the button inside toastr?

I want to handle the button click event inside the toastr. I am using this library: https://www.npmjs.com/package/vue-toastr-2
This is my code:
var app = new Vue({
el: '#app',
data: {
message: 'vue-toastr-2'
},
created: function() {
this.$toastr.success('Click here to fire an event <button #click="clickMe">Hello</button>', 'Title');
},
methods: {
clickMe() {
alert('Clicked');
// write some more code
}
}
})
Basically I want that when clickMe is clicked, my function inside the component should get called. How would I do this?
This is my jsfiddle: https://jsfiddle.net/75154x8w/2/
var app = new Vue({
el: '#app',
data: {
message: 'vue-toastr-2'
},
created: function() {
this.$toastr.success('Click here to fire an event <button onclick="app.clickMe()">Hello</button>', 'Title');
},
methods: {
clickMe() {
alert('Clicked');
}
}
})

trigger event on a bootstrap switch with VueJS 2

I deal with a boostrap switch
In JQuery, it is easy, you can do as doc state:
$('input[name="my-checkbox"]').on('switchChange.bootstrapSwitch', function(event, state) {
console.log(this); // DOM element
console.log(event); // jQuery event
console.log(state); // true | false
});
But in VueJS, I can't bind the element to value:
<div id="vue-instance">
<input type="checkbox" name="my-checkbox" checked #click="log">
</div>
var vm = new Vue({
el: '#vue-instance',
data: {
},
methods:{
log(){
alert('ok');
}
},
mounted(){
$("[name='my-checkbox']").bootstrapSwitch();
}
});
Here is the fiddle: https://jsfiddle.net/xoco70/tfkLkLqw/1/
If you really need to use this, you can simply bind to the change on the mounted life-cycle hook.
var vm = new Vue({
el: '#vue-instance',
data: {
},
methods:{
log(event, state){
alert('ok');
}
},
mounted(){
$("[name='my-checkbox']").bootstrapSwitch();
$('input[name="my-checkbox"]').on('switchChange.bootstrapSwitch', this.log.bind(this));
}
});
I say If you really need to use this because you are mixing the data driven approach of Vue.js with querying the view with JQuery. I also tried coming up with a solution where you could use a Vue binding so you could react to events or changes in the model, but it seems the library does not update the value of the html input when the toggle is switched.
Working Example
var vm = new Vue({
el: '#vue-instance',
data: {
bSwitch: null
},
methods:{
log(event, state){
this.bSwitch = state;
console.log('switch to state ' + state );
}
},
mounted(){
$("[name='my-checkbox']").bootstrapSwitch();
$('input[name="my-checkbox"]').on('switchChange.bootstrapSwitch', this.log.bind(this));
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/bootstrap-switch#3.3.4/dist/js/bootstrap-switch.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-switch#3.3.4/dist/css/bootstrap3/bootstrap-switch.css" rel="stylesheet"/>
<div id="vue-instance">
Switch State: {{bSwitch}}
<br>
<input type="checkbox" name="my-checkbox">
</div>

Categories

Resources