Passing component name to title field in Vue app - vue.js

I have a Vue app like this:
<div id="app">
<Navbar/>
<div class="main">
<h1>{{ title }}</h1>
<router-view/>
</div>
<Foot/>
</div>
As I change pages via router-links, I'd like to also update the {{title}} field. The hacky way I've done it is this:
methods: {
logRoute() {
this.$nextTick(() => {
this.title = this.$route.name
});
}
}
However, sometimes I'd like to have the title be something different according to a property in the component, for example the "About" page is titled "About me." What's the best way to approach this?

Hi Jasper try this way.
Go to your route and and a meta on the router you want to have a title on like the code below
const routes = [
{
path: "/",
name: "Home",
component: Home,
meta: {
title: 'Home'
}
},
]
And go to your App.vue and add this a watch to look up when you go to different pages if you dont have a meta with a title it will put a default value on the website title.
export default {
watch: {
'$route' (to) {
document.title = to.meta.title || 'Default Title'
}
},
};
I hope this solves your problem have a nice day !

Related

Pass data into router-view to conditionally redirect - Vue Router

I'm wanting to conditionally redirect to a page based on a user's settings.
I have my app where I've passed the user's setting:
<div id="app">
{{ $defaultStartingPage }} <!-- 'search-page' -->
<router-view></router-view>
</div>
I'd like to pass the $defaultStartingPage into my router-view and then handle it there, something like <router-view default-starting-page="{{ $defaultStartingPage }}"> but so far I haven't been able to.
I was able to set the variable to the window in my app.js file and then do this, but it's not reactive and it doesn't feel right.
export default {
routes: [
{
path: '/',
name: 'home',
redirect: () => {
if (window.defaultStartingPage) {
switch (window.defaultStartingPage) {
case 'service-appointments':
return { name: 'services-appointments' };
}
}
return { name: 'services-repairs' };
},
}
]
}
Is there a way to pass attributes to router-view and then access them when returning views? Or maybe a better way to do this?
Thanks!

How to achieve "visited recently" using Vue?

I'm trying to create a list of hyperlinks to the last 5 pages (views) visited by a user. However I don't really know where/ how to start...
I thought about somehow storing each route when visited but I don't know what's the best practice to do so.
I'm not asking for a working code, I just need some help to find the right direction to start with.
You can use router.beforeEach((to, from, next) =>{}) and store the url in an array. You can read more details about navigation guards here.
https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Found this, it may help:
https://codesandbox.io/s/mutable-glade-qnrxm
It stores the visited routes.
It works like this:
<template>
<div>
<span v-for="crumb in crumbs" :key="crumb.index">
<router-link :to="crumb.path">{{ crumb.name }}</router-link> |
</span>
</div>
</template>
<script>
export default {
data() {
return {
crumbs: []
};
},
watch: {
$route(to) {
if (this.crumbs.length > 3) {
this.crumbs.shift();
}
this.crumbs.push({
path: to.fullPath,
name: to.name/* */
});
}
},
mounted() {
this.crumbs.push({
path: this.$route.fullPath,
name: this.$route.name
});
}
};
</script>
Here, it gets the route name and path on mount and pushes it to crumbs array.
mounted() {
this.crumbs.push({
path: this.$route.fullPath,
name: this.$route.name
});
}
Then it constantly watches and changes on route. On change, it shifts(removes the last and adds a new one) any router if there are more than 3. And then it assignes the name and path for every new router.
watch: {
$route(to) {
if (this.crumbs.length > 3) {
this.crumbs.shift();
}
this.crumbs.push({
path: to.fullPath,
name: to.name/* */
});
}
},
And at last, it loops the crumbs and displays it.
<span v-for="crumb in crumbs" :key="crumb.index">
<router-link :to="crumb.path">{{ crumb.name }}</router-link> |
</span>

Where should route meta data be loaded in a Vue app?

