Search code examples
vue.jsvuexvue-router

Push to vue-router does not change the route


I'm trying to get this example up and running on my laptop by using Typescript, instead of JavaScript. It is a sample "note-taking" application, showing the capabilities of Python+VUE3, authentication included. My GitHub repo is here. Lastly, I had a decent good progress, however, currently I'm stuck in the Register.vue component, when the process of registering is finished and the user should be "pushed" to the dashboard. Here is the code of the "Register.vue" view:

<template>
    <div class="mb-3">
        <label for="username" class="form-label">Username:</label>
        <input type="text" name="username" v-model="username" class="form-control" />
    </div>
    <div class="mb-3">
        <label for="full_name" class="form-label">Full Name:</label>
        <input type="text" name="full_name" v-model="full_name" class="form-control" />
    </div>
    <div class="mb-3">
        <label for="password" class="form-label">Password:</label>
        <input type="password" name="password" v-model="password" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary" @click="submit">Register</button>
</template>

<script lang="ts">
import { ref, defineComponent } from 'vue'
import { useStore } from '../store/index';
import { useRouter } from '../router/index';
import { UsersActionTypes } from '../store/modules/users/action-types';

export default defineComponent({
    name: 'Register',
    setup() {
        const store = useStore();

        const router = useRouter();

        const username = ref('');
        const full_name = ref('');
        const password = ref('');

        const submit = () => {
            console.log("start submit");
            store.dispatch(UsersActionTypes.REGISTER, {
                "username": username.value,
                "full_name": full_name.value,
                "password": password.value
            })
            .then((res) => {
                console.log("res: " + JSON.stringify(res));
                router.push('/dashboard');
            })
            //.catch((reason) => {
            //     console.log("catch error: " + JSON.stringify(reason));
            // })
            //.finally(() => {
            //     console.log("finally error");
            // });
        }

        return {
            submit,
            username,
            full_name,
            password
        };
    }
})
</script>

From the debug logs of the console, everything seems to be executed correctly (user action "register" calls "login", calls "viewme", commits state). I got the impression, that the Register component stops exactly after calling the user action "REGISTER".

The router index.ts files looks like this:

import {
  createRouter,
  createWebHistory,
  RouteRecordRaw,
  Router
} from 'vue-router';

import { useStore } from '../store/index';

import Home from '../views/Home.vue';
import Register from '../views/Register.vue';
import Login from '../views/Login.vue';
import Dashboard from '../views/Dashboard.vue';
import Profile from '../views/Profile.vue';
import Note from '../views/Note.vue';
import EditNote from '../views/EditNote.vue';

const store = useStore();

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: "Home",
    component: Home
  },
  {
    path: '/register',
    name: 'Register',
    component: Register,
  },
  {
    path: '/login',
    name: 'Login',
    component: Login,
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: Dashboard,
    meta: { requiresAuth: true },
  },
  {
    path: '/profile',
    name: 'Profile',
    component: Profile,
    meta: { requiresAuth: true },
  },
  {
    path: '/note/:id',
    name: 'Note',
    component: Note,
    meta: { requiresAuth: true }
  },
  {
    path: '/note/:id',
    name: 'EditNote',
    component: EditNote,
    meta: { requiresAuth: true }
  }
]

let router: Router;

export function useRouter() {
  if (!router) {
    router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes,
    });

    router.beforeEach((to, from, next) => {
      if (to.matched.some(record => record.meta.requiresAuth)) {
        if (store.getters.isAuthenticated) {
          next();
          return;
        }
        next('/login');
      } else {
        next();
      }
    });
  }
  return router;
}

I pushed my changes to my repo and I hope, I'm not to far off the road for getting this example done. Thank you in advance for helping me!


Solution

  • The correct way to use router is to push a config object. Never noticed in the docs the ability to .push('/dashboard') and personally would never do it that way because it can be unfavourable to you at later date.

    You missed the purpose of naming your routes.

    For your /dashboard path you have declared name: 'Dashboard', for this the preferred usage would be:

    router.push({ name: 'Dashboard' })
    

    The same applies to next in navigation guards: next({ name: 'Login' })

    This will save you from ever having to go through all code to update every button, link and router push with new a path should you decide to change it.

    Other than that, if it is still not working, I suggest making sure that you have correctly registered vue-router in new Vue({}) ensuring the router config is indeed loaded.

    I also suggest learning to get the basics working by following the official documentation before implementing something from a 3rd party tutorial to avoid misinformation on correct usages that lead to these mysterious bugs. Most bugs in peoples code come from tutorial sites like that. The authors code is not necessarily 100% accurate and proper.

    If you have a <form> tag on your page with <button type="submit"> and your page is reloading unexpectedly, you need to prevent native form submissions from happing when executing a custom function for a form submit event, the correct usage is as follows:

    <template>
    
      <form @submit.prevent="submit">
    
        <button type="submit">Register</button>
    
      </form>
    
    </template>
    
    <script setup>
    
     function submit () {
       alert('I am a custom submit action.')
     }
    
    </script>
    

    .prevent will cancel native browser submit event action so your custom submit function is triggered without the browser reloading the page unintendedly at the same time.