Global CSS doesn’t work when programmatically change route - vue.js

My index page has a Header and Home components.
{
path: "/",
name: "Homepage",
components: {
header: () => import("../components/layout/HomepageHeader.vue"),
main: () => import("../views/Home.vue"),
},
},
Both don’t have any scoped CSS, they only use Tailwind CSS classes. And that Tailwind CSS file is global.
The problem occurs when I programmatically redirect to my index page (for example, after I logged in). The HomepageHeader component doesn’t have all the CSS. Same with the Home.vue component.
Here’s the simplified code of the Header
<template>
<div class="w-full bg-white sm:shadow-lg mb-6">
<div class="mx-6">
<nav
class="px-3 flex flex-col sm:flex-row sm:px-4 sm:justify-between sm:h-16 list-none"
>
<li
class="text-center h-16 font-semibold text-red-500 text-lg sm:text-xl sm:h-16 table"
>
<div class=" py-4 sm:h-16">
<router-link to="/">Home</router-link>
</div>
</li>
<li>
<login-badge
class="shadow-lg md:shadow-none px-4 py-3 flex sm:px-0 sm:h-14 text-white"
></login-badge>
</li>
</nav>
</div>
</div>
</template>
When I say
“component doesn’t have all the CSS”
I mean that all the classes in the HomepageHeader.vue component don’t work. But the classes inside the child component LoginBadge.vue work fine!
What else can I say? I use router.replace("/"); to redirect to the homepage.
How do I fix that?
P.S. Just found out that the same thing happends when I go back to a previous page and that previous page is the index page.
P.P.S. global CSS is imported inside the App.vue
<template>
<router-view name="header"></router-view>
<main class="container mx-auto">
<router-view name="main"></router-view>
</main>
</template>
<script>
export default {
setup() {
return {};
},
};
</script>
<style>
#import "./assets/tailwind.css";

Related

Nuxt3 - NuxtLink to anchor not scrolling on click or page refresh

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>
...

JAMStack: how to impelement E-Mail Services or simple reactive variables?

I'm currently working on a marketing website for a client. Because SSR would be over-engineered and I'm curious about JAMStack, I decided to make a static website using Nuxt, because I'm already familiar with it.
So I managed to implement Tailwind, which works fine, also when the static site is generated and live on Github pages for example. Now I want to add a feature like a send e-mail form or a menu with a toggle icon.
The simplest solution I could think of for the menu would be a reactive variable and conditional rendering using tailwind. This works fine on localhost using npm run dev, but I can't figure out how to implement this on the generated static site.
I couldn't even manage to fire a click event to a defined method in the script tag.
That's how I would do the burger menu with nuxt SRR, how can I implement something like this in static generated nuxt?
<template>
<div>
<nav
class="flex p-5 items-center fixed w-full border-b-4 border-green bg-white z-50 duration-300 transform justify-between">
<div>
<div><img src="logo.png" class="max-h-6 md:max-h-10" /></div>
</div>
<div class="w-10 h-5 flex flex-col relative cursor-pointer" id="nav-menu" #click="menuOpen = !menuOpen">
<div class="w-full h-1 bg-green absolute duration-200 transform "
:class="[menuOpen ? 'rotate-45 translate-y-2' : 'burger-hover1']" id="menu-first"></div>
<div class="w-full h-1 bg-green absolute bottom-0 duration-200 transform "
:class="[menuOpen ? '-rotate-45 -translate-y-2' : 'burger-hover2']" id="menu-second"></div>
</div>
</nav>
<Transition>
<div v-if="menuOpen" class="fixed w-screen h-screen bg-white pt-20 z-30 space-y-10 text-xl text-center">
<div class="mt-20" #click="menuOpen = !menuOpen">
<nuxt-link to="/">Start</nuxt-link>
</div>
<div #click="menuOpen = !menuOpen">
<nuxt-link to="/partner">Partner</nuxt-link>
</div>
<div #click="menuOpen = !menuOpen">
<nuxt-link to="/kontakt">Kontakt</nuxt-link>
</div>
<div #click="menuOpen = !menuOpen">
<nuxt-link to="/impressum">Impressum</nuxt-link>
</div>
</div>
</Transition>
</div>
</template>
<script>
export default {
name: 'Navbar',
data() {
return {
menuOpen: false
}
}
}
</script>
That's how it looks on localhost. When the static site is hosted, it won't toggle the menu.

Vue how to customize global navbar at view level

