vue-router not rendering component, but changing url on this.$router.push - vue.js

I'm trying to redirect to a Menu after pressing a button, I have followed this tutorial
but it's not working,
When Pressing my button the url updates, but stays in the same view, it's also adding /#/ to my url instead of following what I coded in routes.js
I'm getting the next error on console
Uncaught (in promise)
NavigationDuplicated {
_name: "NavigationDuplicated",
name: "NavigationDuplicated",
message: "Navigating to current location ("/menu") is not allowed", stack:
When pressing the button the url turns into http://localhost:8080/#/menu instead of
http://localhost:8080/menu
If I manually type the url http://localhost:8080/menu turns into this
http://localhost:8080/menu/#/
Please help, I'm fairly new to vuejs
This is the structure of my project
main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import vuetify from './plugins/vuetify'
import routes from './routes'
import 'roboto-fontface/css/roboto/roboto-fontface.css'
import '#mdi/font/css/materialdesignicons.css'
Vue.config.productionTip = false
Vue.use(VueRouter)
const router = new VueRouter({routes});
new Vue({
render: h => h(App),
router,
vuetify
}).$mount('#app')
App.vue
<template>
<div id="app">
<Home/>
</div>
</template>
<script>
import Home from './views/Home.vue'
import 'material-design-icons-iconfont/dist/material-design-icons.css';
export default {
name: 'App',
components: {
Home
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
my routes.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
import TraceabilityMenu from './views/TraceabilityMenu.vue'
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Home, name: 'home' },
{ path: '/menu', component: TraceabilityMenu, name: 'traceability-menu' },
{path: '/about', component: About, name: 'about'}
]
export default routes;
My Home.vue which is the first view to load(by the App.vue)
<template>
<v-app id="inspire">
<v-app-bar app color="indigo" dark>
<v-toolbar-title>Project Traceability</v-toolbar-title>
<template>
<v-spacer />
<v-btn color="primary" #click="showPopupLogin()" :to="{ name: 'login'}" >Ingresar</v-btn>
</template>
</v-app-bar>
<PopupLogin v-show="showLogin"/>
<v-content>
<v-container
class="fill-height"
fluid
>
<v-row
align="center"
justify="center"
>
<v-col class="text-center">
</v-col>
</v-row>
</v-container>
</v-content>
<v-footer
color="indigo"
app
>
</v-footer>
</v-app>
</template>
<script>
import PopupLogin from '#/components/PopupLogin.vue';
export default {
props: {
source: String,
},
data: () => ({
showLogin : false
}),
components: {
PopupLogin,
},
methods: {
showPopupLogin() {
this.showLogin = !this.showLogin
}
}
}
</script>
The component PopupLogin
<template>
<v-app id="inspire">
<v-content>
<v-container class="fill-height" fluid>
<v-row align="center" justify="center">
<v-col cols="12" sm="8" md="4">
<v-card class="elevation-12">
<v-toolbar color="primary" dark flat >
<v-toolbar-title>Iniciar sesión</v-toolbar-title>
<v-spacer />
<v-tooltip bottom>
</v-tooltip>
</v-toolbar>
<v-card-text>
<!-- Formulario de login-->
<v-form v-model="validForm" ref="formLogin">
<v-text-field
required
label="Usuario"
:rules="nameRules"
name="login"
type="text"
v-model="existingUser.username"/>
<v-text-field
required
id="password"
prepend-icon="lock"
label="Contraseña"
name="password"
type="password"
v-model="existingUser.password"/>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer/>
<v-btn color="primary" #click="loginUser()">Ingresar</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</v-content>
</v-app>
</template>
<script>
export default {
name: 'PopupLogin',
props: {
source: String
},
data: () => ({
validForm : false,
//objetos
existingUser : {}
}),
methods: {
//Funcion que llamara al servicio de login en backend
loginUser() {
this.$router.push({path: '/menu'});
}
}
}
</script>
TraceabilityMenu.vue the view which I'm trying to render after the press of the button Login
<template>
<v-app id="inspire">
<div>RENDER ME!</div>
</v-app>
</template>
<script>
export default {
props: {
source: String,
},
data: () => ({
drawer: null,
}),
}
</script>

On your main.js file try changing
const router = new VueRouter({routes});
to
const router = new VueRouter({routes, mode: 'history'});
Edit: Also check if you have included the router-view tag on your root component App.vue.

Related

Opening a modal component with a button in another ccomponent using Vuetify

I have 2 components. Modal and Navbar. I'm trying to open Modal using a button in Navbar using vuex. I have a state called modalIsOpen. This states value changes from false to true when clicked on the button in Navbar but only a blank row is rendered as a modal and modal content is not shown. I could not figure out what is wrong.
At the beginning i thought it was a vuetify v-dialog problem. But ive tried other libraries too. And as i said nothing worked yet.
Here is the components ,app.vue and store.js.
AddUpdateModal.vue:
<template>
<v-dialog>
<v-card width="50%" height="50%">
<v-card-title class="text-h5 grey lighten-2">
Privacy Policy
</v-card-title>
<v-card-text>
Lorem
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" text #click="dialog = false">
I accept
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: "AddUpdateModal",
components: {},
data: () => ({}),
}
</script>
<style>
.v-card {
display: block !important;
}
.v-easy-dialog {
height: 500px !important;
}
</style>
NavBar.vue:
<template>
<div id="Navbar">
<v-row>
<v-btn color="primary" #click.stop="openModal" rounded>
Add / Update
</v-btn>
<v-btn color="primary" #click="closeModal" rounded>
Refresh
</v-btn>
</v-row>
<v-row>
<v-divider class="grey darken-4"></v-divider>
</v-row>
</div>
</template>
<script>
export default {
name: 'NavBar',
components: {},
data: () => ({}),
methods: {
openModal() {
this.$store.commit('openModal');
},
closeModal() {
this.$store.commit('closeModal')
}
},
}
</script>
App.vue:
<template>
<v-app>
<v-container>
<NavBar />
<br>
<div class="parent">
<!-- <AddButton></AddButton> -->
<v-btn #click="konsol">Konsol</v-btn>
<div id="modal" v-if="$store.state.modalIsOpen">
<template>
<AddUpdateModal></AddUpdateModal>
</template>
</div>
<v-row>
<v-col>
<DataTable />
</v-col>
<v-divider class="grey darken-4" vertical inset></v-divider>
<v-col>
<PieChart />
</v-col>
</v-row>
</div>
</v-container>
</v-app>
</template>
<script>
import NavBar from './components/NavBar.vue';
import PieChart from './components/PieChart.vue';
import DataTable from './components/DataTable.vue';
// import AddButton from './components/AddButton';
import AddUpdateModal from './components/AddUpdateModal';
// eslint-disable-next-line no-unused-vars
import store from './store/store'
// import axios from 'axios';
export default {
name: 'App',
components: {
NavBar,
AddUpdateModal,
PieChart,
DataTable,
// AddButton,
},
created() {
this.$store.dispatch("initApp")
},
data: () => ({
}),
methods: {
konsol() {
console.log("modalIsOpen", this.$store.state.modalIsOpen)
}
},
};
</script>
<style>
* {
margin: 5px;
}
.v-dialog__container {
display: unset !important;
position: relative !important;
}
</style>
store.js:
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
binance24HrData: [],
modalIsOpen: false,
},
mutations: {
initPortfolioDetails(state, newCoin) {
state.binance24HrData = newCoin;
},
openModal(state) {
state.modalIsOpen = true;
},
closeModal(state) {
state.modalIsOpen = false;
},
},
actions: {
initApp(context) {
axios
.get("https://api2.binance.com/api/v3/ticker/24hr")
.then((response) => {
context.commit("initPortfolioDetails", response.data);
console.log("Binance", response.data);
});
},
openModal({ commit }) {
commit("openModal");
},
closeModal({ commit }) {
commit("closeModal");
},
},
getters: {
getCoinsDetails(state) {
return state.binance24HrData;
},
},
});
export default store;
main.js:
import Vue from "vue";
import App from "./App.vue";
import vuetify from "./plugins/vuetify";
import store from "./store/store.js";
Vue.config.productionTip = false;
new Vue({
vuetify,
store,
render: (h) => h(App),
}).$mount("#app");
I figured it out: all I had to do was add v-model to v-dialog. I thought it was unnecessary because I already had a v-if that wrapped the component containing the v-dialog. I assumed that with this requirement fulfilled, it should render the child component, but it didn't because I didn't have v-model in v-dialog.

