How to retrieve data from vue component in another component? - javascript

There is a vue royter
.....
const Edit = Vue.component('edit', require('./components/pages/edit.vue'));
const Product_category = Vue.component('home', require('./components/pages/product_categories.vue'));
const routes = [
........
{
path: '/product_categories',
name: 'product_categories',
component: Product_category
},
{
path: '/product_categories/edit/:id',
name: 'product_categories_edit',
components: {default: Edit, entity: Product_category},
props: {default: true, entity: true}
}
];
How can I get data in component Product_category componate Edit?
<script>
export default {
props: ['id', 'entity'],
mounted: function () {
console.log('Admin edit page mounted.');
console.log(this.entity); // Eror
console.log(entity); // Eror
this.getData();
},
}
</script>
A direct appeal is not suitable, because each router will have its own entity.

Use vuex if you prefer a global state-object. It's properties can be mapped to each instance and component https://github.com/vuejs/vuex
If you prefer an event based approach use an event bus https://alligator.io/vuejs/global-event-bus/
It is all well described at multiple positions in the official vuejs documentation.

Related

How to get a specific child component object property from parent component?

I would like to grab a child component's "meta" property from parent. Is it possible somehow ?
I know there is a solution with an emit method, but is there some easier way to make it happen ?
// Default.vue <-- parent component
<template>
<h1>{{ pagetitle }}</h1>
<router-view />
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'LayoutDefault',
computed: {
pagetitle () {
let title = this.$route.meta.title // <--- I want to access child's component meta here
// if title not provided, set to empty string
if (!title) title = ''
return title
}
}
})
</script>
// router/routes.js
const routes = [
{
path: '/',
component: () => import('layouts/Default.vue'),
children: [
{
path: 'dashboard',
name: 'dashboard',
meta: { title: 'Dashboard', auth: true, fullscreen: false }, // <--- TAKE THIS
component: () => import('pages/dashboard.vue')
}
]
}
]
// pages/dashboard.vue <-- child component
<template>
<div>
dashboard content
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Dashboard',
meta: { // <--- this should be reachable from the parent component (Default.vue)
title: 'Dashboard',
auth: true,
fullscreen: false
}
})
</script>
You can get component info via $route.matched.
Here's a PoC:
const Dashboard = Vue.defineComponent({
template: "<div>Some dashboard</div>",
meta: { title: "Dashboard" },
})
const router = new VueRouter({
routes: [{ path: "/", component: Dashboard }],
})
const app = new Vue({
router,
computed: {
// Note that this takes the *last* matched component, since there could be a multiple ones
childComponent: (vm) => vm.$route.matched.at(-1).components.default,
},
}).$mount('#app')
<div id="app">
<h1>{{ childComponent.meta.title }}</h1>
<router-view />
</div>
<script src="https://unpkg.com/vue#2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router#3/dist/vue-router.js"></script>
As suggested by Estus Flash in a comment, instead of taking the last matched component we can take the last matched component that has meta defined. To do that, replace the following:
vm.$route.matched.at(-1).components.default
with:
vm.$route.matched.findLast((r) => "meta" in r.components.default)
.components.default
Some approaches I could figure from the web:
Using ref by this.$refs.REF_NAME.$data (As done here: https://stackoverflow.com/a/63872783/16045352)
Vuex or duplicating the logic behind stores (As done here: https://stackoverflow.com/a/40411389/16045352)
Source: VueJS access child component's data from parent

How to get query params in Vue.js 3 setup?

I want to make search page which after I click its button will be redirected to another page. And this page will be like this
http://localhost:8080/search?q=foo
and my router index.js looks like this
const routers = [
{
path: '/search',
name: 'Search',
component: SearchPage,
props: route => ( { query: route.query.q } )
}
]
and the question is how do i get the query value in target page SearchPage, in Vue.js 3?
This Answer is still confusing me, because not using composition API and not in vuejs 3
Using useRoute
You don't need to send the query as a prop. It's better to use useRoute because it's simpler and it can be accessed from any component, not just the page view component.
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
console.log(route.query);
}
}
Using a prop
First change the router mapping so that query maps to the query object:
props: route => ({ query: route.query })
In the destination component, SearchPage, create a query prop which will receive the query object from the router:
props: ['query']
And access it in setup via the props argument:
props: ['query'],
setup(props) {
console.log(props.query.q);
console.log(props.query.status);
}
another setup you can try, send propname by url
{ path: '/search/:propname', name: 'Search', component: SearchPage, props: true },
and on searchpage, on created() you can get recive it
this.$route.params.propname
For Vue Router 4 :: Compositon API :: Vue3
Router-link attach params
<router-link
style="width: 100%"
class="btn btn-success btn-block edit"
:to="{ name: 'editUser',params: {id: user.id}}">
<i class="fa-solid fa-user-gear"></i>
</router-link>
In a page you are receiving,
<script>
export default {
props: ["id"],
setup(props, context) {
console.log(props.id);
},
};
</script>
In Your app.js
import { createApp } from 'vue';
...
...
const app = createApp(root);
app.use(router);
router.isReady().then(() => {
app.mount('#app');
})

