Vue Router link Ana Router exact in children - vuejs3

there are two components Dashboard and Apps and they have children. What I want to do is, when clicking any of the Main Components
showMenu is showing but,
I don't want the main components to be opened. and I want the main components and active class to be written
I want it to act as Dashboard and Appsself
app.vue
<template>
<router-link :to="{name:'dashboard'}" #click="isShow('dashboard')">Dashboard</router-link>
<router-link :to="{name:'apps'}" #click="isShow('apps')">Apps</router-link>
<div class="children">
<div class="dashboard" v-show="showName ==='dashboard' && showMenu">
<router-link exact :to="{name:'home'}">Home</router-link>
<router-link exact :to="{name:'about'}">About</router-link>
</div>
<div class="apps" v-show="showName ==='apps' && showMenu">
<router-link exact :to="{name:'setting'}">Setting</router-link>
<router-link exact :to="{name:'profile'}">Profile</router-link>
</div>
</div>
<div class="container">
<router-view/>
</div>
</template>
<script setup>
const showName = ref('')
const showMenu = ref(false)
function isShow(name) {
if (showName.value !== name) {
showMenu.value = true
}
if (showName.value === name) {
showMenu.value = !showMenu.value
} else {
showName.value = name
}
}
</script>
router.js
import {createRouter, createWebHistory, RouterView} from 'vue-router'
const routes = [
{
path: '/dashboards',
name: 'dashboards',
component: RouterView,
children: [
{
path: 'home',
component: () => import('../Views/home.vue'),
name: 'home',
},
{
path: 'about',
component: () => import('../Views/about.vue'),
name: 'about',
},
]
},
{
path: '/apps',
name: 'apps',
component: RouterView,
children: [
{
path: 'setting',
component: () => import('../Views/setting.vue'),
name: 'setting',
},
{
path: 'profile',
component: () => import('../Views/profile.vue'),
name: 'profile',
},
]
},
];
const router = createRouter({
linkActiveClass: "item-active",
linkExactActiveClass: "exact-active",
history: createWebHistory(),
routes
})
export default router;

Related

Passing information into sub component in storybook

Is there a way to pass information down though components in storybook that do not inheartly pass props though.
A proacticle example of this being that i have a language select button (usually pulls its information from a store so no information is passed to it), for storybook i have some props avaiable for this so i can run tests etc without having dirrect access to the store. this component on its own works fine.
On top of this i have a header nav bar that utlises this component, but does not pass informaton to it (as its self managed), however when trying to now set this component up in storybook the language select does not display as not having data.
I was reading though the multi-component documentation but struggling to get this to work for this use case and begining to wonder if this is going to be possiable and if so whats the best way to approach this.
in terms of code this is what i currently have
//header.vue
<template>
<div class="HeaderComponent">
//other component code here
<language-selector/>
</div>
</div>
</nav>
</div>
</template>
<script setup lang="ts">
import LanguageSelector from "#/components/HeaderAndFooter/LanguageSelector.vue";
import _ from "underscore";
import ImageModel from "#/helpers/classes/core/ImageModel";
import { defineProps } from "vue";
import HeaderNavigation from "#/helpers/classes/Navigation/HeaderNavigation";
// eslint-disable-next-line no-unused-vars
const props = defineProps({
menu: {
type: Object as () => {[key: string]: HeaderNavigation} | undefined,
default: undefined
},
icon: {
type: Object as () => ImageModel | undefined,
default: undefined
}
})
</script>
///languageSelector.vue
<template>
<div class="languageSelector" v-if="props.languages.length > 1">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="languageDropdownButton" data-bs-toggle="dropdown" aria-expanded="false">
{{currentLang.name}}
</button>
<ul class="dropdown-menu" aria-labelledby="languageDropdownButton">
<li v-for="language in props.languages" :key="language.i18n">
<a class="dropdown-item" href="#" #click="ChangeLanguage(language.i18n)">{{language.name}}</a>
</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import {useLanguageStore} from "#/store/languageStore";
import {computed, defineProps} from "vue";
import Language from "#/helpers/classes/Language";
const props = defineProps({
languages: {
languages: Object as () => Language[],
default: useLanguageStore().availableLanguages
},
})
const currentLang = computed(() => useLanguageStore().currentLanguage);
function ChangeLanguage(i18nCode : string)
{
useLanguageStore().updateCurrentLanguage(i18nCode);
}
</script>
///header.story.js
import LanguageSelector from "#/components/HeaderAndFooter/LanguageSelector.vue";
import { DefaultLangSelector } from './LanguageSelector.stories.js';
// More on default export: https://storybook.js.org/docs/vue/writing-stories/introduction#default-export
export default {
title: 'Framework/Header',
component: HeaderComponent,
// More on argTypes: https://storybook.js.org/docs/vue/api/argtypes
argTypes: {},
};
// More on component templates: https://storybook.js.org/docs/vue/writing-stories/introduction#using-args
const Template = (args) => ({
components: { HeaderComponent, LanguageSelector },
setup() {
return { args };
},
template: '<header-component v-bind="args" />',
});
export const Primary = Template.bind({});
Primary.args = {
menu: {
"Item 1": {
"children": {},
"id": "1",
"title": "Item 1"
},
"Item 2": {
"children": {},
"id": "2",
"title": "Item 2"
}
},
icon:
{
"url": "https://th.bing.com/th/id/R.25df89f6bbce9ce2324fb746d01940ea?rik=WpLZgCWrgPXbLA&pid=ImgRaw&r=0"
},
...DefaultLangSelector.args = {
languages: [
{"name" : "English", "i18n" : "en"},
{"name" : "Spanish", "i18n" : "es"},
],
}
};