I'm in the process of setting up a VueJs SPA. I'm using vue-router and I'm trying to find the best solution to the following problem. I have a series of routes. Each of which needs to call an API to get the meta data for the given ID.
/industry/:id/overview
/industry/:id/top-stories
/industry/:id/top-tweets
/brand/:id/overview
/brand/:id/top-stories
/brand/:id/top-tweets
I've been looking at using created or beforeRouteEnter/beforeRouteUpdate and I'm a bit lost. Ideally, I would only fetch new data when a new /industry/:id is reached, not when navigating between pages within the same ID. Also, I'd like to avoid having to define the fetch to grab data in every page component. Also don't want to over complicate this, so my question is, Is there a standard method for tackling this issue?
Clarification:
When I say meta here, I mean data returned from an API about the given industry or brand which I pull using the ID in the route. The api call includes the name of the industry/brand which I want to have on page as soon as the page is presented to the user.
I have something similar. I tackle this using the following approach:
I use the same component for all /industry/:id Vue likes to reuse components wherever it can so if two routes (for example /industry/:id/overview and /industry/:id/top-stories) are using the same component it will stay the same.
What does change, however, is the route meta. So if you add a page key to the meta object in the route objects, and probably add a computed property called page that return this.$route.meta.page, you can use v-if attributes to conditionally render any component. So you might have something like <div v-if="page === 'overview'"></div><div v-else-if="page==='top-stories'"></div>
What this allows you to do is fetch all the data from the API during created or mounted lifecycle and store it as the state. Since the route change doesn't reload the component the state stays the same.
Here is a code example
// router.js
const Project = () =>
import(/* webpackChunkName: "projects" */ "./views/projects/_id");
export default new Router({
mode: "history",
routes: [
{
path: "/projects/:project_id/views",
name: "ViewProject",
component: Project,
meta: {
page: "views",
}
},
{
path: "/projects/:project_id/export",
name: "ExportProject",
component: Project,
meta: {
page: "exports"
}
},
{
path: "/projects/:project_id/recommendations",
name: "ProjectRecommendations",
component: Project,
meta: {
page: "recommendations"
}
},
]
});
And here is the template
<template>
<div v-if="project">
<h1>{{ project.name }}</h1>
<router-link :to="/project/someid/views">Views</router-link>
<router-link :to="/project/someid/exports">Exports</router-link>
<router-link :to="/project/someid/recommendations">Recommendations</router-link>
<ul v-if="page==='views">
<li v-for="(view, i) in project.views" :key="i">{{ views }}</div>
</ul>
<ul v-else-if="page==='exports">
<li v-for="(export, i) in project.exports" :key="i">{{ export }}</div>
</ul>
<ul v-else-if="page==='recommendations">
<li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
project: null
}
},
computed: {
page() {
return this.$route.meta.page;
}
},
mounted() {
this.getProject()
},
methods: {
getProject() {
axios
.get(`/projects/someid`)
.then(res => this.project = res.data)
}
}
}
</script>

Vue: props passed to router-link