VueJS $emit and $on not working in one component to another component page

I am trying to long time Vue $emit and $on functionality but still I am not getting any solution. I created simple message passing page its two pages only. One is Component Sender and Component Receiver and added eventbus for $emit and $on functionality.
I declared $emit and $on functionality but I don't know where I made a mistake.
please help some one.
Component Sender:
<script>
import { EventBus } from '../main';
export default {
name: 'Send',
data () {
return {
text: '',
receiveText: ''
}
},
methods: {
sender() {
EventBus.$emit('message', this.text);
this.$router.push('/receive');
}
}
}
</script>
Component Receiver:
<script>
import { EventBus } from '../main';
export default {
name: 'Receive',
props: ["message"],
data () {
return {
list: null
}
},
created() {
EventBus.$on('message', this.Receive);
},
methods: {
Receive(text){
console.log('text', text);
this.list = text;
},
save() {
alert('list', this.list);//need to list value but $emit is not working here
}
}
}
</script>
Router View:
const router = new Router({
mode: 'history',
routes: [
{
path: "/",
name: "Send",
component: Send
},
{
path: "/receive",
name: "Receive",
component: Receive,
props: true
}
]
})
Main.JS
import Vue from 'vue'
import App from './App.vue'
import router from './router';
export const EventBus = new Vue();
new Vue({
el: '#app',
router,
render: h => h(App)
})
The EventBus is designed to allow communication between two components that exist on the page at the same time. If you need data that persists between router-views, then you should look at using a Vuex store.
As it is, the Receiver component doesn't exist at the time of the message being sent, and therefore it has no listener attached to the event.
Well your mistake is here:
created() {
EventBus.$on('message', this.Receive);
},
the second argument is an handler it should look like this:
created() {
EventBus.$on('message', function(data){
this.Receive(data);
console.log(data)
});
},
you should see now 2 console.log() messages

Vue/Nuxt/Vuex - [NUXT:SSR] [ERROR] [vuex] unknown getter

The error appears when I use a v-for loop to go through the 'allPosts' data on my div.
The Nuxt documentation says 'Modules: every .js file inside the store directory is transformed as a namespaced module'. Maybe I'm missing something in this regard?
pages/index.vue
<template>
<section id="postgrid">
<div v-for="post in allPosts" :key="post.id"></div>
</section>
</template>
<script>
import {mapGetters} from 'vuex'
import PostTile from '#/components/Blog/PostTile'
export default {
components: {
PostTile
},
computed: mapGetters(['allPosts'])
}
</script>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import Posts from './posts'
const store = new Vuex.Store({
modules: {
Posts
}
})
store/posts.js
const state = () => ({
posts: [
{
id: 0,
title: 'A new beginning',
previewText: 'This will be awesome don\'t miss it',
category: 'Food',
featured_image: 'http://getwallpapers.com/wallpaper/full/6/9/8/668959.jpg',
slug: 'a-new-beginning',
post_body: '<p>Post body here</p>',
next_post_slug: 'a-second-beginning'
},
{
id: 1,
title: 'A second beginning',
previewText: 'This will be awesome don\'t miss it',
category: 'Venues',
featured_image: 'https://images.wallpaperscraft.com/image/beautiful_scenery_mountains_lake_nature_93318_1920x1080.jpg',
slug: 'a-second-beginning',
post_body: '<p>Post body here</p>',
prev_post_slug: 'a-new-beginning',
next_post_slug: 'a-third-beginning'
},
{
id: 2,
title: 'A third beginning',
previewText: 'This will be awesome don\'t miss it',
category: 'Experiences',
featured_image: 'http://eskipaper.com/images/beautiful-reflective-wallpaper-1.jpg',
slug: 'a-third-beginning',
post_body: '<p>Post body here</p>',
prev_post_slug: 'a-second-beginning',
next_post_slug: 'a-forth-beginning'
}
]
})
const getters = {
allPosts: (state) => state.posts
}
export default {
state,
getters
}
You have a number of issues in how you are setting up and accessing your store. Firstly you are creating your store using the "classic mode" which the docs tell us:
This feature is deprecated and will be removed in Nuxt 3.
So in order to be using the latest methods your store/index.js should look like this:
//store/index.js
//end
This is not a mistake, you don't actually need anything in it, just have it exist. There is no need to import vue or vuex or any modules.
Your store/posts.js can largely stay as it is, just change your state, mutations, getters, and actions to be exported constants and delete the bottom export:
//store/posts.js
export const state = () => ({
posts: [
...
]
})
export const mutations = {
}
export const actions = {
}
export const getters = {
allPosts: state => state.posts
}
//delete the following
export default {
state,
getters
}
Secondly you seem to be using mapGetters incorrectly. If you set up your store like I have above, you can use it in pages/index.vue like so:
//pages.index.vue
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters ({
allposts: 'posts/allPosts'
})
}
}
</script>
Then you can access "allPosts" in your template as you would any computed property or access it with "this.allPosts" in your script.

