vuejs: Trying to focus the input using v-el directive - vue.js

I am creating a wizard login form where the Mobile Number is first entered and
password is entered next.
Here am trying to focus the password input using
this.$$.passwordInput.focus()
however if am getting the error given below
Uncaught TypeError: Cannot read property 'focus' of undefined
The full code is below
index.html
<div id="login">
<div v-if="flow.mobile">
<form v-on="submit: checkmobile">
<p>
Mobile Number<br>
<input type="text" v-model="mobile_number" v-el="mobileNumber">
</p>
</form>
</div>
<div v-if="flow.password">
<form v-on="submit: checkpassword">
<p>
Password<br>
<input type="password" v-model="password" v-el="passwordInput">
</p>
</form>
</div>
script.js
var demo = new Vue({
el: '#login',
data: {
flow: {
mobile: true,
password: false
}
},
methods: {
checkmobile: function(e) {
e.preventDefault();
this.flow.mobile = false;
this.flow.password = true;
this.$$.passwordInput.focus();
},
checkpassword: function(e) {
e.preventDefault();
}
}
});

Your passwordInput is inside a v-if block, which only gets rendered when you set flow.password to true; However Vue uses asynchronous rendering, so the v-if block will not be rendered immediately. You can use Vue.nextTick to wait until it does:
this.flow.password = true;
var self = this;
Vue.nextTick(function () {
self.$$.passwordInput.focus();
});
Read the guide about async rendering for more details.

In Vue.js 2.x you can create your own directive to focus a field automatically:
Vue.directive('focus', {
inserted: function (el) {
el.focus();
},
update: function (el) {
Vue.nextTick(function() {
el.focus();
})
}
})
Then you can use v-focus attribute on inputs and other elements:
<input v-focus>
Working example: https://jsfiddle.net/LukaszWiktor/cap43pdn/

If you are using vuejs 2, you should read this:
https://v2.vuejs.org/v2/guide/migration.html#v-el-and-v-ref-replaced
--
In this case, in your template:
use ref="passwordInput" instead v-el="passwordInput"
and in your method:
this.$refs.passwordInput.focus()
I hope this help you!

Glad this worked for some of you.
I have no idea why, but after trying every conceivable variation of the accepted answer I could not get the $$.ref property when using v-repeat.
I could only access the newly created dom elements like so:
new Vue({
el: '#reporting_create',
data: {
recipients: {
0: {
fname: null,
lname: null,
email: null,
registration: false,
report: false
}
},
curRec:1
},
methods: {
addRecipient: function(){
event.preventDefault();
this.recipients.$add(
this.curRec,
{
fname: null,
lname: null,
email: null,
registration: false,
report: false
}
);
var num = this.curRec;
this.$nextTick(function () {
console.log(this._children[num].$$.rowrec);
newSwitches.find('.switch').bootstrapSwitch();
})
this.curRec++;
}
}})
html:
<template v-repeat="recipients">
<div class="row" v-el="rowrec">
<div>{{$key}}</div>
</div>
</template>
The addRecipients function was called outside the v-repeat so even the suggested answer here did couldn't help
Not sure if there is an issue with doing it this way but it works and I'm tired.

Vue.js 1 works a bit different.
Example:
<textarea v-el:model_message></textarea>
JS:
this.$els.model_message.focus();

If you are using Vue.js 2.0, you should do the following:
<input type="text" v-model="currentItem.name" ref="txtName">
So you can access this input by using the $refs object:
this.$refs.txtName.focus();
I hope it helps.

Vue v2's documentation uses focus as an example in writing custom directives. All of the needed code is supplied with the example, https://v2.vuejs.org/v2/guide/custom-directive.html. First you must register the directive. The link shows how to register it locally on the component.
// Register a global custom directive called `v-focus`
Vue.directive('focus', {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus the element
el.focus()
}
})
Having done this, you are now able to use v-focus on an element:
<input v-focus>
Like so.

Related

Hide one item from input list that is dynamically generated unless other input from list has value in vue / vuetify