I want to have 3 main parts in my webapp:
App.vue - this page only has the <router-view> tag and some general configuration + it fetches an API every second
ControlPanel.vue - this page visualizes some data that the App.vue page gets
Profile.vue - this page visualizes some data that the App.vue page gets too
Right now I set up my App.vue with the API call and it passes the data it receives to the two pages with props like the following example. As you can see when it gets mounted it starts a loop that lasts 1 second where it goes and fetches the API and then it returns it to the two routes.
<template>
<div id="app">
<div id="nav">
<router-link :to="{ name: 'control_panel', params: { APIlogs } }">Control panel</router-link>
<span> | </span>
<router-link :to="{ name: 'profile', params: { APIlogs } }">Profile</router-link>
</div>
<router-view/>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
APIlogs: '',
};
},
mounted() {
setInterval(() => this.refreshData(), 1000);
},
methods: {
refreshData() {
axios.get('http://192.168.30.65:5000/logs')
.then((response) => {
this.APIlogs = response.data;
});
},
},
};
</script>
<style>
...
</style>
On the other hand, Control Panel and Profile are fundamentally the same page and they should get the props from the "father" and use it to visualize data but right now it doesn't work. When I click on one route it shows me the value the prop has in that moment and doesn't update as the App.vue page fetches more and more data.
<template>
<div id="app">
{{APIlogs}}
</div>
</template>
<script lang="ts">
import axios from 'axios';
export default {
name: 'control-panel',
props: ['APIlogs'],
data() {
return {
};
},
mounted(){
console.log(this.APIlogs);
},
methods: {
},
};
</script>
<style>
...
</style>
Did I do something wrong? Is my implementation good enough or is it lacking in some way? Really hope someone can help me out with this one, it's really tearing me apart.
Thanks a lot in advance
EDIT
Just to give a bit more context, before having props I was calling the same exact API from both components and it seemd very inefficient to me so I switched to this method.
Also my router.ts looks like this:
import Vue from 'vue';
import Router from 'vue-router';
import ControlPanel from '../src/components/ControlPanel.vue';
import Profile from '../src/components/Profile.vue';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'control_panel',
component: ControlPanel,
props: true,
},
{
path: '/profile',
name: 'profile',
component: Profile,
props: true,
},
],
});
there's no params inside your paths i.e: path: '/:apilogs'
A dynamic segment is denoted by a colon :. When a route is matched,
the value of the dynamic segments will be exposed as
this.$route.params in every component.
(source)
After a while and almost an entire afternoon wasted on this problem, I found out this article which helped me achieve my goal. I just created a file with all my api calls and I call it every time I need to fetch something. It's a way more elegant and intelligent solution I think.
An easy way to make this work is to just make your APIlogs an object. Then it would be passed by reference and any updates to it will be reflected in the other components ..
export default {
data() {
return {
APIlogs: {logs: ''},
};
},
mounted() {
setInterval(() => this.refreshData(), 1000);
},
methods: {
refreshData() {
axios.get('http://192.168.30.65:5000/logs')
.then((response) => {
this.APIlogs.logs = response.data;
});
},
},
};
<template>
<div id="app">
{{APIlogs.logs}}
</div>
</template>
PS: You should probably use clearInterval in your beforeDestroy hook.

Navigating vuejs SPA via routes that share component does not refresh component data as expected

I have a couple routes in my vuejs SPA that I have set up using vue-router:
/create/feedback
/edit/feedback/66a0660662674061b84e8ea2fface0e4
The component for each route is the same form with a bit of smarts to change form values based on the absence or present of the ID in the route (feedbackID, in my example).
I notice that when I click from the edit route to the create route, the data in my form does not clear.
Below is the gist of my route file
import FeedbackFormView from './components/FeedbackForm.vue'
// Routes
const routes = [
{
path: '/create/feedback',
component: FeedbackFormView,
name: 'FeedbackCreate',
meta: {
description: 'Create Feedback',
}
},
{
path: '/edit/feedback/:feedbackId',
component: FeedbackFormView,
name: 'FeedbackEdit',
meta: {
description: 'Edit Feedback Form'
},
props: true
}
]
export default routes
Below is the gist of my component
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
}
}
</script>
However, if I modify my component as follows, everything works as expected
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
},
watch: {
'$route' (to, from) {
if (to.path === '/create/feedback') {
this.model = {}
}
}
}
}
</script>
Why is this? Why do I need watch?
I would have though that changing routes would be sufficient as the purpose of routing is to mimic the semantic behavior of page navigation
You have same component for different routes, when you go to edit route from the create route component is already created and mounted so the state of the component doesn't clear up.
Your component can listen to route changes using $router provided by vue-router every time the route changes the watcher is called.
For those who come this later, the following answer addresses the issue I was facing:
Vue-Router: view returning to login page after page refresh