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

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()
},

Related

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

Reusable component/mixin for a form that is disabled during submit in Vue

I have many components that are basically a form that, on submit, makes a request to the server and disables its input elements until a response is received. I'd like to not have to care about this disabling every time and factor it out into something reusable. Is there a good way to do that?
For concreteness, here is a minimal example:
<form v-on:submit.prevent="send">
<fieldset :disabled="isDisabled">
<div>
<label>Name</label>
<input v-model="u.name">
</div>
<div>
<label>Email</label>
<input type="email" v-model="u.email">
</div>
</fieldset>
</form>
As you can see, handling this isDisabled state clutters up the component:
data () {
return {
u: {
name: '',
email: '',
},
isDisabled: false
}
},
methods: {
send: function () {
this.isDisabled = true
api.post('/users/create', {
name: this.u.name,
email: this.u.email
}).then(response => {
this.isDisabled = false
<do something>
}).catch(error => {
alert(error)
this.isDisabled = false
})
}
}
One idea was to make a generic Form component parametrized by the required fields and REST endpoint, passed in by props. However, the forms and their send functions vary considerably, and might also include conditional inputs so this seems difficult to me.
It sounds like you want a mixin, but all it would do is declare the isDisabled data item (which I would recommend you call saving so that it better indicates program state).
Since you set it to false in both the resolve and reject phases of the Promise, you can move it to the finally phase, which would help the perceived clutter a bit.
You could possibly have a directive in your mixin that would find all the form elements in the form and disable them when saving, and re-enable them afterward, so the markup in the template would just be
<form v-disable-elements="saving">
I'm quite happy with the way I ended up doing it. My form component is basically just
<template>
<form v-on:submit.prevent="send">
<fieldset :disabled="submitting">
<slot></slot>
<div class="submit">
<input type="submit" :value="submitText">
</div>
</fieldset>
</form>
</template>
plus some code for displaying error messages. The component is parametrized by props, notably the endpoint to send data to, the payload, and the text in the button.
With slots, the actual form lives in the parent component and the computation of the payload is also done there, it is easy to have forms that contain very different inputs, unlike my first idea of passing the fields, their types and their name in the payload themselves as props. For concreteness, I usually make a computed property like this:
formdata: function () {
return {
endpoint: '/events/create',
submitText: 'Create event',
payload: {
description: this.ev.description,
date: this.date
}
}
}
and pass it with
<sync-form v-bind="formdata">
The send method in the form component takes care of disabling/un-disabling, and emits an ok or err event depending on the response. Optionally, the parent component can pass in a prop that tells the form if the input is valid and can be submitted.
I created a FormContainer.vue which also passes the data as described here https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots
<template>
<form
#submit.prevent="submit">
<slot v-bind:formInfo="formInfo"></slot>
</form>
</template>
<script>
export default {
props: {
onSubmit: Function
},
data () {
return {
formInfo: {
submitting: false
}
}
},
methods: {
async submit () {
try {
this.formInfo.submitting = true
const response = await this.onSubmit()
this.$emit('onSuccess', response)
} catch (err) {
console.log('err', err)
this.$emit('onError', err)
}
this.formInfo.submitting = false
}
}
</script>
In a child I can do
<template>
<form-container
:onSubmit="onSubmit"
#onSuccess="onSuccess"
#onError="onError"
v-slot="{formInfo}">
<input type="text" v-model="email" />
<button>{{ formInfo.submitting ? 'Submitting...' : 'Submit' }}</button>
</form-container>
</template>
<script>
import FormContainer from './FormContainer'
export default {
components: {
FormContainer
},
data () {
return {
email: ''
}
},
methods: {
onSubmit () {
// Your axios or grahql call or whatever
},
onSuccess (res) {
// Whatever you want to do after the submit succeeded
console.log('Submitted!', res)
},
onError (err) {
// Whatever you want to do after the submit failed
console.log('Submit failed!', err)
}
}
</script>

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

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

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.