Vue3 - Routes if page doesn't exist with dynamic routes not working with my 404?

I am using Vue3 and have my Router setup for detail pages. Any title returns the same data and 404 is being ignored even after adding the regEx inside the routes.
Routes:
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import HomeView from "../views/HomeView.vue";
import ErrorView from "../views/ErrorView.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "home",
component: HomeView,
},
{
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(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
},
{
path: "/article/:slug",
name: "article",
component: () =>
import(/* webpackChunkName: "article" */ "../views/ArticleView.vue"),
},
{
path: "/404",
name: "PageNotExist",
component: () => import("../views/ErrorView.vue"),
},
{
path: "/:catchAll(.*)", // Unrecognized path automatically matches 404
redirect: "/404",
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
Article:
<template>
<div>
<h1>{{ data.title }}</h1>
<h3>{{ data.textarea }}</h3>
</div>
</template>
<script lang="ts">
import { useSanityFetcher } from "vue-sanity";
import { defineComponent } from "vue";
export default defineComponent({
name: "ArticleView",
setup: () => {
const articleQuery = `*[_type == "article"][0] {
title,
textarea,
}`;
const options = {
listen: true,
clientOnly: true,
};
const { data } = useSanityFetcher<object>(articleQuery, options);
return { data };
},
});
</script>

vuejs lifecycle hooks mounted push computed

I want to send the data from axios in mounted to the menuItem function in computed, but I have not been successful. Where am I doing wrong?
<template v-slot:MenuItem>
<MenuItems
v-for="(Item, Index) in menuItem"
:key="Index"
:items="Item"
:depth="Index"
>
<router-link :to="Item.path">{{ Item.name }}</router-link>
</MenuItems>
</template>
<script>
export default {
name: 'Nav',
data() {
return {}
},
computed: {
menuItem() {
return [
{
name: this.$t('navbar.home'),
path: '/',
},
{
name: this.$t('navbar.gallery'),
path: '/gallery',
},
{
name: this.$t('navbar.contact'),
path: '/contact',
},
]
},
},
async mounted() {
const res = await axios.get('http://localhost:3000/categories')
if (res.status === 200) {
const arr = res.data.data
arr.forEach((value, index) => {
this.menuItem.push({
name: value.title,
path: '/' + value.slug,
})
})
}
},
}
</script>

How do I display a route parameter (Vue / Firebase)

I'm creating a blog with free sewing patterns as content. I'm using route parameters to receive each blog individually. However, I'm getting a blank page when trying to retrieve its data from firebase firestore. Please help.
The blog's id appears on my address bar:
http://localhost:8080/#/admin/single-pattern/4LIS362IEWa7RKEv79g8
But it renders a blank page. I cant see my blog content.
This is my route path code. I've added a parameter of :id in my singlepattern. The SinglePattern component is where I will get the individual blog's data:
{
path: "/admin",
name: "admin",
component: Admin,
meta: {
auth: true,
},
children: [
{
path: "dashboard",
name: "dashboard",
component: Dashboard,
},
{
path: "manage-patterns",
name: "manage-patterns",
component: ManagePatterns,
},
{
path: "single-pattern/:id",
name: "single-pattern",
component: SinglePattern,
},
],
},
Here is my "ListPattern" component's code. ListPattern is where all my sewing blogs are displayed.
<template>
<div class="list-blogs">
<h1>LIST BLOG TITLES</h1>
<br />
<input type="text" v-model="search" placeholder="search blogs" />
<div
class="blog-cover"
v-for="pattern in filteredPatterns"
:key="pattern.id"
>
<div>
<router-link v-bind:to="'/admin/single-pattern/' + pattern.id">
<h3 style="cursor: pointer" v-rainbow>
{{ pattern.title | uppercase }}
</h3></router-link
>
</div>
<p
:style="'background-color: var(--lightgrey)'"
:inner-html.prop="pattern.description | snippet"
></p>
</div>
</div>
</template>
<script>
import firebase from "firebase";
import searchMixin from "../mixins/searchMixin";
// Basic Use - Covers most scenarios
import { VueEditor } from "vue2-editor";
import Quill from "quill";
const AlignStyle = Quill.import("attributors/style/align");
Quill.register(AlignStyle, true);
// import $ from "jquery";
import Swal from "sweetalert2";
window.Swal = Swal;
const Toast = Swal.mixin({
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
});
window.Toast = Toast;
export default {
name: "ManagePatterns",
components: { VueEditor },
data() {
return {
patterns: [],
pattern: {
title: null,
description: null,
image: null,
},
search: "",
};
},
firestore() {
return {
patterns: firebase.firestore().collection("free-patterns"),
};
},
computed: {},
},
};
</script>
And this is my 'SinglePattern' component where the clicked blog/pattern is displayed.
<template>
<div class="single-pattern">
<div class="blog-cover">
<div>
</div>
<div v-if="pattern">
<h3 style="cursor: pointer">
{{ pattern.title }}
</h3>
<div v-if="pattern.description">
<p
:style="'background-color: var(--lightgrey)'"
:inner-html.prop="pattern.description"
></p>
</div>
</div>
</div>
</div>
</template>
<script>
import firebase from "firebase";
import searchMixin from "../../mixins/searchMixin";
export default {
data() {
return {
id: this.$route.params.id,
patterns: [],
pattern: {
title: null,
description: null,
image: null,
},
};
},
firestore() {
return {
patterns: firebase.firestore().collection("free-patterns"),
};
},
mixins: [searchMixin],
created() {
console.log(this.$route.params.id);
var pat = this;
firebase
.firestore()
.collection("free-patterns")
.doc(this.$route.params.id)
.get()
.then(function(doc) {
if (doc.exists) {
pat.pattern = doc.data().pattern;
} else {
console.log("no such doc");
}
});
},
methods: {},
};
</script>
It works. I just had to change the code in my created() hook in 'SingePattern' component.
created() {
console.log(this.$route.params.id);
var docRef = firebase
.firestore()
.collection("free-patterns")
.doc(this.$route.params.id);
docRef
.get()
.then((doc) => {
if (doc.exists) {
this.pattern = doc.data();
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch((error) => {
console.log("Error getting document:", error);
});

vue router error with firebase auth

I tried to auth using vue.js and firebase.
and error occurs router.beforeEach function,
Anyone has any idea why it might happen?
console error
vue-router.esm.js?fe87:16 [vue-router] uncaught error during route navigation:
warn # vue-router.esm.js?fe87:16
abort # vue-router.esm.js?fe87:1904
iterator # vue-router.esm.js?fe87:1968
step # vue-router.esm.js?fe87:1717
runQueue # vue-router.esm.js?fe87:1725
confirmTransition # vue-router.esm.js?fe87:1972
transitionTo # vue-router.esm.js?fe87:1874
push # vue-router.esm.js?fe87:2181
(anonymous) # vue-router.esm.js?fe87:1960
(anonymous) # index.js?3672:44
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import addPost from '#/components/addPost'
import showPost from '#/components/showPost'
import Login from '#/components/Login'
import SignUp from '#/components/SignUp'
import firebase from 'firebase'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'app',
component: showPost
},
{
path: '/add',
component: addPost,
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/signup',
name: 'SignUp',
component: SignUp
}
]
})
router.beforeEach((to, from, next) => {
let currentUser = firebase.auth().currentUser;
let requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) next('/login')
else if (!requiresAuth && currentUser) next('/')
else next()
})
export default router
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import VueFire from 'vuefire'
import firebase from 'firebase'
Vue.use(VueFire)
Vue.config.productionTip = false
let app;
firebase.auth().onAuthStateChanged(function(user) {
if(!app) {
app = new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
}
})
App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
<button #click="logout">Logout</button>
</div>
</template>
<script>
import header from './components/header'
import firebase from 'firebase'
export default {
name: 'app',
components: {
'app-header': header
},
methods: {
logout: function() {
firebase.auth().signOut().then(() => {
this.$router.replace('login')
})
}
}
}
</script>
Login.vue
import firebase from 'firebase'
import db from '../firebaseInit'
const postRef = db.ref('posts')
export default {
name: 'login',
data: function() {
return {
email: '',
password: ''
}
},
methods: {
signIn: function() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
(user) => {
this.$router.replace('/')
},
(err) => {
alert('Oops ' + err.message)
}
);
}
}
}
</script>
addPost.vue
<template>
<div id="add-blog">
<h2>Add a New Post</h2>
<form v-if="!submitted">
<label>Title:</label>
<input type="text" v-model="newPost.title" required />
<p>{{ getDate }}</p>
<label for="">Content:</label>
<textarea v-model.trim="newPost.content"></textarea>
<div id="checkboxes">
<p>Categories:</p>
<label>Vue.js</label>
<input type="checkbox" value="vue" v-model="newPost.categories" />
<label>CSS Magic</label>
<input type="checkbox" value="css" v-model="newPost.categories" />
</div>
<label>Author:</label>
<select v-model="newPost.author">
<option v-for="author in authors">{{ author }}</option>
</select>
<button #click.prevent="addPost">Add Post</button>
</form>
<div v-if="submitted">
<p>Congraturation!</p>
</div>
<div id="preview">
<h3>Preview Post</h3>
<h4>Title {{ newPost.title }}</h4>
<h4>Content </h4>
<p style="white-space: pre">{{ newPost.content }}</p>
<ul>
<li v-for="category in newPost.categories">{{ category }}</li>
</ul>
<p>{{ newPost.author }}</p>
</div>
</div>
</template>
<script>
import db from '../firebaseInit'
const postRef = db.ref('posts')
export default {
data() {
return {
newPost: {
date: '',
title: '',
author: '',
content: '',
categories: []
},
authors: ['Naeun', 'Raphael'],
submitted: false,
items: []
}
},
methods: {
addPost: function() {
postRef.push(this.newPost)
this.newPost.date = '',
this.newPost.title = '',
this.newPost.author = '',
this.newPost.content = '',
this.newPost.categories = ''
},
removePost: function() {
postRef.child(post['.key']).remove()
}
},
computed: {
getDate: function() {
const toTwoDigits = num => num < 10 ? '0' + num : num;
let today = new Date();
let year = today.getFullYear();
let month = toTwoDigits(today.getMonth() + 1);
let day = toTwoDigits(today.getDate());
return this.newPost.date = `${year}-${month}-${day}`;
}
}
}
</script>
Make sure that the next function is called exactly once in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.
Your else if condition else if (!requiresAuth && currentUser) next('/') violates this rule.
Change your route guarding logic
// check if route requiresAuth
router.beforeEach((to, from, next) => {
if (to.matched.some(rec => rec.meta.requiresAuth)) {
const user = firebase.auth().currentUser;
// check auth state of user
user ? next() : next('/login') // user not signed in, route to login
} else {
next(); // route does not require auth
}
});
https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Hope this helps.

Resources