Using vue-router programmatically - vue.js

I'm having issues when trying to use vue-router programatically.
When I use <router-link> in my HTML it works no problem but as soon as I try to use this.$router.push I have no luck. Below is a minimum viable snippet.
main.js
import Vue from 'vue';
import App from './App';
import router from './router';
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>',
});
router index.js
import Vue from 'vue';
import Router from 'vue-router';
// Pages
import Home from '#/components/pages/Home';
import CalculatorSuite from '#/components/pages/calculator-suite/CalculatorSuite';
Vue.use(Router);
export default new Router({
routes: [
{ path: '/', name: 'Home', component: Home },
{ path: '/calculator-suite', component: CalculatorSuite},
],
});
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
Home.Vue
<template>
<div class="home-parent">
// Works
<router-link to="/calculator-suite">Test</router-link>
//Doesn't work
<button :onClick="change">Test</button>
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
change() {
// Do stuff before changing
this.$router.push('/calculator-suite');
},
},
};
</script>
What can I do to allow this to work?

You have a problem on the listener of "click" event. You are biding a value when you should be doing #click instead of :onClick to listen to the click event.
More info about binding: https://v2.vuejs.org/v2/guide/forms.html#Basic-Usage
More info about event handling: https://v2.vuejs.org/v2/guide/events.html

Instead of onclick you need to replace it with #click or v-on:click like as below
<button #click="change">Test</button> // shorthand
or
<button v-on:click="change">Test</button>
You can also use href tag to go to desired page.like as below
<button>Test</button>

Related

Vue-router: do i need to define the default route for vue-router

Experimenting with vue-router, when i click on <router-link to="/about">Go to About</router-link> it doesnt redirect me to anywhere so for example: if i was on localhost:8080/#/ and i click the Go to About button, i still remain on localhost:8080/#/. Also if i change the url directly to localhost:8080/#/about nothing happens (ie no change to the website as if i am still on localhost:8080/#/)
The code:
/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path: '/about',
component: () => {
import ('../views/About.vue');
}
}
]
const router = new VueRouter({
routes
})
export default router;
./App.vue
<template>
<div>
<div id='nav'>
<router-link to="/about">Go to Foo</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
//
},
data: () => ({
//
}),
};
</script>
./views/About.vue
<template>
<p>Testing out vue-routes</p>
</template>
./main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import store from './store';
import router from './router';
Vue.config.productionTip = false;
new Vue({
vuetify,
store,
router,
render: h => h(App)
}).$mount('#app')
I tried running the following code. it gave no error on serving. (I am also using vuetify and vuex but i dont think that is the problem?)
EDIT
I have edited the code such that it works by creating another vue app and using the default that vue has provided with but i cant seem to understand why the edited code works and why the previous code ^^ above didnt. It seems like the reason was that the default localhost:8080/#/ needs to have its own route in ./App.vue as well and is not there by default? can someone confirm this (can't seem to find something like this explanation online?)
Revised working code:
./App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
./views/Home.vue
<template>
<h1>This is the home page</h1>
</template>
<script>
export default {
name: 'Home',
components: {
//
},
data: () => ({
//
}),
};
</script>
./main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import store from './store';
import router from './router';
Vue.config.productionTip = false;
new Vue({
vuetify,
store,
router,
render: h => h(App)
}).$mount('#app')
./router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router';
import Home from "../views/Home.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/about",
name: "About",
component: () =>
import("../views/About.vue")
}
];
const router = new VueRouter({
routes
})
export default router;
./views/About.vue
<template>
<p>Testing out vue-routes</p>
</template>
You need to have a <router-view/> where the routes are going to resolve.
You can read more about it here: documentation

Vue2 - how to recreate component?