How can I solve the issue with redirection?

I am setting up a new project on nuxt and I've made one new layout for a login page, and created a page login.
In my default layout I am setting middleware: 'auth' and in my middleware I am checking for a token and if not authenticated I am redirecting the user to the login page.
The funny thing is that when I've just set it up it worked fine but after some time (I tried to go back with my code to find the issue) I started to receive an Error Redirected when going from "/" to "/login" via a navigation guard.
I don't have any redirects but the one in the auth middleware.
What can be a problem here that I cannot see?
// middleware/auth.js
export default ({ app, error, redirect }) => {
const hasToken = !!app.$apolloHelpers.getToken()
if (!hasToken) {
error({
errorCode: 503,
message: 'You are not allowed to see this'
})
return redirect('/login')
}
}
// layouts/default.vue
<template>
<v-app dark>
<v-navigation-drawer
v-model="drawer"
:mini-variant="miniVariant"
:clipped="clipped"
fixed
app
>
<v-list>
<v-list-item
v-for="(item, i) in items"
:key="i"
:to="item.to"
router
exact
>
<v-list-item-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-app-bar :clipped-left="clipped" fixed app>
<v-app-bar-nav-icon #click.stop="drawer = !drawer" />
<v-btn icon #click.stop="clipped = !clipped">
<v-icon>mdi-application</v-icon>
</v-btn>
<v-toolbar-title v-text="title" />
<v-spacer />
</v-app-bar>
<v-main>
<v-container>
<nuxt />
</v-container>
</v-main>
<v-footer :absolute="!fixed" app>
<span>© {{ new Date().getFullYear() }}</span>
</v-footer>
</v-app>
</template>
<script>
export default {
middleware: ['auth'],
data() {
return {
clipped: false,
drawer: true,
fixed: true,
items: [
{
icon: 'mdi-apps',
title: 'Welcome',
to: '/',
},
{
icon: 'mdi-account-group-outline',
title: 'Clients',
to: '/clients',
},
{
icon: 'mdi-briefcase-check-outline',
title: 'Orders',
to: '/orders',
},
{
icon: 'mdi-briefcase-clock-outline',
title: 'Pending Orders',
to: '/pending-orders',
},
],
miniVariant: false,
right: true,
rightDrawer: false,
title: 'Title',
}
},
}
</script>
// layouts/login.vue
<template>
<v-app dark>
<v-main>
<v-container>
<nuxt />
</v-container>
</v-main>
</v-app>
</template>
<script>
export default { }
</script>
// pages/login.vue
<template>
<div>test login</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
Sorry) apparently I removed layout property from the login page component

