Component incorrectly outputting content from other component - vue.js

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 :)

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

NuxtJS: styles of inline SVG not exported to resulting CSS

I want to style inline SVG, but NuxtJS (or better say WebPack) doesn't include this styling into output CSS:
<template>
<header>
<NuxtLink to="/" v-html="logoIco" class="logo"></NuxtLink>
</header>
</template>
<script>
export default {
name: 'HeaderComponent',
computed: {
logoIco() {
return require(`assets/images/logo.svg?raw`);
},
},
};
</script>
<style scoped lang="scss">
header {
.logo {
background: red; // this style included in result CSS
svg {
background: yellow; // and this NOT
}
}
}
</style>
WebPack doesn't see any SVG during build and doesn't include header .logo svg rule in resulting CSS.
How this can be done?
I've found an idea here: https://github.com/nuxt-community/svg-module/issues/72
This code works as expected:
<template>
<header>
<NuxtLink to="/" v-html="logoIco" class="logo">
<component :is="require(`assets/images/logo.svg?inline`)"></component>
</NuxtLink>
</header>
</template>
<script>
export default {
name: 'HeaderComponent',
};
</script>
<style scoped lang="scss">
header {
.logo {
background: red; // this style included in result CSS
svg {
background: yellow; // and this now also included
}
}
}
</style>

Is it possible to create a v-class Vue directive that can be overridden by parent just like regular :class prop?

Please see this minimum example
I have a Child.vue and Parent.vue which look like this
<!-- Child.vue -->
<template>
<div :class="'bg-gray text-white'">I'm Child</div>
</template>
<template>
<Child class="p-4" />
</template>
<script>
import Child from "./Child.vue";
export default {
components: {
Child,
},
};
</script>
<style>
.p-4 {
padding: 16px;
}
.bg-gray {
background: gray;
}
.text-white {
color: white;
}
</style>
The final class name for Child.vue wrapper element will be bg-gray text-white p-4, class names are all merged.
However, I would like to create a v-class directive and make my API looks like this
<!-- Child.vue -->
<template>
<div v-class="'bg-gray text-white'">I'm Child</div>
</template>
<script>
export default {
directives: {
class: {
inserted(el, binding) {
const css = String.raw; // Not doing anything right now.
el.className = css`
${binding.value}
`;
},
},
},
};
</script>
And Parent.vue stays the same.
Now the final merged class name is bg-gray text-white, p-4 is missing!
How can I preserve p-4 just like I'm using regular :class prop?
My use case is I want to use the tagged template literal function in my Vue template.
However, Vue doesn't support template literal function in the template, the compiler will yell!
For example
<div :class="css`text`"></div>
Try out to push the value to the class list :
el.classList.add( css`
${binding.value}
`);

Understanding Vue components and routing

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

Changing styles only when a Vue component is rendered

I'm currently using vue-router to send us to an about page like so:
# #/views/About
<template>
<p>About Vue...</p>
</template>
<script>
[...]
</script>
<style>
</style>
The way routes are rendered are like so:
# #/App.vue
<template>
<Header />
<router-view>
</template>
<script>
import ...
</script>
<style>
header {
background: black;
}
</style>
Question:
How would I go about changing the header background color ONLY when I am on the About.vue (if i go to other routes like / or /contact header should stay the same)?
Thank you in advance for the help
Use a watcher or a computed property to watch your this.$route then conditionally apply a class or style based on if you're currently on the /about route. For example ..
# #/App.vue
<template>
<Header :class="[altBackground ? 'header-red' : 'header-black']" />
<router-view>
</template>
<script>
import ...
computed: {
altBackground() {
const path = this.$route.path.split('/')
return path[path.length-1].toLowerCase() === 'about'
}
}
</script>
<style>
.header-black {
color: black;
}
.header-red {
color: red;
}
</style>