Understanding Vue components and routing - vue.js

I am learning how to build a website using a Vue frontend and Express REST backend.
I have created new routes as follows:
/path/to/vue-client/routes/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Hello from '#components/HelloWorld';
import Register from '#components/Register';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'hello',
component: Hello
},
{
path: '/register',
name: 'register',
component: Register
}
]
});
/path/to/vue-client/src/components/Register.vue
<template>
<div>
<h1>{{ msg }}</h1>
<p>
This is the registration page.
</p>
</div>
</template>
<script>
export default {
name: 'Register',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
Now, having made these changes, I expect that when I browse to http://localhost:8080/register, I will see the Register page displayed, however, the browser just shows the home (irrespective of what I type in the browser url bar!).
The only way I can get the register page to be shown, is to modify App.vue directly:
/path/to/vue-client/src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Register msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import Register from './components/Register.vue'
export default {
name: 'app',
components: {
Register
}
}
</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>
How do I get a link to another page (implemented as a Single File Component) to work in Vue?

Inside your App.vue you need to add a
<router-view></router-view>
that will render your component (in your case Hello or Register) when matching a specific route.
To generate a link inside a component and then navigate between route/component use
<router-link to="/">Go to Hello component</router-link>
cf VueJS router doc: https://router.vuejs.org/guide/#html

Related

v-dialog isn't rendered after compilation in web component but works in development

I am trying to build a simple web component with Vuetify's Dialog.
Something like this and it would render a vuetify button where x-button is and when it's clicked it would open a vuetify full screen dialog.
<html>
<head>
[script src=".../webcomponent.js"/]
</head>
<body>
Hello world! <x-button/>
</body>
</html>
Everything seems to be working but when I click a button to open the modal, it just doesn't show up. It gives me error like
[Vuetify] Unable to locate target [data-app]
found in
---> <VDialog>
<XButton>
<App>
<Root>
Even though my program has only one data-app="true" property in the root node. Also I when I inspect my dist index.html after generating the webcomponent, I still can see the vuetify dialog element but with display:block;. What point am I be missing? There's no problem whatsoever in development mode.
CustomVuetify.vue
<template>
<div>
<v-btn color="primary" dark #click="showDialog = true">
Open Dialog
</v-btn>
<v-dialog v-model="showDialog" max-width="420">
<div class="dialog">
<v-card>
<v-toolbar color="primary" dark>Hi, i am a dialog</v-toolbar>
<img
src="https://pbs.twimg.com/profile_images/736210171899174917/DPfHmqtM_400x400.jpg"
/>
</v-card>
</div>
</v-dialog>
</div>
</template>
<script>
import { VBtn, VDialog, VCard, VToolbar } from "vuetify/lib";
import vuetify from "../../plugins/vuetify";
export default {
name: "XButton",
vuetify,
components: {
VBtn,
VDialog,
VCard,
VToolbar,
},
data() {
return {
showDialog: false,
};
},
};
</script>
<style lang="scss">
#import "vuetify/dist/vuetify.min.css";
.dialog {
// padding: 10px;
img {
overflow: hidden;
border-radius: 12px;
height: 220px;
object-fit: cover;
margin-top: 10px;
}
}
</style>
App.vue
<template>
<div id="app" data-app="true">
<CustomVuetify />
</div>
</template>
<script>
import CustomVuetify from "./components/CustomVuetify.vue";
export default {
name: "App",
components: {
CustomVuetify,
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from '../plugins/vuetify';
Vue.config.productionTip = false
import wrap from "#vue/web-component-wrapper";
const wrappedElement = wrap(Vue, App);
window.customElements.define("custom-vuetify", wrappedElement);
new Vue({
vuetify,
render: h => h(App),
}).$mount('#app')
Problem's Screenshot

Components which are not defined in SFC are not shown using Vue3 Router?

Now I am trying to create a front-end of a website using Vue3. But now I am having an issue using the Router.
Here's my code.
Main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
router/index.js
import {createRouter, createWebHistory} from 'vue-router'
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
App.vue
<template>
<img alt="Vue logo" src="./assets/logo.png">
<h1>Hello App!</h1>
<p>
<!-- use the router-link component for navigation. -->
<!-- specify the link by passing the `to` prop. -->
<!-- `<router-link>` will render an `<a>` tag with the correct `href` attribute -->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view></router-view>
</template>
<script>
export default {
name: 'App',
}
</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>
There's no content in router-view after rendering. How can I fix it?
I tried out your example here and i got the same behavior as you have but after defining components inside single files they work fine:
import Contact from "./Contact.vue";
import About from "./About.vue";
const Home = { name: "Home", template: "<div>Home</div>" };// this will not be displayed
const routes = [
{ path: "/", name: "Home", component: Home },
{ path: "/contact", name: "Contact", component: Contact },
{ path: "/about", component: About }
];

""TypeError: Cannot read property '$emit' of undefined vue

I want to emit event from "home" component to "about" component. But it throws this exception-"TypeError: Cannot read property '$emit' of undefined".
This is my "main.js"
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
export const bus = new Vue();
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App)
}).$mount("#app");
This is my "Home" component.
<template>
<div class="home">
<input
v-model="text"
class="text-input"
placeholder="Add text to your child component"
/>
<button #click="sendmsg" class="btn-add">Add message</button>
</div>
</template>
<script>
import { bus } from "../main";
export default {
name: "home",
data() {
return {
text: "",
};
},
methods: {
sendmsg() {
bus.$emit("msg", this.text);
this.text = "";
}
}
};
</script>
And this "About" component
<template>
<div class="about"></div>
</template>
<script>
import { bus } from "../main";
export default {
name: "about",
created() {
bus.$on("message", (data) => {
console.log(data);
});
}
};
</script>
Looks like a race-condition brought on by a possible circular dependency (main -> App -> Home -> main).
I would suggest moving your bus to its own module instead of main. See here for an example
// src/event-bus.js
import Vue from 'vue'
export default new Vue()
and then in Home.vue and About.vue
import bus from '#/event-bus'
Also, your About component is listening for the wrong event. Home emits "msg" but About listens for "message". They should use the same event name.
Demo ~ https://codesandbox.io/s/serverless-frog-kvtoj?file=/src/event-bus.js
It's working, but whem the components have their own view it's stop working.
App.vue
<template>
<div class="app">
<router-link to="/"> HOME</router-link>
<router-link to="/about"> About</router-link>
<router-view />
</div>
</template>
<style lang="scss">
.app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
align-items: center;
.btn-add {
width: 10vw;
}
.text-input {
width: 10vw;
}
}
</style>
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",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue")
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;

