Reputation: 73
I have 2 components. header and login.
In header
<a v-if="isLoggedIn" href="/#/insurance">
<img src="@/assets/images/user.svg" class="profile_img">
</a>
<a v-else href="/#/login">
<img src="@/assets/images/login.svg" class="profile_img">
</a>
I want to toggle isLoggedIn
true
and false
at header
while a method is called at login
I have tried putting this at login
var userState = new Vue();
userState.$emit('userState', true)
And this on header
updated(){
userState.$on('userState', function(state) {
this.isLoggedIn = state
})
But it is not working. Can you please help me? Thanks
Upvotes: 1
Views: 1016
Reputation: 90068
The typical solution is to use Vuex (which is a state management module). I won't cover Vuex in my answer. I only recommend it. It's robust and you'll end up needing it sooner than you think. Using Vue.observable
(presented below) is only useful in simple cases. When you start needing async
behavior it's probably time to start using Vuex.
Here's the simpler solution: Vue.observable()
:
Declare a Vue.observable()
containing whatever you want to share between the two components. Place it in a file and export it: E.g:
export default Vue.observable({
isLoggedIn: false
})
In any component you want to read or write to that state object, import it:
import userState from '../path/to/userState.js'
If you only want to read from it, use it as a simple computed:
computed: {
isLoggedIn() { return userState.isLoggedIn }
}
If you also want to write to it, use it as a setter + getter computed:
computed: {
isLoggedIn: {
get() { return userState.isLoggedIn; }
set(value) { userState.isLoggedIn = value; }
}
}
That's it, really. Working example:
const userState = Vue.observable({
isLoggedIn: false
});
Vue.component('header-component', {
template: `
<label>
<input type="checkbox" v-model="isLoggedIn">
Logged in
</label>
`,
computed: {
isLoggedIn: {
get() {
return userState.isLoggedIn
},
set(value) {
userState.isLoggedIn = value
}
}
}
});
Vue.component('footer-component', {
template: `<pre v-text="{ isLoggedIn }"></pre>`,
computed: {
isLoggedIn() {
return userState.isLoggedIn
}
}
});
new Vue({
el: '#app'
})
body {
margin: 0;
}
#app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
#app>* {
padding: 1rem;
border: 1px solid #eee;
}
#app main {
flex-grow: 1;
}
#app pre {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div id="app">
<header-component></header-component>
<main>main content here...</main>
<footer-component></footer-component>
</div>
Note: Vue.observable()
was introduced in Vue 2.6
.
If you want this functionality on a prior version, you can use a Vue instance instead of the observable.
Using a full-blown Vue instance instead of a Vue.observable
is also useful when you want access to everything a Vue instance has to offer: watchers, computed, methods - which could be async, etc...
const userState = new Vue({
data: () => ({
isLoggedIn: false
})
});
See it working (in 2.5
):
const userState = new Vue({
data: () => ({
isLoggedIn: false
})
});
Vue.component('header-component', {
template: `
<label>
<input type="checkbox" v-model="isLoggedIn">
Logged in
</label>
`,
computed: {
isLoggedIn: {
get() {
return userState.isLoggedIn
},
set(value) {
userState.isLoggedIn = value
}
}
}
});
Vue.component('footer-component', {
template: `<pre v-text="{ isLoggedIn }"></pre>`,
computed: {
isLoggedIn() {
return userState.isLoggedIn
}
}
});
new Vue({
el: '#app'
})
body {
margin: 0;
}
#app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
#app>* {
padding: 1rem;
border: 1px solid #eee;
}
#app main {
flex-grow: 1;
}
#app pre {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div id="app">
<header-component></header-component>
<main>main content here...</main>
<footer-component></footer-component>
</div>
Upvotes: 1