I'm simply trying to have a page scroll to an anchor point in Nuxt3 and nothing I can do will get it to work. It doesn't scroll on click, or on page refresh with the anchor in the url.
<nuxt-link :to="{path: '/', hash: '#projects'}">Let's go</nuxt-link>
Tried a bunch of other SO answers, adding custom scrollBehaviour code to the nuxtConfig didn't work and trying to install vue-scrollTo in Nuxt3 just gave me this error when running the app with the vue-scrollTo module
ERROR Cannot restart nuxt: serialize is not defined
Any help would be greatly appreciated!
Full code
<script setup>
import '#/assets/css/main.css';
const { data } = await useAsyncData('home', () => queryContent('/').find())
const projects = data
</script>
<template>
<div>
<div class="flex flex-col h-screen">
<div class="lg:p-20 p-10 text-white bg-orange-500">
<p class="font-playfair lg:text-7xl text-4xl mb-5">Kia Ora, my name is <span class="font-medium italic">George Bates</span></p>
<p class="font-lato lg:text-3xl text-xl mb-5">Content creator and web developer in Auckland, New Zealand</p>
</div>
<div class="lg:p-20 p-10 text-white flex flex-grow" style="background-image: url('images/header.jpg'); background-position: center; background-size: cover;">
<nuxt-link :to="{path: '/', hash: '#projects'}">Let's go</nuxt-link>
</div>
</div>
<main class="lg:p-20 p-10" id="projects">
<p class="text-3xl font-playfair mb-5 font-semibold pb-2 text-orange-500">Some of my work</p>
<Projects :projects="projects" />
</main>
</div>
</template>
You said that you already tried to add a custom scrollBehavior, but how did you do that?
I'm very new to Vue & Nuxt, but thanks to this Github answer, you can customize the scroll behavior, and this works for me:
File app/route.options.ts:
import type { RouterConfig } from '#nuxt/schema';
// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
scrollBehavior(to, from, savedPosition) {
return {
el: to.hash,
behavior: 'smooth',
};
},
};
(Here I put a smooth behavior, but default seems to be auto)
And with a sample of code like:
...
<NuxtLink :to="{path: '/', hash: '#anchor'}">Let's go!</NuxtLink>
...
Here is my parent component
<template>
<rooms-card
roomImage="../../assets/images/room3.jpg"
roomType="Duplex Room"
roomDescription="Sami double bed 1 guest room 3 windows"
roomPrice="$50/night"
/>
</template>
Here is the child component
<template>
<div class="m-5 px-6 py-4 shadow-xl border mb-14">
<div class="w-64 h-72 bg-red-700">
<img :src="roomImage" class="h-full"/>
</div>
<div class="">
<h1 class="font-semibold text-xl my-3">{{roomType}}</h1>
<p class="text-sm my-2 text-slate-500 w-44">{{roomDescription}}</p>
<p>Starting from<span class="font-semibold text-xl">{{roomPrice}}</span></p>
<button class="bg-pink-500 my-5 px-6 py-3 text-white">
Book now
</button>
</div>
</div>
</template>
<script setup>
// import { any } from "prop-types";
const props = defineProps({
roomImage:String,
roomType: String,
roomDescription:String,
roomPrice:String
})
console.log(props.roomImage)
</script>
NOTE: even if i use require(#/asset/images......) in my image tag src it wont work because for some reasons vite isn't configure to use requires
Just import the asset in <script> and use the variable as the src attribute.
From the docs
Importing a static asset will return the resolved public URL when it is served:
<script setup>
import RoomImage from "../../assets/images/room3.jpg";
</script>
<template>
<rooms-card
:roomImage="RoomImage"
roomType="Duplex Room"
roomDescription="Sami double bed 1 guest room 3 windows"
roomPrice="$50/night"
/>
</template>
I am using Quasar, and I would like the drawer changes into mini mode automatically whenever the browser gets smaller by users. For now, it's always opened even I narrows the browser.
It's what I tried below:
<q-drawer
v-model="drawer"
show-if-above
:mini="!drawer || miniState"
#click.capture="drawerClick"
:width="220"
:breakpoint="500"
bordered
:content-style="{ backgroundColor: '#f5f7f9' }"
>
<q-scroll-area class="fit">
<q-list padding>
<q-btn
v-if="!miniState"
flat
left
#click="miniState = !miniState"
class="logo-btn"
>
<img
src="~assets/os_logo.png"
width="144px"
height="24px"
contain
/>
</q-btn>
<q-btn v-else flat left #click="miniState = !miniState">
<img src="~assets/os_logo_no_text.png" width="24px" contain />
</q-btn>
<!-- MENU -->
<q-expansion-item
default-opened
v-for="(menu, index) in menus"
:style="index === 0 && 'margin-top: 27px'"
:icon="menu.icon"
:label="menu.title"
:key="menu.id"
:expand-icon="menu.subMenus.length === 0 ? 'none' : ''"
header-class="header-bg text-black"
expand-icon-class="text-gray"
>
<q-expansion-item
v-for="(sub, index) in menu.subMenus"
:key="index"
:label="sub.title"
expand-icon="none"
class="sub-content"
:to="{ name: sub.link }"
/>
</q-expansion-item>
</q-list>
</q-scroll-area>
</q-drawer>
And the script code below :
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
// name: 'ComponentName'
data() {
return {
drawer: false,
miniState: false,
},
computed: { // <-- I tried this one..
miniState() {
if (window.innerWidth < 600) {
return (this.miniState = true);
}
}
},
});
</script>
I have also checked this answers here : How to default navigation drawer to closed on mobile and open on desktop?
But this is not really what I am aiming for since the mode of drawer gets stuck and not changes responsively. Any suggestions?
In your code you have made several mistakes;
You are repeating your data property miniState as a computed property. The miniState property inside the data function will override the computed property; thus it will always be false
You are not returning a value from your computed property; instead you are only assigning.
Still; window properties such as innerWidth is not reactive and watchable in Vue. Please correct me if I am wrong. Thus, watching window.innerWidth will not trigger every time the window is resized.
Since you are using Quasar, you can make use of the the screen plugin which comes with Quasar. You dont have to install anything, the screen plugin is installed by default. Here is the link to the docs.
I have put below a very minimal example of the code with the functionality you required. This is not the code you have put in your post above. I extracted the drawer and the content from the Quasar documentation here
Now the drawer will automatically go into mini mode when the screen size is below 500 px; this is of course configurable.
Also; on a side note, if you are only starting with Vue and Quasar, Vue is now upgraded to Vue3 which comes with the composition api. Quasar is also being upgraded to version 2 which supports Vue3.
Follow the comments below and you will understand the code!
new Vue({
el: '#q-app',
data: function() {
return {
drawer: true,
// using a property to track when to show the mini drawer: this way is easy to maintain.
switchToMini: 500
}
},
computed: {
// use ministate as a computed property
miniState: function() {
// use the screen plugin of Quasar -> this is very handy
return this.$q.screen.width < this.switchToMini
// you can do better and compare agains Quasars default breakpoints; following code checks whether the current screen size is 'sm'. You can comapare against 'xs', 'sm', 'md', 'lg' and 'xl'
// return this.$q.screen.name === "sm"
}
}
})
<!-- quasar and vue includes -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/#quasar/extras/material-icons/material-icons.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.min.js"></script>
<!-- end of includes -->
<!-- app -->
<div id="q-app">
<template>
<div class="q-pa-md">
<q-layout view="hHh Lpr lff" container style="height: 300px" class="shadow-2 rounded-borders">
<q-header elevated class="bg-black">
<q-toolbar>
<q-toolbar-title>Header</q-toolbar-title>
</q-toolbar>
</q-header>
<q-drawer
v-model="drawer"
show-if-above
:mini="miniState"
:breakpoint="200"
:width="200"
bordered
class="bg-grey-3"
>
<q-scroll-area class="fit">
<q-list padding>
<q-item clickable v-ripple>
<q-item-section avatar>
<q-icon name="send" />
</q-item-section>
<q-item-section>
Send
</q-item-section>
</q-item>
</q-list>
</q-scroll-area>
</q-drawer>
<q-page-container>
<q-page padding>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit nihil praesentium molestias a adipisci, dolore vitae odit, quidem consequatur optio voluptates asperiores pariatur eos numquam rerum delectus commodi perferendis voluptate?
</p>
</q-page>
</q-page-container>
</q-layout>
</div>
</template>
</div>
Using bootstrap-vue I have a working example using b-tabs and some other of it's components.
However, on one page (.vue) when I try to wrap the block within a tag the page always renders that card and its contents as 'undefined'
I use the sample from here.
Without the v-card I see:
and with it I see:
The code literally follows the sample referenced in the page above and in my main.ts I have the following
import {Card} from 'bootstrap-vue/es/components'
import { Button } from 'bootstrap-vue/es/components';
import { Collapse } from 'bootstrap-vue/es/components';
import { Alert } from 'bootstrap-vue/es/components';
import { Tabs } from 'bootstrap-vue/es/components';
Vue.use(Tabs);
Vue.use(Alert);
Vue.use(Collapse);
Vue.use(Button);
Vue.use(Card);
new Vue({
router,
store,
components: {
bCard: Card,
bButton: Button,
bCollapse: Collapse,
bAlert: Alert,
bTabs: Tabs,
pagination
},
render: h => h(App)
}).$mount("#app");
Can anyone point me in the right direction even to see how to isolate any underlying problem please?
EXTRA CODE ADDED BELOW
Vue (lang="ts") component which does not display correctly:
<template>
<div class="container-fluid">
<div class="row ml-1">
<h4>Complaint Reference: {{ complaint.ComplaintReference }}</h4>
<div class="col-12"><p>Enter the details of the complaint and click Save</p></div>
<div class="col-12">
<b-alert variant="success"
dismissible
:show="showDismissableAlert"
#dismissed="showDismissableAlert=false">
Your changes have been saved
</b-alert>
</div>
</div>
<div class="row">
<!-- the next line is a separate vue component that uses the same approach and which renders correctly -->
<tabs-view></tabs-view>
<div class="col-md-12">
<b-card no-body> <!-- THIS IS WHAT BREAKS! -->
<b-tabs card>
<b-tab title="Details" active>
<p> in first tab</p>
</b-tab>
<b-tab title="Contact">
<p> in second tab</p>
</b-tab>
<b-tab title="Description">
<p> in third tab</p>
</b-tab>
<b-tab title="Outcome">
<p> in final tab</p>
</b-tab>
</b-tabs>
</b-card> <!-- THIS IS WHAT BREAKS! -->
</div>
</div>
<div class="clearfix"></div>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";
import {Complaint} from "#/components/complaints/Complaint";
import TabsView from '../shared/Tabs.vue'
#Component({
components: {
tabsView: TabsView
}
})
export default class EditComplaintComponent extends Vue {
complaint: Complaint = new Complaint("");
baseUri: string = "api/Complaints/GetByReference/?id=";
showDismissableAlert: Boolean = false;
}
</script>
<style scoped>
</style>
and then another non-TS vue component consumed on the broken one which works correctly with b-tabs inside b-card
<template>
<div>
<b-card title="Card Title"
img-src="https://lorempixel.com/600/300/food/5/"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2">
<!--<p class="card-text">-->
<!--Some quick example text to build on the card title and make up the bulk of the card's content.-->
<!--</p>-->
<b-button href="#" variant="primary">Go somewhere</b-button>
<b-card no-body>
<b-tabs card>
<b-tab title="first" active>
<br>I'm the first fading tab
</b-tab>
<b-tab title="second" >
<br>I'm the second tab content
</b-tab>
<b-tab title="disabled" disabled>
<br>Disabled tab!
</b-tab>
</b-tabs >
</b-card>
</b-card>
</div>
</template>
<script>
export default {
components: { }
}
</script>
<style scoped>
</style>
I ran into a similar issue with bootstrap-vue 2.21.2. Ended up being that I had a component that wasn't marked up with #Component. After adding it in all my components went from 'undefined' text to display the proper content. See https://github.com/bootstrap-vue/bootstrap-vue/issues/5985#issuecomment-765558938.
Upgrade to the latest version of BootstrapVue to get around this bug.
I had a parent component missing the #Component decorator (in my case it was App.vue) that was causing the undefined behaviour. Make sure that all components have this decorator applied.
I'm trying to follow the setup guide here to add the grid layout into my Electron/Vue application. I'm getting an error "Error: Layout must be an array!"
My code is below...
<template>
<div id="wrapper">
<img id="logo" src="~#/assets/logo.png" alt="electron-vue">
<main>
<div class="left-side">
<span class="title">
Connection to OMS Database
</span>
<system-information></system-information>
</div>
<div class="right-side">
<grid-layout
:layout="layout"
:col-num="12"
:row-height="30"
:is-draggable="true"
:is-resizable="true"
:vertical-compact="true"
:margin="[10, 10]"
:use-css-transforms="true"
>
<grid-item v-for="item in layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i">
{{item.i}}
</grid-item>
</grid-layout>
</div>
</main>
</div>
</template>
<script>
import SystemInformation from './LandingPage/SystemInformation'
import VueGridLayout from 'vue-grid-layout';
var GridLayout = VueGridLayout.GridLayout;
var GridItem = VueGridLayout.GridItem;
var testLayout = [
{"x":0,"y":0,"w":2,"h":2,"i":"0"},
{"x":2,"y":0,"w":2,"h":4,"i":"1"},
{"x":4,"y":0,"w":2,"h":5,"i":"2"},
{"x":6,"y":0,"w":2,"h":3,"i":"3"},
{"x":8,"y":0,"w":2,"h":3,"i":"4"},
{"x":10,"y":0,"w":2,"h":3,"i":"5"},
{"x":0,"y":5,"w":2,"h":5,"i":"6"},
{"x":2,"y":5,"w":2,"h":5,"i":"7"},
{"x":4,"y":5,"w":2,"h":5,"i":"8"},
{"x":6,"y":4,"w":2,"h":4,"i":"9"},
{"x":8,"y":4,"w":2,"h":4,"i":"10"},
{"x":10,"y":4,"w":2,"h":4,"i":"11"},
{"x":0,"y":10,"w":2,"h":5,"i":"12"},
{"x":2,"y":10,"w":2,"h":5,"i":"13"},
{"x":4,"y":8,"w":2,"h":4,"i":"14"},
{"x":6,"y":8,"w":2,"h":4,"i":"15"},
{"x":8,"y":10,"w":2,"h":5,"i":"16"},
{"x":10,"y":4,"w":2,"h":2,"i":"17"},
{"x":0,"y":9,"w":2,"h":3,"i":"18"},
{"x":2,"y":6,"w":2,"h":2,"i":"19"}
];
export default {
name: 'landing-page',
components: { SystemInformation, GridItem, GridLayout },
layout: testLayout,
methods: {
open (link) {
this.$electron.shell.openExternal(link)
}
},
}
</script>
I tried importing the library using import and require but I keep getting the array issue. Not sure why it's going wrong.
Thank you for taking time to help.