Component incorrectly outputting content from other component

I am using Gridsome for the first time. I am just trying to setup the layout of my website - I don't have any API connected or anything, just trying to output raw HTML. I have a SkipToContent component and a Header component. Both are outputting the contents of the Header component, and I'm rather confused. I've tried restarting the local server and general debugging stuff like making sure the files are all saved. Below are the files:
Header.vue
<template>
<header class="header">
<strong>
<g-link to="/">{{ $static.metadata.siteName }}</g-link>
</strong>
<nav class="nav">
<g-link class="nav__link" to="/">Home</g-link>
<g-link class="nav__link" to="/about/">About</g-link>
<g-link class="nav__link" to="/web/">Web Development</g-link>
<g-link class="nav__link" to="/photography/">Photography</g-link>
</nav>
</header>
</template>
<script lang="ts">
import Vue from "vue";
export default class Header extends Vue {}
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
</style>
SkipToContent.vue
<template>
Skip to content
</template>
<script lang="ts">
import Vue from 'vue';
export default class SkipToContent extends Vue {}
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
.skip-to-content {
&__link {
position: fixed;
left: 50%;
transform: translate(-50%, -200%);
transition: transform 0.15s ease;
background: $primary;
&:focus {
transform: translate(-50%, 50%);
}
}
}
</style>
Default.vue
<template>
<div class="layout">
<SkipToContent />
<Header />
<main id="main-content">
<slot />
</main>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import SkipToContent from "~/components/SkipToContent.vue";
import Header from "~/components/Header.vue";
import { Component } from "vue-property-decorator";
#Component({
components: {
Header,
SkipToContent,
}
})
export default class App extends Vue {
};
</script>
<static-query>
query {
metadata {
siteName
}
}
</static-query>
<style lang="scss">
#use '../styles/common' as *;
#import url("https://rsms.me/inter/inter.css");
html {
font-family: "Inter", sans-serif;
}
#supports (font-variation-settings: normal) {
html {
font-family: "Inter var", sans-serif;
}
}
</style>
HTML Output:
I feel like I'm either doing something very obvious incorrectly, or this is some sort of issue with using TypeScript in Gridsome?
Thanks for any suggestions :)

Why vue-router does not work? My project is in GitHub

I have tried to access to others pages via url, but those pages never loads
http://localhost:8080/users
http://localhost:8080/#/users
http://localhost:8080/home
http://localhost:8080/#/home
etc
This is my main.js file:
import Vue from 'vue'
import App from './App.vue'
import VueRouter from "vue-router"
Vue.config.productionTip = false
Vue.use(VueRouter);
import Users from './components/Users.vue'
const Home = { template: "<p>home page</p>" };
const Index = { template: "<p>index page</p>" };
const About = { template: "<p>about page</p>" };
const routes = [
{ path: "/users", name: "users", component: Users },
{ path: "/home", name: "home", component: Home },
{ path: "/index", name: "index", component: Index },
{ path: "/about", name: "about", component: About },
];
var router = new VueRouter({
routes: routes,
mode: "history",
});
new Vue({
router: router,
render: h => h(App),
}).$mount('#app')
Does anyone know why vue-router not work?
I created this project with Vue CLI, and then I intalled npm install vue-router, basically i just added a new component in ./components/users and also modified the main.js file, and that's all.
I uploaded my project to GitHub: https://github.com/alex-developer-18x/vueapp
I ran your code and the problem is because you are not using "router-view". Go to your App.vue and add "router-view" component.
Example (Edited from your code):
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<!-- You should add this instead of <HelloWorld/> -->
<router-view></router-view>
</div>
</template>
<script>
// Remove the Hello World import here
export default {
name: 'App',
components: {
// And remove the Hello World component here
}
}
</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>