Im super new to Vue.
i have a Vue-CLI app, which have a navbar and content.
Navbar is common to all pages, but i want to customize in each page whit some additional content.
Example:
Common-> home | about
View home -> home | about | your are in view home
View about -> home | about | your are in view about
router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '../views/Home.vue';
import NavBar from '#/components/NavBar.vue';
Vue.use(VueRouter);
Vue.component('nav-bar', NavBar);
//...
components/navbar.vue
<template>
<div>
<b-nav-item to="/">home</b-nav-item>
<b-nav-item to="/about">about</b-nav-item>
{{customContent}}
</div>
</template>
<script>
export default {
name: 'NavBar',
props: {
customContent: {
type: String,
default: 'default Content',
},
},
};
</script>
App.vue
<template>
<div id="app">
<nav-bar />
<div class="container-fluid">
<router-view />
</div>
</div>
</template>
views/home.vue
<template>
<div class="row">
<div class="col-12">
<image-card :images="images"/>
</div>
</div>
</template>
<script>
//how can i customize here the navbar by adding for example 'your are in view home'???
</script>
Thanks so much!
There are a few ways in which you can solve this problem. I'll list two of them.
1. Update NavBar by $route
In this approach, the NavBar component already contains all of the possible combinations, and will display the relevant portion(s) depending on what $route contains.
Here's some pseudo code:
navbar.vue
<template>
<div class="navbar">
<div class="navbar-left>
APPNAME
</div>
<div v-if="name === 'landing'">
...
</div>
<div v-else-if="name === 'room'">
...
</div>
</div>
</template>
App.vue
<template>
<div id="app">
<NavBar :name="$route.name"/>
<main>
<router-view/>
</main>
</div>
</template>
In this example, the NavBar component is very rigid, and doesn't really lend itself to much reuse. However, it does encapsulate all the relevant code relating to the nav bar.
2. Extensible NavBar with slots
In this approach, the NavBar only provides the bare-minimum to create a nav bar. The rest of the route-specific elements are to be filled in by the views.
navbar.vue
<template>
<div class="navbar">
<div class="navbar-left">
<div class="navbar-brand">
APPNAME
</div>
<slot name="left"></slot>
</div>
<div class="navbar-right">
<slot name="right"></slot>
</div>
</div>
</template>
App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
landing.vue
<template>
<div>
<header>
<NavBar>
<template slot="right">
<span>
<div class="navbar-item">
<div class="buttons">
<button class="button" #click="...">Start Watching</button>
</div>
</div>
</span>
</template>
</NavBar>
</header>
<main>
...
</main>
</div>
</template>
This approach has a bit of repetition in terms of DOM elements, but gives you an extremely flexible NavBar that can be customized by each view.
The approach you want to use depends on what is important to you.
If strict encapsulation is what you want, then you may want to use approach 1, as all of the NavBar-related code is contained within a single file.
However, if you believe that there is a potential for reuse, or if you would like all view-related code to live in one place, then it makes sense to use slots instead and extend the NavBar as required by each view.
I use a breadcrumb to achieve a similar thing. Just an idea but Vue router allows you to add meta data to the current route which you always have access to
router.js
path: '/add',
name: 'add',
component: () => import(/* webpackChunkName: "add" */ '../../views/Add.vue'),
meta: {
breadCrumb: [
{ name: 'Add New' }
]
},
Notice the meta object attached to the route.. this will be used to describe the current view.
Breadcrumb.vue component
<template>
<div class="breadcrumb">
<ul class="d-flex m-0 p-0"
<li
v-for="(breadcrumb, idx) in breadcrumbList"
:key="idx">
{{ breadcrumb.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Breadcrumb',
data () {
return {
breadcrumbList: []
}
},
mounted () { this.updateList() },
watch: { '$route' () { this.updateList() } },
methods: {
routeTo (pRouteTo) {
if (this.breadcrumbList[pRouteTo].link) this.$router.push(this.breadcrumbList[pRouteTo].link)
},
updateList () { this.breadcrumbList = this.$route.meta.breadCrumb },
formatPath(path) {
const newPath = path.replace(/\//g, " > ")
return newPath
}
}
}
</script>
And then you can import the breadcrumb into your navbar or where ever you would like to place it
<Breadcrumb class="breadcrumb" />
import Breadcrumb from '#/components/Breadcrumb.vue'
components: {Breadcrumb}
So basically the breadcrumb will always watch your current route and change the data based on the meta data you provide in your router.js file
You can access to router name like this:
<div v-if="this.$route.name == 'home'">
<HeaderTransparent />
</div>
<div v-else>
<HeaderWhite />
</div>

How to transfer data between components (siblings) Vue?

In the HeaderComponent method 'clickPrices' is called on click
<template>
<header>
<div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
<h5 class="my-0 mr-md-auto font-weight-normal">Company name</h5>
<nav class="my-2 my-md-0 mr-md-3">
<a class="p-2 text-dark" href="#">Features</a>
<a class="p-2 text-dark">Enterprise</a>
<a class="p-2 text-dark" #click="clickPrices()">Get pricing</a>
</nav>
<a class="btn btn-outline-primary " href="#">Sign up</a>
</div>
</header>
</template>
<script>
export default {
name: "HeaderComponent",
methods: {
clickPrices() {
...
},
},
}
</script>
<style scoped>
</style>
and there is a Pricing Component in which I make a request to the server in the method 'getPricing'
<template>
<div class="wrap-pricing">
<div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">Pricing</h1>
<p class="lead">Quickly build an effective pricing table for your potential customers with this Bootstrap example. It’s built with default Bootstrap components and utilities with little customization.</p>
</div>
<div class="container">
<div class="card-deck mb-3 text-center">
<div class="card mb-4 shadow-sm">
<div class="card-header">
<h4 class="my-0 font-weight-normal">Lorem</h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title">$10 <small class="text-muted">/ mo</small></h1>
<ul class="list-unstyled mt-3 mb-4">
<li>Lorem</li>
<li>Lorem</li>
<li>Lorem</li>
<li>Lorem</li>
</ul>
<button type="button" class="btn btn-lg btn-block"></button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import router from '../routes.js';
import { axios } from 'axios';
export default {
name: "PriceComponent",
methods: {
getPricing() {
axios.get('api/pricing').then((response) => {
//some actions
router.push('prices');
});
},
},
}
</script>
<style scoped>
</style>
How should I process the result of the 'сlickPrices' HeaderComponent method?
Or am I waiting for your ways, how can I get data in another by clicking in one component
You can emit an event and let the parent component handle the fetching and pass the data as props to the child component.
Or another way is to directly listen to the event as follows
Component 1:
this.$root.$emit('clickPrices', data);
Component 2:
mounted() {
this.$root.$on('clickPrices', data => {
this.getPricing();
});
}
Check out the answer by #alex
Since you are using 2 independent components (one is not included in the other), you cannot pass props
Things you can do -
Instead of fetching all the prices on click, just create a created hook like so, which will fetch all the pricing details whenever your component is created -
created () {
this.getPricing()
},
methods: {
getPricing() {
axios.get('api/pricing').then((response) => {
//some actions
router.push('prices');
});
},
},
Use State and when a user clicks on the button, pricing details are fetched and added in the state. And you can just use the state anywhere in your application like so -
this.$store.state.prices
Let me know if it works, if not we will find some other solution for you!

Vue JS Component reattach or Cache

VueJS component doesn't get cached or atleast reattached after navigation. On refresh or launch everything gets attached and rendered well but after navigating to another page then back. The First Component - Carousel - component in my case doesn't get rendered but the API call is made.
<template>
<div class="rel">
<div id="homeCarousel" class="owl-carousel owl-slider">
<div class="item" v-for="product in featured">
<div class="bg-holder top-area-half" >
<div class="bg-mask-lighten"></div>
<img class="bg-img" v-bind:src="product.feature_image_url">
<div class="hero-caption">
<div class="container">
<h3 class="hero-title">{{product.feature_title}}</h3>
<p class="hero-subtitle">{{product.feature_subtitle}}</p>
<a class="btn btn-white btn-ghost btn-lg hero-btn" href="#">Shop now</a>
</div>
</div>
</div>
</div>
</div>
<div id="hero-slider-nav" class="hero-slider-nav">
<div class="container">
<div class="pull-right"></div>
</div>
</div>
</div>
</template>
<style>
</style>
<script>
export default{
data(){
return{
featured:[]
}
},
ready(){
},
mounted(){
this.getFeaturedProducts();
},
components:{
},
methods: {
getFeaturedProducts: function () {
Vue.http.get('/api/product/filter/featured=1').then(
(response) => {
this.featured = response.body;
}
)
}
}
}
</script>
`
<template>
<div class="global-wrapper clearfix ">
<keep-alive>
<Carousel></Carousel>
</keep-alive>
//The rest of the code which is just importing the Component
I found out what i was doing wrong. I had a separate JS/JQuery file and on the document ready i was initializing an owl carousel by id #('homeCarousel').owlCarousel({}) . What worked was, since i had already bootstrapped owl carousel -> on the mounted lifecycle callback i was now targeting the element and making it an owl carousel.