Basic Vue help: Accessing JS object values in component

I’ve been experimenting with vue.js and I'm having difficulty accessing JS object values in components when routing.
Using this repo to experiment, https://github.com/johnayeni/filter-app-vue-js, I'm just trying to replicate a basic a “product list” and “product description” app, but I can't get it working. The repo's homepage (the SearchPage.vue component) serves as the "product list," and I'm just trying to add the "product description" component to display only one item at a time.
I've added a "description page" component (calling it "item.vue") to allow a user to click on one of the languages/frameworks that will then route to item.vue to just display that specific object's associated information (item.name, item.logo, etc.), i.e., and not display any of the other languages.
Following some tutorials, here's what I've tried:
First, I added ids to the JS objects (found in data/data.js), i.e., id:'1'.
const data = [
{
id: '1',
name: 'vue js',
logo: 'http://... .png',
stack: [ 'framework', 'frontend', 'web', 'mobile' ],
},
{
id: '2',
name: 'react js',
logo: 'http://... .png',
stack: [ 'framework', 'frontend', 'web', 'mobile' ]
},
...
];
export default data
Then, I wrapped the item.name (in ItemCard.vue) in router-link tags:
<router-link :to="'/item/'+item.id"> {{ item.name}} </router-link>
I then added a new path in router/index.js:
{
path: './item/:id',
component: item,
props: true
}
But, when that router-link is clicked I can only access the ".id" (via $route.params.id), but I can't get .name or .logo. How do I access the other values (i.e. item.name, item.logo, etc.)? I have a feeling I'm going down the wrong track here.
Thank you so much for your help.
The only reason you have access the id because it's an url param: ./item/:id.
You have a couple options here, which depends on what you're trying to accomplish:
As suggested by #dziraf, you can use vuex to create a store, which in turn would give you access to all the data at any point in your app:
export default {
computed: {
data() {
return this.$store.data;
}
}
}
Learn more here: https://vuex.vuejs.org/
As an alternative, you can just import your data, and grab the correct item by its id:
import data from './data.js';
export default {
computed: {
data() {
return data.find(d => d.id === this.$route.params.id);
}
}
}
Just depends on what you're trying to do.
I guess you just need a wrapper component that takes the desired item from the URL and renders the proper item. Let's say an ItemWrapper:
<template>
<item-card :item="item"></item-card>
</template>
<script>
import ItemCard from './ItemCard.vue';
import data from '../data/data';
export default {
components: {
ItemCard,
},
props: {
stackNameUrl: {
required: true,
type: String,
},
},
data() {
return {
item: {},
}
},
computed: {
stackName() {
return decodeURI(this.stackNameUrl);
}
},
created() {
this.item = data.find( fw => fw.name === this.stackName);
}
}
</script>
<style>
</style>
This component takes a prop which is a stack/fw name uri encoded, decodes it, finds the fw from data based on such string, and renders an ItemCard with the fw item.
For this to work we need to setup the router so /item/vue js f.i. renders ItemWrapper with 'vue js' as the stackNameUrl prop. To do so, the important bit is to set props as true:
import Vue from 'vue';
import Router from 'vue-router';
import SearchPage from '#/components/SearchPage';
import ItemWrapper from '#/components/ItemWrapper';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'SearchPage',
component: SearchPage
},
{
path: '/item/:stackNameUrl',
name: 'ItemWrapper',
component: ItemWrapper,
props: true,
},
]
});
Now we need to modify SearchPage.vue to let the stack boxes act as links. Instead of:
<!-- iterate data -->
<item-card v-for="(item, index) in filteredData" :key="index" :item="item"></item-card>
we now place:
<template v-for="(item, index) in filteredData" >
<router-link :to="'/item/' + item.name" :key="index">
<item-card :key="index" :item="item"></item-card>
</router-link>
</template>
So now every component is placed within a link to item/name.
And voilá.
Some considerations:
the :param is key for the vue router to work. You wanted to use it to render the ItemCard itself. That could work, but you would need to retrieve the fw from data from the component created(). This ties your card component with data.js which is bad, because such component is meant to be reusable, and take an item param is much better than go grabbing data from a file in such scenario. So a ItemWrapper was created that sort of proxies the request and pick the correct framework for the card.
You should still check for cases when an user types a bad string.
Explore Vue in depth before going for vuex solutions. Vuex is great but usually leads to brittle code and shouldn't be overused.

Categories

Resources