How can I recreate a whole component after button click.
Let say I'm in Component "UserPanel" and there is a button call "Refresh".
When I click that button I would like to destroy component and create it from the scratch. I don't wont to use option like "vm.$forceUpdate()" because it doesn't help in my case.
Is it any way to do it?
My app code:
App.vue:
<template>
<div id="main-cont">
<NavBar></NavBar>
<router-view></router-view>
</div>
</template>
<script>
import NavBar from './components/TopBar/NavBar';
import {mapActions,mapGetters} from 'vuex';
import axios from 'axios';
export default {
name: 'App',
components: {
NavBar,
},
computed:{
...mapGetters(['isLoggedIn'])
},
methods:{
...mapActions(['loadLanguage','setToken','setUserLogged','loadUserProfile'])
},
created(){
this.loadLanguage();
this.setToken();
let userLoggedIn = document.head.querySelector('meta[name="logged"]').content;
if(userLoggedIn){
this.setUserLogged();
this.loadUserProfile();
}
}
}
</script>
<style scoped>
#main-cont{
height: 100%;
}
</style>
main.js:
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueCookie from 'vue-cookie';
import store from './store';
import App from './App';
//Components
import Main from './components/main/Main';
import UserRegister from './components/user/UserRegister';
import ResetPassword from './components/user/ResetPassword';
import UserEdit from './components/user/UserEdit';
import UserView from './components/user/UserView.vue';
import GameMain from './components/game/GameMain';
import GamesList from './components/main/GameList';
import Hall from './components/main/Hall';
import Language from './components/main/Language';
import GameCreate from './components/game/GameCreate';
//Plugins
import langPlugin from './langPlugin';
import VTooltip from 'v-tooltip';
Vue.use(VueRouter);
Vue.use(VueCookie);
Vue.use(langPlugin);
export const router = new VueRouter({
mode: 'history',
routes: [
{path: '/', component: Main},
{path: '/user-register', component: UserRegister},
{path: '/user-edit', component: UserEdit},
{path: '/password-reset', component: ResetPassword},
{path: '/user', component: UserView},
{path: '/game', component: GameMain},
{path: '/game-create', component: GameCreate},
{path: '/games-list', component: GamesList},
{path: '/hall-of-fame', component: Hall},
{path: '/language', component: Language},
]
});
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
Component to Reload.
GameCreate:
<Template>
<div>
<button #click="reloadThisComponent"></button>
</div>
</Template>
<script>
export default{
name: 'GameCreate',
methods:{
reloadThisComponent(){
}
}
}
</script>
Thank you.
EDIT (with the new question details) : Since you're using view router and your component is registred as a route, juste simply add the following to your rebuild method in your Game component this should works fine
this.$router.go(this.$router.currentRoute)
const router = new VueRouter({
mode: 'history',
})
new Vue({
router,
el: '#app',
methods: {
reload: function() {
this.$router.go(this.$router.currentRoute)
}
},
created() {
console.log("Hey")
}
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<button #click="reload">Reload</button>
<router-view></router-view>
</div>
The simple way to get to the goal is to set a boolean in v-if on your component. Then switch true/false the boolean. When v-if is false the component is destroyed and will be reinstanciate after.
To do this, there is two way. What we want is to change de state of the parent component that will say if we print our component or not. The first way to do it is by using a State Management plugin like VueX, but it's a little bit too much for what we simply want to do. To be simpliest, we have to trigger an event from your component, that will trigger the state change on the parent.
In the exemple bellow, when you click on the reset button inside MyComponent,custom event named "reset" is emitted. In the parent component, we have a showMyComponent boolean on our MyComponent tag and a listener #reset that will trigger the method named "resetMyComponent" when the event "reset" is emmited by our MyComponent.
Here is a few resources :
What is VueX and State Management if you want know more about the first way to do it
Documentation about Custom Events in VueJS
Doc about Reactivity in Deep, not important here but it's for the explaination of this.nexttick usage here
Hope it's more clear now
var MyComponent = Vue.component('my-component', {
name : "my-component",
template : "#my-component-template",
data(){
return {
interval : null,
count : 0
}
},
created() {
console.log("MyComponent is created")
this.interval = setInterval(() => {
this.count++
},1000)
},
destroyed() {
console.log("MyComponent is destroyed")
clearInterval(this.interval)
}
});
new Vue({
el: "#app",
components : {
MyComponent
},
data: {
showMyComponent : true
},
methods : {
resetMyComponent() {
this.showMyComponent = false;
Vue.nextTick(() => {
this.showMyComponent = true;
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<my-component v-if="showMyComponent" #reset="resetMyComponent"></my-component>
</div>
<script type="text/x-template" id="my-component-template">
<div style="padding: 15px">
<p>My component is created since {{count}} seconds</p>
<button #click="$emit('reset')">Reset my component</button>
</div>
</script>
There are multiple ways of recreating components. The most efficient way is to change the component key. What we do here is we will supply a key attribute so Vue knows that a specific component is coupled or tied to a specific piece of data. If the key stays the same, it won't change the component, but if the key changes, Vue knows that it should get rid of the old component and re-create a new one.
Here is a very basic way of doing it:
<template>
<component-to-re-render :key="componentKey" />
</template>
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
Every time that forceRerender is called, our prop componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one. What you get is a child component that will re-initialize itself and “reset” its state. this simple and elegant way is solving the most common challenge we face in the Vue app development!
You can also check other possible ways od doing this: https://medium.com/emblatech/ways-to-force-vue-to-re-render-a-component-df866fbacf47

vue router-view does not work if wrapped in a component

UPDATE:
It magically starts to work later on after I clear browser cache, I do not know what happened before.
I wonder how to include router-view in a component rather than directly use it?
For Example:
main.js
import Vue from 'vue'
import router from "./router"
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App),
router
})
App.vue
<template>
<div>
<page-content></page-content>
</div>
</template>
<script>
import pageContent from "./page-content.vue";
export default {
name: 'app',
data() { return {}; },
components: {
"page-content": pageContent
}
}
</script>
page-content.vue
<template>
<div class="page-content">
<router-view>
</router-view>
</div>
</template>
<script>
export default {
}
</script>
router.js
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [{ path: '/', component: Foo }]
export default new VueRouter({
routes
})
The problem is I can not see foo shown up on page

Vue router link add object in the url

This is my setting:
main.js creates a vue and attaches the component App to an element in the dom
router.js sets the routs
App.vue has the router-view and a few router-links
Problem:
the link <router-link to="/admin">Admin1</router-link> works fine
the link <router-link to="{name: 'admin'}">Admin2</router-link> doesn;t work and adds to the url bar: #/{name: 'admin'}
Am I using the router in the wrong way?
Below my files in details
main.js
import Vue from 'vue'
import router from './router'
import App from './App'
new Vue({
router,
el: '#app',
components: { App },
template: '<App/>',
data: {
}
})
router.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import Marketplace from '#/components/Marketplace'
import Admin from '#/components/Admin'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/marketplace',
name: 'marketplace',
component: Marketplace
},
{
path: '/admin',
name: 'admin',
component: Admin
}
]
})
App.vue
<template>
<div id="app">
<p>
<router-link to="/">Home</router-link>
<router-link to="/admin">Admin1</router-link>
<router-link to="{name: 'admin'}">Admin2</router-link>
</p>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
In order for your to="{name: admin}" to work without adding the char #, do the following inside your router config file.
Also you are supposed to use the v-bind for to="".
Use v-bind:to="{name: 'admin'}" or :to="{name: 'admin'}"
Example:
export default new Router({
mode: 'history',
// whatever you have
})
Source: https://router.vuejs.org/en/essentials/history-mode.html

Can't use router-link inside Component

Ok so I started a basic VueJS with the VueCli.
This is my setup:
App.vue
<template>
<div id="app">
<router-link to="/home"><span>Home</span></router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
Home.vue
<template>
<h1>{{ msg }}</h1>
</template>
<script>
export default {
name: 'home',
data () {
return {
msg: 'HomeView'
}
}
}
</script>
index.js from the router:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/pages/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/home',
name: 'Home',
component: Home
}
]
})
main.js:
import Vue from 'vue'
import App from './App'
import NavigationBar from './components/NavigationBar'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: {App}
})
new Vue({
el: '#navigation-bar',
template: '<NavigationBar/>',
components: {NavigationBar}
})
This works perfectly fine. The routerlink works and display the Home.vue.
However I created a NavigationBar Component:
NavigationBar.vue
<template>
<div><span>Test</span>
<router-link to="/home"><span>Home</span></router-link></div>
</template>
<script>
export default {
name: 'navigationBar',
data () {
return {
}
}
}
</script>
When I now delete the router-link in the App.vue and add the navigation-bar ID I cant see the router-link. But the NavigationBar gets loaded, because I see the Test Span:
App.vue
<template>
<div id="app">
<div id="navigation-bar"></div>
<router-view></router-view>
</div>
</template>
Why doesnt it work? The router-link is just inside a template and I get no error message.