M Rahi
M Rahi

Reputation: 73

Update a component from another component in Vue2

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

Answers (1)

tao
tao

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

Related Questions