Search code examples
vue.jsvuejs2vuexsingle-page-applicationsidebar

VueJS Update Sidebar Component


I've got a problem for updating a component. I explain it:

I have a Single Page Application with VueJS and Vuex. I've got my App.vue like this:

    <template>
  <div class="wrapper">
   <Sidebar></Sidebar>
  </div>
</template>

<script>

import Sidebar from "../components/Menu/Sidebar";
export default {
  name: 'App',
  components:{Sidebar},
  props:['user'],
  updated () {
  console.log(updated)
  }
}
</script>

<style>

</style>

My sidebar component like this:

  <div >
    <nav id="sidebar">
      <div id="dismiss">
        <i class="fas fa-arrow-left"></i>
      </div>
      <div class="sidebar-header">
        <h3>
          <router-link
              :to="{name:'home'}"
          >
            Gamer-Seek App
          </router-link>
        </h3>
      </div>

      <div class="components" v-if="user">
        <div class="d-flex">
          <router-link
              :to="{name: 'user_show', params:{id: user.id}}"
          >
            <img
                :src="user.image" alt="user-avatar"
                class="d-block"/>
          </router-link>
          <span class="pt-4 pl-3">Hello,<br> {{ user.username }}</span>
        </div>
      </div>
      <div v-else>
        <ul id="btn-content" class="d-flex  list-unstyled justify-content-between  components">
          <li><router-link
              :to="{name:'login'}"
          ><i class="fas fa-sign-in-alt"></i> Login
          </router-link></li>
          <li><router-link
              :to="{name:'register'}"
          ><i class="fas fa-registered"></i> Register
          </router-link></li>
        </ul>
      </div>
      <ul class="list-unstyled components">
        <li>
          <router-link
              :to="{name:'home'}"
          >
            <i class="fas fa-home"></i> Home
          </router-link>
        </li>

        <li>
          <a href="#pageSubmenu" data-toggle="collapse" aria-expanded="false" class="dropdown-toggle"><i
              class="fas fa-gamepad"></i> Games</a>
          <ul class="collapse list-unstyled" id="pageSubmenu">
            <li>
              <a href="#">Seek games</a>
            </li>
          </ul>
        </li>
        <li>
          <router-link
              :to="{name: 'about'}"
          >
            <i class="fas fa-address-card"></i> About
          </router-link>
        </li>
        <li>
          <a v-if="user" class="btn btn-danger"
             @click.prevent="logout"
          >Logout</a>
        </li>
      </ul>
    </nav>
    <div id="content">
      <menu-app></menu-app>
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
import Menu from "../Menu/Nav";

export default {
  name: 'Sidebar',
  components: {
    'menu-app': Menu
  },
  data () {
    return {
      user: ''
    }
  },
  methods: {
    logout: function () {
      this.$store.dispatch('logout')
          .then(() => {
            this.user = false;
            this.$router.push('/login')
          })
    },

  }
}
</script>

My store module:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';
import setAuthorizationToken from "../module/axiosHeader";


Vue.use(Vuex)

