I've a header components who's rendered in the layout default.vue and when the menu is open, it takes the entire page and hide the content. When i click on the link i want the menu to wait if the page where the user is redirect finished to load. But i dont see any "hooks" that tell if the page is loaded or not.
<template>
<header class="the-header" :class="{ 'the-header--open': isNavOpen }">
<div class="the-header__container container">
<a href="#" class="the-header__brand" #click="onToggleTheme" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)">
<img src="~/assets/img/logo.png" alt="Logo" />
</a>
<div class="the-header__menu">
<div #click="onToggleNav" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)" class="the-header__burger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<nav class="the-header__nav">
<nuxt-link
to="/"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
Accueil
</span>
</nuxt-link>
<nuxt-link
to="/about"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
A propos
</span>
</nuxt-link>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
isNavOpen: false,
};
},
mounted() {
this.$nextTick(() => {
this.$nuxt.$loading.start()
setTimeout(() => this.$nuxt.$loading.finish(), 500)
})
},
methods: {
onToggleNav() {
this.isNavOpen = !this.isNavOpen;
},
onRedirection() {
setTimeout(() => {
this.isNavOpen = false;
}, 500);
},
onToggleTheme() {
let currentTheme = document.documentElement.getAttribute("data-theme");
let targetTheme = "dark";
if (currentTheme === "dark") {
targetTheme = "light";
}
document.documentElement.setAttribute("data-theme", targetTheme);
},
},
};
</script>
So I put a setTimeout on the loading function with a minimum of 500ms, but if the loading take more time then the effet doesnt work :/
sry for bad english (i'm french)
Related
I'm making a website that has a filter function before pouring data out... Currently, I'm doing the pretty stupid way of dumping all the data out and putting it in an object and then filtering. I want when I click on it, it will call the api to return according to the params that I pass, but the initial default is that the checkbox is in the all box, it still returns all. Here is my code part....
Template:
<template>
<div class="job">
<div class="container recruit flex-wrap">
<div class="job-filter">
<h3>
Ngành nghề
<img
id="icon-filter"
#click="showfilter"
:style="{ display: display.btn_show_filter }"
src="../assets/recruit/angle-down-svgrepo-com.svg"
alt=""
/>
<img
id="icon-close-filter"
:style="{ display: display.btn_close_filter }"
#click="closefilter"
src="../assets/recruit/close-svgrepo-com.svg"
alt=""
/>
</h3>
//Handle Filter
<div
class="radio-group"
id="group-filter"
:style="{ display: display.group_filter }"
>
<div
class="radio-check"
v-for="(check, index) in checks"
#click="selectFilter(check)"
:key="index"
>
<input
type="radio"
:id="index"
name="fav_language"
:value="check"
v-model="selected"
/>
<label :for="check">{{ check }}</label
><br />
</div>
</div>
</div>
//End Handle filter
<div class="search">
<div class="search-top flex-wrap">
<h5>{{ totalJobs }} {{ localised("countRecruit") }}</h5>
<div class="search-input">
<input
type="search"
placeholder="Nhập tên vị trí công việc"
v-model="search"
/>
<button #click="searchJob">
<img src="../assets/recruit/search.svg" alt="" />
</button>
</div>
</div>
<div
id="jobs"
class="job-item"
v-for="(item, index) in jobs"
:key="index"
:per-page="perPage"
:current-page="currentPage"
>
<router-link
tag="a"
:to="{ name: 'Detail', params: { id: item.id } }"
>
<h3 class="mleft-27">{{ item.Name }}</h3>
</router-link>
<div class="job-info flex-wrap">
<div class="job-info-left pleft-27 flex-wrap">
<div>
<img src="../assets/recruit/years.svg" alt="" />
<b>{{ item.Exprerience }}</b>
</div>
<div>
<img src="../assets/recruit/luong.svg" alt="" />
<b>{{ item.Salary }}</b>
</div>
<div>
<img src="../assets/recruit/diadiem.svg" alt="" />
<b>{{ item.Headequarters }}</b>
</div>
</div>
<div>
<h6>{{ momentTime(item.updatedAt) }}</h6>
</div>
</div>
<div class="info-job flex-wrap">
<div class="list-info-job">
<li>{{ item.Content }}</li>
</div>
<router-link
tag="a"
:to="{ name: 'Detail', params: { id: item.id } }"
>
<button class="btn-detail">Xem chi tiết</button>
</router-link>
</div>
</div>
<h3 class="not-found" v-show="showNoData">Not Found</h3>
</div>
</div>
</div>
</template>
And here is the js logic I'm dealing with:
<script>
import "../assets/style.css";
import "../assets/job.css";
import job from "../locales/lang";
import request from "#/utils/request";
import moment from "moment";
export default {
name: "jobs",
components: {},
data() {
return {
totalJobs: null,
tickTime: "",
currentPage: 1,
showPag: true,
showNoData: false,
perPage: 4,
search: "",
page: "",
noData: [],
checks: [
"All",
"Developer",
"Tester",
"Designer",
"Support",
"Marketing",
"Other",
],
jobinfos: [],
showJobs: [],
selected: "All",
};
},
computed: {
},
watch: {
// selected(newVal) {
// if (newVal === "All") {
// return this.jobinfos;
// } else {
// return this.jobinfos.filter((i) => i.Genres === newVal);
// }
// },
},
async mounted() {
//API
this.getJobs();
// event enter
var self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.searchJob();
}
});
},
methods: {
//Call api job information by locale
async getJobs() {
await request
.get(`jobs?_locale=${this.$route.params.locale}`)
.then((response) => {
this.jobinfos = response.data;
this.showJobs = response.data;
this.totalJobs = response.data.length;
})
.catch((e) => {});
},
//Filter item jobs
selectFilter(item) {
this.selected = item;
if (this.selected == "All") {
this.showJobs = this.jobinfos;
} else {
this.showJobs = this.jobinfos.filter((i) => i.Genres === this.selected);
if (this.showJobs.length > 0) {
this.showPag = true;
this.showNoData = false;
} else {
this.showPag = false;
this.showNoData = true;
}
}
},
// method search by filter
},
};
</script>
As for the backend, I'm using 3rd party cms, it probably already provides enough query params for me, please give me some pseudocode or a way so that when I click on the checkbox, it will call the api to return the records according to the check box ^^ thanks
I use a component menu and this is displayed for each item but it all stays open if I don't click on close out I would like it to close when I click on another item with a menu.
to open and close I use a toggle on click the component has several styles
i on vuejs 3
I have the impression that each menu is independent so I don't know what to do
<nav role="navigation" :id="uuid">
<div class="app-menu">
<div class="menu-princ" #click.stop="showModal = !showModal">
<span
#click.stop="showModal = !showModal"
v-if="ellipsis"
:class="{ ellipsis: ellipsis, cells: cells }"
></span>
<span #click.stop="showModal = !showModal" v-if="ellipsis === false"
><b></b
></span>
</div>
<div
id="menu-princ"
class="menu-modal"
:class="[{ open: showModal }, { menuEllipsisPosition: ellipsis }]"
v-if="showModal"
>
<div class="wrap">
<header class="menuHeader" :class="{ ellipsisMenu: ellipsis }">
<h6
:class="{ ellipsisTitle: ellipsis, ellipsisTitleAfter: ellipsis }"
>
{{ titleMenu }}
</h6>
<u :class="{ ellipsisSubTitle: ellipsis }">{{ subTitleMenu }}</u>
<button
class="detailBtn"
:class="{ ellipsisDetailBtn: ellipsis }"
#click.stop="showModal = !showModal"
></button>
</header>
<section>
<ul v-if="disableMenuLi">
<li
#click.stop="showModal = !showModal"
v-for="([key, value], j) in Object.entries(items)"
:key="`item${j}`"
:data-label="value.label"
class="menuLiContent"
:class="{
ellipsisBorderBottom: ellipsis,
ellipsisMenuLiContent: ellipsis,
ellipsisliHover: ellipsis,
disable: disable
}"
>
<slot #click.stop="showModal = !showModal" :item="value">{{
item[key]
}}</slot>
</li>
</ul>
</section>
</div>
</div>
</div>
</nav>
</template>
<script>
import { uuid } from 'vue-uuid';
export default {
name: 'Menu',
data() {
return {
showModal: false,
uuid: uuid.v4()
};
}
};
</script>
<style lang="scss" src="./style.scss" scoped></style>
I am disabling document body scroll when my mobile nav is open which is working as expected. However it was not removing the overflow hidden when user would click a link to another route/page. I created a simple method to remove the overflow hidden when a link is clicked in the nav when the menu is open, which does work with one small caveat. When the user is on say page "home" and the mobile nav is OPEN, when they click the link "home" inside of the mobile nav it is closing the menu, and I understand I have done that with the method I created. Is there a way to prevent that event from firing when clicking the link of the page you are on?
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden();
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden();
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden();
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden();
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden() {
this.activeHamburger = false;
}
}
You can pass the route value to the method and check that it's not same as the current route before executing.
<template>
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden('home');
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden('about');
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden('work');
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden('contact');
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden(value) {
if (this.$route.path !== value) {
this.activeHamburger = false;
}
}
}
}
</script>
I haven't seen your routes but you can also use this.$route.name if you prefer and adjust accordingly the values you pass to the method.
i have a costume made Tabs and Tab from Laracasts Tabs Tutorial , and it work fine, but i need to load data when the tab is changed and i did that but when the data is loaded,
i need to render another component which have accordion and inside each accordion tab their some charts components need to be render also
so how can i render the accordion with the charts components when the Tabs tab is changed
Tabs Component:
<template>
<div class="tab-container -mt-px w-full">
<div class="tabs">
<ul class="list-reset flex border-b">
<li class="" v-for="(tab, index) in tabs" role="tab">
<a class="bg-white inline-block font-semibold hover:no-underline"
:class="[
{
'active-tab-link text-blue-dark border-l border-r active-tab-link-p': tab.isActive,
'text-blue hover:text-blue-darker non-active-tab-link-p': !tab.isActive
},
]"
:href="tab.href" #click="selectedTab(tab)">
{{tab.name}}
</a>
</li>
</ul>
</div>
<div class="tabs-details px-4 w-full">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "Tabs",
data() {
return {
tabs: []
};
},
created() {
this.tabs = this.$children;
// this.selectFirstTab();
},
methods: {
selectedTab(selectedTab) {
this.$emit('onTabChange', selectedTab);
this.tabs.map((tab, index) => {
tab.isActive = (tab.name === selectedTab.name)
});
},
}
}
</script>
Tab Component:
<template>
<div class="w-full" role="tabpanel" v-show="isActive">
<slot></slot>
</div>
</template>
<script>
import {isEmpty} from "../helpers/Helper";
export default {
name: "Tab",
props: {
name: {
type: String,
required: true
},
selected: {
type: Boolean,
default: false,
},
isFirst: {
type: Boolean,
default: false,
},
},
data() {
return {
// isActive: false,
isActive: true,
// isFirst: this.isFirst
};
},
computed: {
href() {
return this.formatHref(this.name);
},
},
mounted() {
this.selectTabFromURL();
},
methods: {
selectTabFromURL() {
let hash = this.$route.hash;
if (this.selected) {
this.isActive = this.selected;
} else if (!isEmpty(hash)) {
this.isActive = (this.formatHref(this.name) === hash);
} else if (this.isFirst) {
this.isActive = this.isFirst;
} else if (!this.isFirst && this.isActive) {
this.isActive = !this.isActive;
}
},
formatHref(id) {
return `#${id.toLowerCase().replace(/ /g, '-')}`;
}
}
}
</script>
the main component:
<template>
<!--components tabs start-->
<div class="flex flex-col">
<div class="mt-3 border-t-4 border-primary-color border-6 bg-white">
<div class=" border border-gray-400 lg:border-l-0 lg:border-t lg:border-gray-400 rounded-b lg:rounded-b-none lg:rounded-r leading-normal">
<Tabs #onTabChange="handleTabChange">
<!--:name="`${tab.name} - ${tab.component.type}`"-->
<Tab v-for="(tab, index) in page.tabs"
:key="tab.id"
:id="tab.slug"
:name="tab.name"
:slug="tab.slug"
:isFirst="index === 0"
>
<div>
How to render the dynamic accordion with the charts one time only no need to re-render
</div>
</Tab>
</Tabs>
</div>
</div>
</div>
<!--components tabs end-->
</template>
in normal HTML and JQuery, i can load the data and the render the result and append it to the wanted tab how can we do this with vue, dose the dynamic component help in this case ?
I searched for a solution and found and implement the "Creating Vue.js Component Instances Programmatically"
I'm starting a new website with Nuxt and I'm currently setting it to be multilingual (french and english)
I followed this tutorial to setup the translation and the language switcher, and got the following:
nuxt.config.js (relevant part)
['nuxt-i18n', {
detectBrowserLanguage: {
useCookie: true,
alwaysRedirect: true
},
strategy: 'prefix_except_default',
defaultLocale: 'fr',
parsePages: false,
seo: true,
pages: {
about: {
en: '/about-us',
fr: '/a-propos'
},
portfolio: {
en: '/projects',
fr: '/portfolio'
}
},
locales: [
{
code: 'en',
iso: 'en-US',
name: 'English',
file: 'en-US.js'
},
{
code: 'fr',
iso: 'fr-FR',
name: 'Français',
file: 'fr-FR.js'
}
],
lazy: true,
langDir: 'lang/'
}]
navbar.vue
<nav class="header">
<div class="logo">
<nuxt-link :to="localePath('index')">
<img src="https://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox" width="112" height="28">
</nuxt-link>
</div>
<div class="links">
<nuxt-link :to="localePath('about')">
{{ $t('navbar.about') }}
</nuxt-link>
<nuxt-link :to="localePath('blog')">
Blog
</nuxt-link>
<nuxt-link :to="localePath('portfolio')">
Portfolio
</nuxt-link>
<nuxt-link :to="localePath('contact')">
Contact
</nuxt-link>
<span>|</span>
<nuxt-link v-if="$i18n.locale === 'fr'" :to="switchLocalePath('en')">
English
</nuxt-link>
<nuxt-link v-else :to="switchLocalePath('fr')">
Français
</nuxt-link>
{{ $i18n.locale }}
</div>
</nav>
Here is my directory structure (if that can help)
layouts/
front.vue
navbar.vue
pages/
index.vue
about.vue
blog.vue
portfolio.vue
contact.vue
The navbar.vue file is called inside front.vue, which is my layout.
The problems are the following:
On any page, when I try to click the languageSwitcher link, I get redirected to its english version (ie: /a-propos becomes /en/about-us), however the other links will bring me back to the french version.
{{ $i18n.locale }} keeps displaying fr
I tried the following block of code:
<template>
...
<nuxt-link
v-for="locale in availableLocales"
key="locale.code"
:to="switchLocalePath(locale.code)"
>
{{ locale.name }}
</nuxt-link>
...
</template
<script>
export default {
computed: {
availableLocales () {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
}
}
}
</script>
And it only displays english, while it should display me both english and french.
What have I done wrong, or what am I missing?
This worked for me, using vue-select and availableLocales of Nuxt-$i18n, hope it helps:
<template>
<div>
<client-only>
<b-navbar>
<div class="container">
<b-navbar-brand
class="navbar-brand-custom mr-2"
:to="localePath('index')"
:title="title">
<div class="d-flex flex-row order-2 order-lg-3 ml-auto text-capitalize">
<b-navbar-nav class="d-flex flex-row">
<!-- Language menu -->
<v-select
v-model="selected"
v-for="(lang, index) in availableLocales"
:key="index"
:value="lang.code"
:searchable="false"
#input="onChange"
class="vue-select-custom"
></v-select>
<!-- Navbar menu -->
<b-nav-item-dropdown
id="menu-links"
right
no-caret
role="manu"
class="ml-0"
menu-class="animated fadeIn text-right menu-links rounded-0"
>
<span class="dropdown-menu-arrow"></span>
<template
v-slot:button-content
style="height:42px"
>
</b-nav-item-dropdown>
</b-navbar-nav>
</div>
</div>
</b-navbar>
</client-only>
<script>
export default {
data() {
return {
selected: this.$i18n.locale
}
},
computed: {
availableLocales() {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale);
},
},
.....
methods: {
onChange(locale) {
var language = locale.toLocaleLowerCase();
this.$i18n.setLocaleCookie(language)
this.$router.replace(this.switchLocalePath(language));
},
....
}
}
this is due to alwaysRedirect: true, set it to false