Integration of Socket.IO with VUE Framework

Hello I am trying to implement socket.io with vue.js however I cannot access the emits in the different views.
After configuring main.js I can get emits on the APP, but I can't get them on HOME.
Is it possible to help with this situation.
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import vuetify from "./plugins/vuetify";
import * as io from "socket.io-client";
import VueSocketIO from "vue-socket.io";
Vue.use(
new VueSocketIO({
debug: true,
connection: io("http://localhost:3001"),
})
);
Vue.config.productionTip = false;
new Vue({
router,
store,
vuetify,
render: (h) => h(App),
}).$mount("#app");
Here I have the implementation of the first view, and here I have access to the socket.io emit!
<template>
<v-app id="inspire">
<v-app-bar app color="white" flat>
<v-container class="py-0 fill-height">
<v-avatar class="mr-10" color="grey darken-1" size="32"></v-avatar>
<v-btn v-for="link in links" :key="link" text>
{{ link }}
</v-btn>
<v-spacer></v-spacer>
<v-responsive max-width="260">
<v-switch
v-model="switchLogIn"
label="login"
color="success"
hide-details
></v-switch>
</v-responsive>
</v-container>
</v-app-bar>
<v-main class="grey lighten-3">
<v-container>
<v-row>
<v-col cols="2">
<v-sheet rounded="lg">
<v-list color="transparent">
<v-list-item v-for="n in 5" :key="n" link>
<v-list-item-content>
<v-list-item-title> List Item {{ n }} </v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider class="my-2"></v-divider>
<v-list-item link color="grey lighten-4">
<v-list-item-content>
<v-list-item-title to="/about">
{{ nextSip["Estado Processo"] }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-sheet>
</v-col>
<v-col>
<v-alert icon="mdi-database-outline" prominent text type="info">
FILA DE TRABALHO COM {{ workFlowSize }}.
</v-alert>
<v-sheet min-height="70vh" rounded="lg" v-if="switchLogIn">
<router-view></router-view>
</v-sheet>
</v-col>
</v-row>
</v-container>
</v-main>
</v-app>
</template>
<script>
export default {
data: () => ({
links: ["Dashboard"],
switchLogIn: false,
workFlow: "",
workFlowSize: 0,
nextSip: Object,
}),
sockets: {
connect: function () {
this.$swal("Conectado com Sucesso", "Servidor Localhost:3001", "success");
console.log("socket connected");
},
workFlowSize(data) {
this.workFlowSize = data;
},
nextSip: (data) => {
console.log(data);
},
},
watch: {
switchLogIn: function (value) {
if (value == true) {
console.log("Im LOGIN");
}
},
},
};
</script>
but not here
<template>
<div class="home">
<SipForm/>
</div>
</template>
<script>
import SipForm from "#/components/SipForm.vue";
export default {
name: "Home",
components: {
SipForm,
},
data: () => ({
workFlowSize: 0,
}),
sockets: {
connect: function () {
this.$swal("Conectado com Sucesso", "Servidor Localhost:3001", "success");
console.log("socket connected");
},
workFlowSize(data) {
this.workFlowSize = data;
},
nextSip: (data) => {
console.log(data);
},
},
};
</script>

Vue Component not rendering when clicking v-btn with route assigned

I am having trouble getting a component to load.
I have a Navbar with a Drawer with routes to each main page of my web app.
So far I have been able to make a page titled "Weapons.vue" which shows a list of weapon Categories in a video game ( such as Sniper Rifles and Handguns ). I use the "data()" to pass in names, images, and routes. Clicking on one of these weapons category v-btn brings me to a page which displays weapons within a category. In this question, I will be talking about "Assault-rifles.vue".
The issue I am having is that I am able to get to the Assault-Rifles.vue page whenever I click the v-tn with the assigned routes, but when I try to do the same thing within the Assault-rifles.vue page , by using v-btn and specified routes to get to a specific weapon , will bring me to an empty component. The path I specified shows in the URL, but it isn't displaying my "ADAR.vue" component ( ADAR is a type of weapon in the game ).
Here is my App.vue
<template>
<v-app>
<Navbar />
<v-content class="mt-5--grey mx-5--grey black">
<router-view></router-view>
</v-content>
</v-app>
</template>
<script>
import Navbar from "#/components/Navbar";
export default {
name: "App",
components: { Navbar },
data: () => ({
//
})
};
</script>
Here is my Weapons.vue (where the user should start). Didn't add all the data in the data() to save space.
<template>
<div class="mx-5 mb-5">
<h1 class="green--text ma-5">Weapons</h1>
<v-container class="my-5">
<v-layout row wrap>
<v-flex xs12 s6 m4 lg3 v-for="weapon in WeaponCat" :key="weapon.weapontype">
<v-card class="text-md-center ma-3 grey darken-4">
<v-responsive class="pt-4">
<v-img contain :src="weapon.images"></v-img>
</v-responsive>
<v-card-title class="justify-center">
<div class="heading font-weight-black white--text">{{ weapon.WeaponType}}</div>
</v-card-title>
<v-card-actions class="justify-center">
<v-btn flat class="green black--text ma-3" :to="weapon.route">View Weapons</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
export default {
data() {
return {
WeaponCat: [
{
WeaponType: "Assault Rifles",
images: "/WeaponCatimages/AR.png",
route: "/weapons/Assault-Rifles"
},
.....
]
};
}
};
</script>
Here is my Assault Rifles Category pages ( Assault-Rifles.vue ). Again removed some data to save space.
<template>
<div class="mx-4 Armor">
<h1 class="green--text ma-5">Assault Rifles</h1>
<v-container class="my-5">
<v-layout row wrap>
<v-flex xs12 s6 m4 lg3 v-for="weapon in Weapon" :key="weapon.weapon">
<v-card class="text-md-center ma-3 grey darken-4">
<v-responsive class="pt-4">
<v-img contain :src="weapon.images"></v-img>
</v-responsive>
<v-card-title class="justify-center">
<div class="heading font-weight-black white--text">{{ weapon.Weapon}}</div>
</v-card-title>
<v-card-actions class="justify-center">
<v-btn flat class="green black--text ma-3" :to="weapon.route">View Stats</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
export default {
data() {
return {
Weapon: [
{
Weapon: "ADAR",
images: "/WeaponIMG/AR/ADAR_2-15.png",
route: "/weapons/Assault-Rifles/ADAR"
},
....
]
};
}
};
</script>
Here is my "ADAR.vue" component ( the one that wont render ).
<template>
<div class="mx-4 ADAR">
<h1 class="white--text">ADAR</h1>
</div>
</template>
<script>
export default {};
</script>
Here is my route.js . It is super long so i removed some things...
import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import Weapons from "./views/Weapons.vue";
import AssaultRifles from "./views/AssaultRifles.vue";
import ADAR from "./views/ADAR.vue";
Vue.use(Router);
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "home",
component: Home
},
{
path: "/armor",
name: "armor",
component: Armor
},
{
path: "/weapons",
name: "weapons",
component: Weapons
},
{
path: "/weapons/Assault-Rifles",
name: "Assault Rifles",
component: AssaultRifles
},
{
path: "/Weapons/Assault-Rilfes/ADAR",
name: "ADAR",
component: ADAR
},
]
});
I have tried and tried multiple things such as changing if "/" appears and capitalization and I just cant seem to find the answer anywhere. I would love some help on this please.
Your route path says /Weapons/Assault-Rilfes/ADAR, note the misspell Rilfes.
Also you might want to use lowercase for the whole URL, it looks neater imo.

