VueJs use props that comes from <router-link> - vue.js

i have a navbar and there is a text field in that the user can search for posts by tags. If the user enters 1-3 tags, the written tags will be stored in a tags array.
My router-link in the navbar component looks like this: (only relevant part)
<router-link :to="{name:'posts', props:{searchTags: tags}}">
<button type="button" v-if="this.tags.length > 0"
class="...">Search
</button>
</router-link>
in my routes.js is my posts route (important snippet of my routes.js)
routes: [
{
path: "/posts",
component: posts,
name: 'posts'
},
]
The navbar should send the tags array to the posts component. Unfortunately I can't do it.
The posts component, sends a post request to an API that gets the latest posts. But I want that when tags are passed, not the newest posts are fetched, only posts with certain tags. But first I have to get the tags somehow.
I tried to get them with "this.$props.searchTags" and other things. Unfortunately the result is always "undefined".
export default {
name: "posts",
props: {
searchTags: Array,
required: false
},
data: function () {
return {
apiUrl: '/getPosts',
....
tags: [this.searchTags],
}
},
methods: {
getPosts: function (url) {
this.$http.get(url).then(function (data) {
// blabla
});
},
getPostsByTags: function() {
//
},
},
created() {
if(this.$props.searchTags == null)
this.getPosts(this.apiUrl);
else
this.getPostsByTags(bla);
},
}

Router link to property accepts string or Location as a value. Location object does not have props property.
Instead, it is possible to use params to send data to route component:
<router-link
:to="{ name: 'posts', params: { searchTags: tags } }"
>
...
</router-link>
This way searchTags with value of assigned tags will be accessible via this.$route.params.searchTags inside destination component.
So created hook of example above should be updated to:
created () {
if (!this.$route.params.searchTags) {
this.getPosts(this.apiUrl);
} else {
this.getPostsByTags(bla);
}
},

Try to add props: true in your route definition
routes: [
{
path: "/posts",
component: posts,
name: 'posts',
props: true
},
]

Related

How do you build page within a folder in Vue?

I'm trying to have my URL as /social/fb/, i've tried looking online and documentation but can't find the answer to this simple question for the life of me.
My folder structure is:
Pages
-social
--fb
I have a social.vue file in pages which works fine as www.example.com/social but can't get www.example.com/social/fb. Any direction would be much appreciated.
Create social folder inside pages, then create fb.vue inside social folder.
This should work
I normally load the layout.vue from the router and treat everything else as a child which is passed though as a router-view, this then saves having an index.vue for each parent.
But you would always want to make a directory to contain the social pages. Then would be a case of simply adding to the router.js file.
layouts/template.vue
<template>
<router-view></router-view>
</template>
<script>
export default {
name: 'layout-template'
}
</script>
router.js
...
/*
* Social
*/
{
path: '/social',
component: () => import('./layout/template.vue'),
props: true,
// rendered inside <router-view>
children: [{
path: '/',
component: () => import('./pages/social/index.vue')
}, {
path: 'fb',
component: () => import('./pages/social/fb.vue')
}, {
path: 'twitter',
component: () => import('./pages/social/twitter.vue')
},
// or do something more dynamic
{
path: ':network', // accessible in component though `$route.params.network`
props: true, // makes accessible though `props: ['network']`
component: () => import('./pages/social/network.vue')
}]
},
...
./pages/social/index.vue - could show something /social homepage or change route to import('./pages/not-found.vue') instead.
./pages/social/network.vue
<template>
...
</template>
<script>
export default {
name: "page-social-network",
props: {
network: {
type: String,
default: ''
}
},
created() {
// or through
this.$route.params.network
}
};
</script>
<style lang="scss" scoped></style>
See: https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode
Otherwise is just standard vue pages

Vuex state not being initialised before router-view component being rendered - undefined error

