Nuxt.js Layout not being added to routes - vue.js

I'm trying to apply my layout on other pages but it's not added.
it's working fine on IndexPage but it's not in other routers
I have my layout file on layouts/publicLayout.vue
<template lang="pug">
.layout(class="overflow-x-hidden")
NavBarMobileNavBar.relative.z-50(v-if="isMobile")
NavBar(v-else)
Nuxt
Footer
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
name: 'publicLayout'
})
here is the IndexPage code where the layout is applied:
<template lang="pug">
main.bg-gray-50
section#hero.w-full.flex
html/pug code...
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
name: 'IndexPage',
layout: 'publicLayout'
})
I did the same as always on another page called /services but's it's not applied it all:
<template lang="pug">
.w-full.h-96.bg-red-400.text-center
h1 Services
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
layout: 'publicLayout'
})
when I opened the Devtools it shows me this: .nuxtLayoutDefault.vue
instead of : PublicLayout

Now it's working.
I just noticed that I didn't close the </scrpit> tag on /services page

Related

How to properly register a component in a Vue 3 + Composition API app?

I'm trying to build an app with two layout, one with header and everything, one with just the background for some special pages(like login pages).
I've tried the following:
Created 2 view page:
layouts/Default.vue
<template>
<header class="flex justify-around">
<Header class="w-10/12 max-w-screen-lg"></Header>
</header>
<div class="grow h-full flex justify-around">
<div class="bg-white m-5 rounded-lg p-3 w-10/12 max-w-screen-lg shadow-lg">
<slot />
</div>
</div>
</template>
<script setup lang="ts">
import Header from "../components/Header.vue";
</script>
and
layouts/Plain.vue
<template>
<div class="grow h-full flex justify-around">
<div class="bg-white m-5 rounded-lg p-3 w-10/12 max-w-screen-lg shadow-lg">
<slot />
</div>
</div>
</template>
In my router/index.ts, I provide a "meta"
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/login',
name: 'login',
component: LoginView,
meta: { layout: 'plain' },
},
{
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('../views/AboutView.vue'),
},
],
});
In my App.vue: I try to use this to create a component that wraps my routerView:
<script setup lang="ts">
import { computed } from "vue";
import { RouterView, useRoute } from "vue-router";
const route = useRoute();
const layout = computed(() => {
return (route.meta.layout || 'default');
});
</script>
<template>
<div id="root"
class="min-h-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex flex-col">
<component :is="layout">
<RouterView></RouterView>
</component>
</div>
</template>
And more important, in my main.ts file, I did try to register them:
import { createPinia } from 'pinia';
import Vue, { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import './assets/main.css';
import Plain from './layouts/Plain.vue';
import Default from './layouts/Default.vue';
const app = createApp(App);
app.component('default', Default);
app.component('plain', Plain)
app.use(createPinia());
app.use(router);
app.mount('#app');
But when I try to display ANY page, I get this:
Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/vue.js?v=7cc2bcdd' does not provide an export named 'default' (at main.ts:2:8)
So I guess, that app.component('vue-name', component) is not the correct approach, but I can't find how?
The error is unrelated to your component registration, but rather it points to this:
πŸ‘‡ // The requested module 'vue.js' does not provide an export named 'default'
import Vue, { createApp } from 'vue';
In Vue 3, the vue module has no default export, so import Vue from 'vue' would fail. The Vue import also isn't used anywhere in main.ts, so you apparently don't even need it. The solution is to remove it:
import { createApp } from 'vue'; βœ…
demo
According to official docs you should import them inside the App.vue and use them as variables :
<script setup lang="ts">
import { computed } from "vue";
import { RouterView, useRoute } from "vue-router";
import Plain from '../layouts/Plain.vue';
import Default from '../layouts/Default.vue';
const route = useRoute();
const layout = computed(() => {
return route.meta.layout==='plain'? Plain : Default;
});
</script>
<template>
<div id="root"
class="min-h-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex flex-col">
<component :is="layout">
<RouterView></RouterView>
</component>
</div>
</template>
You could use normal script without setup to get globally registered components, by using vite try out vite-plugin-vue-layouts to register your layouts dynamically.

Vuejs import component in another component

I created a dashboard.vue component and I imported table.vue component into it but the table.vue doesn't appear in my web page and in the vue.dev tools.
When I import the table.vue in app.vue there's no issue.
Below my files.
Thanks in advance!
//dashboard.vue
<template>
<div>
<table></table>
</div>
</template>
<script>
import table from "./table";
export default {
name: 'Dashboard',
component: {
table,
}
}
</script>
<style >
</style>
//table.vue
<template>
<div>
<p>Test</p>
</div>
</template>
<script>
export default {
name: 'Table',
}
</script>
You cannot name components the same as reserved HTML elements. Change it to my-table.
You'll need to map it:
components: {
'my-table': table,
}

How to listen to music without pausing, while changing the page?

How could i continue music playback even while changing pages using Vue js?
Principle of operation:
1. The user starts listening to music on page example.com
2. He goes to the example.com/about and the music he started to listen to doesn't stop.
I guess that i have to the music in the parent component as it’s own separate component, but i am not sure.
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
main.js
import Vue from 'vue'
import App from './App'
import VueRouter from 'vue-router'
import About from './components/About'
import Main from './components/main'
Vue.use(VueRouter);
const routes = [
{ path: '/', component: Main },
{ path: '/about', component: About }
];
const router = new VueRouter({
routes,
mode: 'history'
});
new Vue({
el: '#app',
template: '<App/>',
components: { App },
router
}).$mount('#app');
About.vue
<template>
<div id="about">
<h1>The about page</h1>
<br>
<router-link to="/">Back to main page</router-link>
<br>
<audio controls oncontextmenu="return false">
<source src="../bruh.mp3">
</audio>
</div>
</template>
<script>
export default {
name: 'about'
}
</script>
main.vue
<template>
<div id="about">
<h1>The main page</h1>
<br>
<router-link to="/about">Go to about page</router-link>
<br>
<audio controls oncontextmenu="return false">
<source src="../bruh.mp3">
</audio>
</div>
</template>
<script>
export default {
name: 'about'
}
</script>
Create a new component for the audio, and import that into your App.vue and place it outside <router-view></router-view>. E.g:
/components/AudioComponent.vue (can't use Audio.vue because there is already an audio HTML element)
<template>
<audio controls oncontextmenu="return false">
<source src="../bruh.mp3">
</audio>
</template>
<script>
export default {
// maybe take the src filename and controls on/off attribute as props?
// maybe provide some methods for external control via an event bus or shared state
}
</script>
/App.vue
<template>
<div id="app">
<!-- put a shared header here, e.g. links to routes, top bar, sidebar menu, etc -->
<router-view></router-view>
<audio-component></audio-component>
<!-- put a shared footer here -->
</div>
</template>
<script>
import AudioComponent from './components/AudioComponent.vue'
export default {
name: 'app',
components: {
AudioComponent,
}
}
</script>

How to access parents data value in vuejs with the template syntax

In my VueJS app I am using bootstrap-vue and want to use iframe inside a collapsable elements b-collapse. Because of some problems with iframe content and resizing (problem not related here) I found out that if I enable/disable b-embed with conditional rendering it works.
The parent component b-collapse has a data element called show which change its state if toggle is clicked. In my HelloWorld component I want that b-collapse can pass it's show value into the if check of b-embed.
My approach with this.$parent.$data.show isn't working and I am not sure if there is any better way to do so.
<template>
<div>
<b-btn v-b-toggle.logs>
<span class="when-opened">Close</span>
<span class="when-closed">Open</span>
Trace
</b-btn>
<b-collapse id="logs">
<b-embed :src="src" v-if="this.$parent.$data.show"></b-embed>
<div>Data: {{this.$parent.$data.show}}</div>
</b-collapse>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import { Prop, Component, Inject } from "vue-property-decorator";
#Component
export default class HelloWorld extends Vue {
src = "http://localhost:7681/";
}
</script>
Like this:
parent.vue
<template>
<Child :show="myShow"></Child>
</template>
<script>
import Child from './child'
export default {
data () {
return {
myShow: 'StackOverflow'
}
},
components: {Child}
}
</script>
child.vue
<div>
<b-btn v-b-toggle.logs>
<span class="when-opened">Close</span>
<span class="when-closed">Open</span>
Trace
</b-btn>
<b-collapse id="logs">
<b-embed :src="src" v-if="this.$parent.$data.show"></b-embed>
<div>Data: {{this.$parent.$data.show}}</div>
</b-collapse>
</div>
<script>
export default {
props: {
show: {
type: Number,
require: true
}
}
}
</script>
Or use vuex to do this.

render a Vue slot in a layout from component

I'm having problems with a named slot. This seems like it should work. In the code below I'm trying to use a named slot "sidebar". I would expect my sidebar slot content to show up between the Sidebar starts and Sidebar ends text but nothing shows up in that slot. Everything renders in the main slot.
Here's my code.
route...
{
path: "/test",
name: "test",
meta: {
layout: "test-layout"
},
component: () =>
import("#/pages/Test.vue")
},
and App.vue template...
<template>
<div id="app">
<component :is="layout">
<router-view />
</component>
</div>
</template>
and test-layout...
<template>
<div>
<div>
<h1>Sidebar starts</h1>
<slot name="sidebar"/>
<h1>Sidebar ends</h1>
</div>
<div class="container">
<h1>Content starts</h1>
<slot/>
<h1>Content ends</h1>
</div>
</div>
</template>
and page Test.vue...
<template>
<test-layout>
<span slot="sidebar">sidebar slot content {{forecast.todaySummary}}</span>
<div>main slot content {{forecast.currentSummary}}</div>
</test-layout>
</template>
<script>
import api from "#/js/web-services";
export default {
data() {
return {
forecast: null
};
},
created() {
api.getDailyForecast().then(response => {
this.forecast = response.data;
});
}
};
</script>
and the import in my main.js
import TestLayout from "./layouts/test-layout.vue";
Vue.component('test-layout', TestLayout);
Why isn't my sidebar slot working?
UPDATE
If I get rid of the two lines in main.js and add
import TestLayout from "#/layouts/test-layout.vue";
and
export default {
components: { TestLayout },
data() {...
to Test.vue then it works.
In your router file you are using layout: "test-layout" this means what ever comes in your vue component will be rendered in base test-layout.
There are two ways as far as I know to render the layouts.
Do not define layout in router file and on parent component define named slots like this<slot #header></slot><slot #body></slot> then every slot will be rendered within this (test-layout) layout, and then in your each component extend like this <test-layout><template header>Header content</template><template body>Body content</template></test-layout>.
Defining layout in router file like you did, you can not further use in slots in that layout, you can just import other components e.g <template><Component1><Component2> </template>