Vue Random Router Component

{
path: '/link_1',
name: 'link_1',
component: () => import('./views/Link1.vue')
},
It is possible to have it one path like /link_1 but every time when go to this route load different component.
Like: First time when go to /link_1 load Link1.vue and second time when user go to /link_1 load and display Link2.vue.
You can use a combination of watch and <component> to render a dynamic component each time the link is clicked.
For example, this generates 100 components named component1 through component100, rendering one at random each time the <router-link></router-link> is clicked:
Vue.use(VueRouter)
const router = new VueRouter({
routes: [{
path: '/random/:id'
}]
})
const components = Array.from(Array(100), (x, i) => {
return {
name: `component${ i+ 1 }`,
props: ['lorem'],
template: `
<v-card>
<v-card-title>
<v-avatar>
<span class="blue-grey--text headline">${i + 1}</span>
</v-avatar>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-container fluid>
<v-layout justify-center>
<v-flex>
<span class="subheader" v-html="lorem"></span>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
</v-card>
`
}
}).reduce((carry, c) => {
carry[c.name] = c
return carry
}, {})
new Vue({
el: '#app',
components,
router,
computed: {
current() {
return `component${this.cid}`
}
},
data() {
return {
cid: 1,
lorem: 'What mystery does the next page hold?'
}
},
watch: {
'$route': {
handler: function() {
let id = this.cid
while (this.cid === id) {
id = Math.floor(Math.random() * 100) + 1
}
this.cid = id
fetch('https://baconipsum.com/api/?type=all-meat&paras=3&format=html').then(res => res.text()).then(data => {
this.lorem = data
})
}
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
<script src="https://unpkg.com/vue-router#3.0.2/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
<v-toolbar app>
<v-toolbar-items>
<v-btn :to="`/random/${cid}`" color="deep-orange darken-4" dark>Click Me</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-content>
<v-slide-x-transition leave-absolute mode="out-in">
<component :is="current" :lorem="lorem"></component>
</v-slide-x-transition>
</v-content>
</v-container>
</v-app>
</div>