I am relatively new to vue and have run into a small issue. I am rendering a component that depends on the state stored in vuex. I load this information in from a json file in the main part of the app. It all works fine if I always land on the root (index.html) of the app when it loads up. However, if I refresh the app from a page that is dynamically generated from the router I hit an error:
[Vue warn]: Error in render: "TypeError: Cannot read property 'name' of undefined"
found in
---> <Room>
<RoomsOverview>
<Root>
As far as I can tell what is happening is that that the component is trying to access the state in vuex but it has not been initialised. Here is the component (Room.vue):
<template>
<div id="room">
<h2>{{ roomName }}</h2>
<div v-for="device in deviceList" v-bind:key="deviceList.name">
{{ device.name }} - {{ device.function}}
<svg-gauge v-bind:g-value="device.value" v-bind:g-min="0" v-bind:g-max="50" v-bind:g-decplace="1" g-units="℃">
<template v-slot:title>
Temperature
</template>
</svg-gauge>
</div>
</div>
</template>
<script>
module.exports = {
name: 'room',
/** Load external component files
* Make sure there is no leading / in the name
* To load from the common folder use like: 'common/component-name.vue' */
components: {
'svg-gauge': httpVueLoader('components/DisplayGauge.vue'),
}, // --- End of components --- //
data() {
return {
};
},
computed: {
roomName() {
// return this.$route.params.roomId;
return this.$store.getters['rooms/getRoomById'](this.$route.params.roomId);
},
deviceList() {
return this.$store.getters['rooms/getDevicesinRoom'](this.$route.params.roomId);
},
},
}
</script>
The error is triggered by the line
return this.$store.getters['rooms/getRoomById'](this.$route.params.roomId);
This tries to access the current state in the getter:
getRoomById: (state) => (id) => {
return state.rooms.find(room => room.id === id).name; // Needs fixing!
},
but it seems that the array:
// Initial state
const stateInitial = {
rooms: [],
};
has not been initialised under these circumstances. The initialisation occurs in the main entry point to the app in index.js in the mounted hook
// Load data from node-red into state
vueApp.$store.dispatch('rooms/loadRooms')
Where loadRooms uses axios to get the data. This works as expected if I arrive at the root of the site (http://192.168.0.136:1880/uibuilderadvanced/#/) but not if I arrive at a link such as (http://192.168.0.136:1880/uibuilderadvanced/#/rooms/office). I suspect it is all down to the order of things happening and my brain has not quite thought things through. If anyone has any ideas as to how to catch this I would be grateful - some kind of watcher is required I think, or a v-if (but I cannot see where to put this as the Room.vue is created dynamically by the router - see below).
Thanks
Martyn
Further information:
The room component is itself generated by router-view from within a parent (RoomsOverview.vue):
<template>
<div id="rooms">
<b-alert variant="info" :show="!hasRooms">
<p>
There are no rooms available yet. Pass a message that defines a room id and device id
to the uibuilder node first. See <router-link :to="{name: 'usage_info'}">the setup information</router-link>
for instructions on how start using the interface.
</p>
</b-alert>
<router-view></router-view>
</div>
</template>
<script>
module.exports = {
name: 'RoomsOverview',
data() {
return {
};
},
computed: {
hasRooms() {
return this.$store.getters['rooms/nRooms'] > 0;
},
roomList() {
return this.$store.getters['rooms/getAllRooms'];
},
},
}
</script>
and is dependent on the router file:
const IndexView = httpVueLoader('./views/IndexView.vue');
const AdminView = httpVueLoader('./views/AdminView.vue');
export default {
routes: [
{
path: '/',
name: 'index',
components: {
default: IndexView,
menu: HeaderMenu,
},
},
{
path: '/rooms',
name: 'rooms_overview',
components: {
default: httpVueLoader('./components/RoomsOverview.vue'),
menu: HeaderMenu,
},
children: [
{
path: ':roomId',
name: 'room',
component: httpVueLoader('./components/Room.vue'),
},
],
},
{
path: '/admin',
name: 'admin',
components: {
default: AdminView,
menu: HeaderMenu,
},
children: [
{
path: 'info',
name: 'usage_info',
component: httpVueLoader('./components/UsageInformation.vue'),
}
]
},
],
};
It seems you already got where the issue is.
When you land on you main entry point, the axios call is triggered and you have all the data you need in the store. But if you land on the component itself, the axios call does not get triggered and your store is empty.
To solve you can add some conditional logic to your component, to do an axios call if the required data is undefined or empty.

Matching route to preselected value in VueJS

In the first componen I have several buttons that should redirect to certain nested route based on which button was clicked.
<btn to="financials/financialdata"></btn>
<btn to="financials/revenuedata"></btn>
This route displays a component that should be different depending on the route name. The most important thing is the options form where the value that is preselected matches the route name but a bit differently.
Here is how i confiugred that route in router
{ path: '/financials/:name', component: dataview}
This is my select form:
<v-select
:items="datasets"
label="Solo field"
item-text="text"
item-value="value"
v-model="data"
dense
solo
></v-select>
And here is the options:
datasets: [
{
text: "Financial Condition",
value: 'A'
},
{
text: "Revenue Data",
value:'B'
}]
So how can match the route name with the value in the dataset, so that i can be preselected in the select form?
I also want to display which route is currently displayed in the title on the component page:
<h1>{{title}}</h1>
So based on the route param it would display the same title as selected in the form (so based on the dataset)
in your router config... add a name to the route or use meta parameter
in routes file:
{
path: "/revenue",
name: "Revenue",
component: Revenue
}
in select component:
<script>
export default {
data() {
return {
datasets: [
{
text: "Financial Condition",
value: "A"
},
{
text: "Revenue Data",
value: "Revenue"
}
]
};
},
mounted() {
this.selectValue = this.$route.name;
}
};
</script>
In your component, in the created function you could set or update the datasets or what you are binding the form to with the route params.
created: function () {
this.yourformdata = this.$route.params.thevalue
},

Dynamic Vue Router

I am researching whether a vue router is the best approach for the following scenario:
I have a page containing 'n' number of divs. Each of the divs have different content inside them. When a user clicks on a button in the div, I would like the div to open in a separate browser window (including its contents).
Can a route name/component be created on the fly to route to? Since I have 'n' number of divs, that are created dynamically, I cannot hard-code name/components for each one
<router-link :to="{ name: 'fooRoute'}" target="_blank">
Link Text
</router-link>
I want to avoid the same component instance being used (via route with params) since I may want multiple divs to be open at the same time (each one in their own browser window)
If the link is opening in a separate window, it makes no sense to use a <router-link> component as the application will load from scratch in any case. You can use an anchor element instead and generate the href property dynamically for each div.
To answer your questions:
A route name cannot be created dynamically since all routes must be defined at the beginning, when the app (along with router) is being initialized. That said, you can have a dynamic route and then dynamically generate different paths that will be matched by that route.
There is no way for the same component instance to be reused if it's running in a separate browser window/tab.
It is possible to create dynamic router name.
profileList.vue
<template>
<main>
<b-container>
<b-card
v-for="username in ['a', 'b']"
:key="username"
>
<b-link :to="{ name: profileType + 'Profile', params: { [profileType + 'name']: username }}">Details</b-link>
</b-container>
</main>
</template>
<script>
export default {
name: 'profileList',
data () {
return {
profileType: ''
}
},
watch: {
// Call again the method if the route changes.
'$route': function () {
this.whatPageLoaded()
}
},
created () {
this.whatPageLoaded()
},
methods: {
whatPageLoaded () {
this.profileType = this.$route.path // /user or /place
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
b-container, b-card, b-link are taken from bootstrap-vue, so you can freely change it.
router.js
const router = new Router({
mode: 'hash',
base: process.env.BASE_URL,
linkExactActiveClass: 'active',
routes: [
// USERS
{
path: '/user/:username',
name: userProfile,
component: userProfile
},
{
path: '/user',
name: 'userList',
component: profileList
},
// PLACES
{
path: '/place/:placename',
name: placeProfile,
component: placeProfile
},
{
path: '/place',
name: 'placeList',
component: ProfileList
}
]
})

Vue 2 make a div Clickable and image

I am playing around with Vue 2, and I want to make a whole div clickable.
This div have a link, image and text.
I used router-link for links in header and other links but when I try to use something else the page keeps refreshing.
Can someone please help me get over this somehow..
Cheers!
Add click event to you <div> that you want to be clickable as below:
<div #click="clickMethod"></div>
Now in your methods property add rhe clickMethod callback that you want to fire when clicked like below
methods: {
clickMethod() {
//add code that you wish to happen on click
}
}:
For anyone who is stuck here like I did on how to make a Div Clickable
<div #click="clickeMethod">
<p> Some Text Here </p>
</div>
script:
<script>
export default {
name: 'headers',
data() {
return {
};
},
methods: {
clickMethod() {
this.$router.push('home');
},
},
};
</script>
This Event will make a div Clickable.
Hope I helped someone :) and thnx to #user7814783
For those wondering how router.push method works, below are various ways you can use the method:
// literal string path
router.push('home')
// object
router.push({ path: 'home' })
// named route
router.push({ name: 'user', params: { userId: '123' } })
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
For me this implementation worked best:
<script>
export default {
name: 'home',
data() {
return {
};
},
methods: {
clickMethod() {
this.$router.push({ path: 'home' });
},
},
};
</script>