So the process is like this. I have a form that is created from api. I want to show all the inputs except for one. That one will only be shown if the user adds value to another specific input from the form.
Something like this is the idea.
<Form>
<div-for="formItem in state.formItemData">
<template v-if="formItem.one !== ''">
<form-input
v-model="invoiceForm.two"
:key="formItem.id"
ref="two"
></form-input>
</template>
</div>
</Form>
const invoiceForm = computed({
get: () => state.forms.formData,
set: (value) => {
state.forms.formData= value
}
})
The concept in your problem is very simple, you just must make use of a computed property that evaluates if there are values in input 1 and it will rerender the component showing input2.
new Vue({
el: '#app',
data: {
input1: '',
input2: ''
},
methods: {
verification() {
console.log(this.input1);
}
},
computed: {
notEmpty() {
return this.input1 !== '' && this.input1.length > 3;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input #change="verification" type="text" v-model="input1"> <input v-if="notEmpty" type="text" v-model="input2">
</div>
You should use a v-show if you're eventually going to show/hide it when rendering, especially if it's going to be multiple times. The Vue documentation says that the v-if will not be rendered when the initial condition is false.
Source: https://v3.vuejs.org/guide/conditional.html#v-if-vs-v-show

How to run a function in Vue.js when state changes

I'm looking to run a function when the state changes in my Vue app.
In my component I'm able to get the boolean state of isOpen. I'm looking to run a function that adds focus to my form input when the modal opens and isOpen is set to true. I've tried using a watcher but with no luck. I'm opening my modal by calling :class="{ 'is-open': search.isOpen }" in the html and showing it via css. Any help would be most appreciated.
data() {
return {
isFocussed: this.$store.state.search,
//checks the state of isOpen?
}
},
computed: {
search() { return this.$store.state.search },
},
watch: {
isFocussed() {
this.formfocus()
},
},
methods: {
formfocus() {
document.getElementById('search').focus()
},
please check my snippet which shows the good way to work in Vue.js, you can work with refs which is very helpful instead of document.getElementById()
new Vue({
el: '#app',
data: {
isOpen: false,
},
computed: {
},
watch: {
isOpen(){
if(this.isOpen){
this.$nextTick( function () {
this.formfocus();
}
);
}
}
},
methods: {
formfocus(){
this.$refs.search.focus();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="app">
<button v-on:click="isOpen = !isOpen">show modal</button>
<div v-if="isOpen">
<input ref="search" type="text" placeholder="search">
</div>
</div>
EDIT: i have added a conditional if on the watch, i hope this solves the problem
I am not sure what your template looks like but here is how I set focus on a conditional element.
element
<input type="text" class="search-input" v-model="search" :class="{'collapsed-search': hideInput}" placeholder="Enter keyword" ref="search">
notice the ref="search" on the input.
here is the method when the input condition is true
toggleSearch() {
this.hideInput = !this.hideInput
this.search = ''
setTimeout(() => {
this.$refs.search.focus()
}, 1000)
}
this.$refs.search.focus() is called after the element has been fully created which is the purpose of the setTimeout

Is it possible to use a v-model from inside a component template?

Can the v-model syntax be used from inside a Vue component template?
The following works as expected when included directly in an .html
<input type="text" v-model="selected_service_shortname">
Putting the following stuff into a component template does not work.
var service_details = {
template: `
...
<input type="text" v-model="selected_service_shortname">
...
`
};
vm = new Vue({
el: "#app",
components: {
'service-details': service_details
},
Results in vue.min.js:6 ReferenceError: selected_service_shortname is not defined
Changing the template syntax to
<input type="text" v-model="this.$parent.selected_service_shortname">
Seems to halfway work -- changes applied externally to selected_service_shortname appear in the input box as expected. But making changes to the input box directly results in Uncaught TypeError: Cannot convert undefined or null to object
Is what I'm trying to do a supported use case? If so, are there working examples somewhere?
You can implement support for v-model in your component. This is covered in the documentation here.
Here is an example.
var service_details = {
props: ["value"],
template: `
<input type="text" v-model="internalValue">
`,
computed: {
internalValue: {
get() {
return this.value
},
set(v) {
this.$emit("input", v)
}
}
}
};
Basically, v-model, by default, is simply sugar for passing a value property and listening for the input event. So all you need to do is add a value property to your component, and emit an input event. This can also be customized as described in the documentation.
console.clear()
var service_details = {
props: ["value"],
template: `
<input type="text" v-model="internalValue">
`,
computed: {
internalValue: {
get() {
return this.value
},
set(v) {
this.$emit("input", v)
}
}
}
};
new Vue({
el: "#app",
data: {
selected_service_shortname: "some service name"
},
components: {
'service-details': service_details
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<service-details v-model="selected_service_shortname"></service-details>
<hr>
Selected Service Shortname: {{selected_service_shortname}}
</div>
Used in the parent like this:
<service-details v-model="selected_service_shortname"></service-details>

Updating custom component's form after getting a response

I'm trying to load in a Tutor's profile in a custom component with Laravel Spark. It updates with whatever I enter no problem, but the is always empty when loaded.
The component itself is as follows:
Vue.component('tutor-settings', {
data() {
return {
tutor: [],
updateTutorProfileForm: new SparkForm({
profile: ''
})
};
},
created() {
this.getTutor();
Bus.$on('updateTutor', function () {
this.updateTutorProfileForm.profile = this.tutor.profile;
});
},
mounted() {
this.updateTutorProfileForm.profile = this.tutor.profile;
},
methods: {
getTutor() {
this.$http.get('/tutor/current')
.then(response => {
Bus.$emit('updateTutor');
this.tutor = response.data;
});
},
updateTutorProfile() {
Spark.put('/tutor/update/profile', this.updateTutorProfileForm)
.then(() => {
// show sweet alert
swal({
type: 'success',
title: 'Success!',
text: 'Your tutor profile has been updated!',
timer: 2500,
showConfirmButton: false
});
});
},
}
});
Here's the inline-template I have:
<tutor-settings inline-template>
<div class="panel panel-default">
<div class="panel-heading">Tutor Profile</div>
<form class="form-horizontal" role="form">
<div class="panel-body">
<div class="form-group" :class="{'has-error': updateTutorProfileForm.errors.has('profile')}">
<div class="col-md-12">
<textarea class="form-control" rows="7" v-model="updateTutorProfileForm.profile" style="font-family: monospace;"></textarea>
<span class="help-block" v-show="updateTutorProfileForm.errors.has('profile')">
#{{ updateTutorProfileForm.errors.get('profile') }}
</span>
</div>
</div>
</div>
<div class="panel-footer">
<!-- Update Button -->
<button type="submit" class="btn btn-primary"
#click.prevent="updateTutorProfile"
:disabled="updateTutorProfileForm.busy">
Update
</button>
</div>
</form>
</div>
Very new to Vue and trying to learn on the go! Any help is much appreciated!
OK, firstly a bus should be used for communication between components, not within the components themselves, so updateTutor should be a method:
methods: {
getTutor() {
this.$http.get('/tutor/current')
.then(response => {
this.tutor = response.data;
this.updateTutor();
});
},
updateTutor() {
this.updateTutorProfileForm.profile = this.tutor.profile;
}
}
Now for a few other things to look out for:
Make sure you call your code in the order you want it to execute, because you appear to be emitting to the bus and then setting this.tutor but your function uses the value of this.tutor for the update of this.updateTutorProfileForm.profile so this.tutor = response.data; should come before trying to use the result.
You have a scope issue in your $on, so the this does not refer to Vue instance data but the function itself:
Bus.$on('updateTutor', function () {
// Here 'this' refers to the function itself not the Vue instance;
this.updateTutorProfileForm.profile = this.tutor.profile;
});
Use an arrow function instead:
Bus.$on('updateTutor', () => {
// Here 'this' refers to Vue instance;
this.updateTutorProfileForm.profile = this.tutor.profile;
});
Make sure you are not developing with the minified version of Vue from the CDN otherwise you will not get warnings in the console.
I can't see how you are defining your bus, but it should just be an empty Vue instance in the global scope:
var Bus = new Vue();
And finally, your mounted() hook is repeating the created() hook code, so it isn't needed. My guess is that you were just trying a few things out to get the update to fire, but you can usually do any initialising of data in the created() hook and you use the mounted hook when you need access to the this.$el. See https://v2.vuejs.org/v2/api/#Options-Lifecycle-Hooks

Vue.JS: How to call function after page loaded?

I have got next Vue component.
Login as calling Login function.
checkAuth -- is calling checking Authorization status between page refresh.
But how I can call checkAuth without pushing on button?
var GuestMenu = Vue.extend({
props: ['username', 'password'],
template: `
<div id="auth">
<form class="form-inline pull-right">
<div class="form-group">
<label class="sr-only" for="UserName">User name</label>
<input type="username" v-model="username" class="form-control" id="UserName" placeholder="username">
</div>
<div class="form-group">
<label class="sr-only" for="Password">Password</label>
<input type="password" v-model="password" class="form-control" id="Password" placeholder="Password">
</div>
<button type="submit" class="btn btn-default" v-on:click.prevent="sendLoginInfo()">LOGIN</button>
<button type="submit" class="btn btn-default" v-on:click.prevent="checkAuth()">CheckAuth</button>
</form>
</div>`,
methods: {
//hash key-value
sendLoginInfo: sendLoginInfo, // key (anyname) | value -> calling function name (from separate file)
//calling without brackets because we do need return from function, we need just function
checkAuth: checkAuth // restore authorization after refresh page if user already have session!
}
});
I tried to call it's from App:
App = new Vue({ // App -- is need for overwrite global var. Global var need declarated abobe all function, because some it's function is calling from outside
el: '#app',
data: {
topMenuView: "guestmenu",
contentView: "guestcontent",
username: "",
password: "",
},
ready: function() {
checkAuth(); // Here
}
}
)
But it's look like it's calling when not all components are loaded,
function checkAuth() {
// we should NOT send any data like: loginData because after refreshing page
// all filds are empty and we need to ask server if he have authorize session
console.log("Checking if user already have active session");
this.$http.post('http://127.0.0.1:8080/checkAuthorization').then(function(response) {
console.log("server response: ", response.data)
}
}
// ...
}
Here I am getting error:
authorization.js:69 Uncaught TypeError: Cannot read property 'post' of undefined
I tried to do:
{
// ...
methods: { //hash key-value
sendLoginInfo : sendLoginInfo, // key (anyname) | value -> calling function name (from separate file)
//calling without brackets because we do need return from function, we need just function
},
ready()
{
checkAuth()
}
// ...
}
But again got error:
Uncaught TypeError: Cannot read property 'post' of undefined
What I am doing wrong?
Let see mounted() I think it is help
https://v2.vuejs.org/v2/api/#mounted
// vue js provides us `mounted()`. this means `onload` in javascript.
mounted () {
// we can implement any method here like
sampleFun () {
// this is the sample method you can implement whatever you want
}
}
If you need run code after 100% loaded with image and files, test this in mounted():
document.onreadystatechange = () => {
if (document.readyState == "complete") {
console.log('Page completed with image and files!')
// fetch to next page or some code
}
}
More info: MDN Api onreadystatechange
You can use the mounted() Vue Lifecycle Hook. This will allow you to call a method before the page loads.
This is an implementation example:
HTML:
<div id="app">
<h1>Welcome our site {{ name }}</h1>
</div>
JS:
var app = new Vue ({
el: '#app',
data: {
name: ''
},
mounted: function() {
this.askName() // Calls the method before page loads
},
methods: {
// Declares the method
askName: function(){
this.name = prompt(`What's your name?`)
}
}
})
This will get the prompt method's value, insert it in the variable name and output in the DOM after the page loads. You can check the code sample here.
You can read more about Lifecycle Hooks here.
You import the function from outside the main instance, and don't add it to the methods block. so the context of this is not the vm.
Either do this:
ready() {
checkAuth.call(this)
}
or add the method to your methods first (which will make Vue bind this correctly for you) and call this method:
methods: {
checkAuth: checkAuth
},
ready() {
this.checkAuth()
}
Vue watch() life-cycle hook, can be used
html
<div id="demo">{{ fullName }}</div>
js
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
You can call a function on load like this:
methods:{
checkAuth: function() {your logic here}
},
mounted(){
this.checkAuth()
},