export const store = new Vuex.Store({
    state: {
        status: '',
        token: localStorage.getItem('token') || '',
        user: localStorage.getItem('user') || {}
    },
    mutations: {
        auth_request(state) {
            state.status = 'loading'
        },
        auth_success(state, token) {
            state.status = 'success'
            state.token = token
        },
        auth_user(state, user){
            state.user =user
        },
        auth_error(state) {
            state.status = 'error'
        },
        logout(state) {
            state.status = ''
            state.token = ''
        },
    },
    actions: {
        login({commit}, user) {
            return new Promise((resolve, reject) => {
                commit('auth_request')
                axios({url: '/api/login_check', data: user, method: 'POST'})
                    .then(resp => {

                        const token = resp.data.token.token
                        const user = resp.data.user

                        localStorage.setItem('token', token)
                        localStorage.setItem('user', JSON.stringify(user))
                        localStorage.setItem('authenticated', 'success')

                        axios.defaults.headers.common['Authorization'] = 'Bearer ' + token

                        commit('auth_success', token)
                        commit('auth_user', user)
                        resolve(resp)
                    })
                    .catch(err => {
                        commit('auth_error')
                        localStorage.removeItem('token')
                        reject(err)
                    })
            })
        },
        register({commit}, user) {
            return new Promise((resolve, reject) => {
                commit('auth_request')
                axios({url: '/api/register', data: user, method: 'POST'})
                    .then(resp => {

                        const token = resp.data.token
                        const user = resp.data.user

                        localStorage.setItem('token', token)
                        setAuthorizationToken(token)

                        commit('auth_success', token)
                        commit('auth_user', user)
                        resolve(resp)

                    })
                    .catch(err => {
                        commit('auth_error', err)
                        localStorage.removeItem('token')
                        reject(err)
                    })
            })
        },
        logout({commit}) {
            return new Promise((resolve, reject) => {
                commit('logout')
                axios({url: '/api/logout'}).then(() => {

                        localStorage.clear();
                        setAuthorizationToken();
                        resolve()
                    }
                ).catch(error => {
                        reject(error)
                    }
                )
            })
        }

    },
    getters: {
        isLoggedIn: state => !!state.token,
        authStatus: state => state.status,
        userInfo : state => state.user
    }
})

And to finish my instance of Vue:

import Vue from 'vue';
import router from '../routes';
import App from '../views/App';
import {store} from "../stores/authentication-store";
import Axios from "axios";
import setAuthorizationToken from "./axiosHeader";

export default class VueClass {
    static init() {

        let hours = 24; // Reset when storage is more than 24hours
        let now = new Date().getTime();
        let setupTime = localStorage.getItem('setupTime');
        if (setupTime == null) {
            localStorage.setItem('setupTime', now)
        } else {
            if (now - setupTime > hours * 60 * 60 * 1000) {
                localStorage.clear()
                localStorage.setItem('setupTime', now);
            }
        }

        router.beforeEach((to, from, next) => {
            const publicPages = ['/login', '/register'];
            const authRequired = !publicPages.includes(to.path);
            const loggedIn = localStorage.getItem('user');

            if (authRequired && !loggedIn) {
                return next('/login');
            }

            next();
        })

        Vue.prototype.$http = Axios;
        const token = localStorage.getItem('token')
        if (token) {
            setAuthorizationToken(token)
        }

        new Vue({
                el: '#app',
                store,
                router,
                template: '<App />',
                components: {App},
                data: {
                    user: ''
                },
                created: function () {
                    this.$http.interceptors.response.use(undefined, function (err) {
                        return new Promise(function (resolve, reject) {
                            if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
                                this.$store.dispatch('logout')
                            }
                            throw err;
                        });
                    });
                },
            }
        )
    }
}

My question is, how can I update my sidebar when the user is logged-in or they're logged-out. I want to show their profile when they're logged-in and show login/register otherwise.


Solution

  • There are different ways to achieve this. In this case since sidebar and user status are global properties, I'd use a global event to handle it. To do so you could try out the Vue Event Bus method:

    Create event-bus.js with these lines in it:

    import Vue from 'vue';
    export const EventBus = new Vue();
    

    Import it in app.js:

    import { EventBus } from './event-bus.js';
    

    Then you could use it this way.

    Emitting an event (when the user logs in or they log out):

    EventBus.$emit('userLoggedIn', this.user);
    

    (you could also set the user in the store instead of passing it as a parameter.)

    Or

    EventBus.$emit('userLoggedOut');
    

    And you could listen for this event in any relevant component (e.g. your Sidebar component) like this:

    Import the event-bus:

    import { EventBus } from '../event-bus.js';
    

    And then something like:

    created() {
        EventBus.$on('userLoggedIn', () => {
            // do whatever necessary
        });
    
        EventBus.$on('userLoggedOut', () => {
            // do whatever necessary
        });
    }