I have a page in Nuxtjs where, on creation I get some data from a vuex store making an axis call to my backend.
In the page I have a sub component that is a table, currently in the sub component I have
computed: {
...mapGetters ({
cases: 'cases/cases',
}),
},
in my table I access the results array by
:items=returnedcases
This works fine but I d like to learn how to make the sub component more reusable by moving this code into the parent page.
So how can I move this to the parent page component so I can make the table component reusable?
the table will have pagination and filtering.
From what I have read I can't use props for this so any guidance please on the correct approach would be fantastic
Thanks
Related
Consider the following Widget component.
<template>
<component :is="name" />
</template>
<script>
import { mapActions } from 'vuex';
export default {
props: {
name: {
type: String,
required: true
}
},
created() {
this.addWidget(this.name);
},
methods: {
...mapActions({
addWidget: 'widgets/add'
}) // adds widget name to an array
}
};
</script>
I want to have multiple components like this one all over the page.
I need to be able to gather all of the component names so I can fetch the relevant data for them.
My current idea is to add the component name to the store when the created hook fires.
That works just fine, but my problem is that I can't find a way to dispatch a Vuex action to fetch the data once all of the components have been created. I tried using various hooks to no avail since none of them seems to have Vuex access.
Am I missing something? Is there some kind of hook that fires after all of the components have been created (SSR) that has Vuex access. Or maybe there's some better way to achieve what I've been trying to do?
Why do you want to wait until all the widgets have been loaded to fetch the data? Instead, what I'd do is fetch the data for each Component as they get added in the page. Using this approach, each component would require a specific piece of data, and nothing better than to load each of them in a different request. Easier to test, easier to trace, adheres to the single responsibility principle.
If you are worried that two components may require the same data, then you can route all requests through an Object that hashes the request (endpoint + verb if using REST) and keeps a pool of active connections, so that if two requests come in one reuses the Promise of the other, and only one of them reaches the server.
Now, if you really want to do it using your original approach, make it so that each widget emits an event once it's been registered. The events are collected by the parent component (maybe the Page that loads all the widgets). The page knows which Widgets are being loaded. Every time an event is received it sets a flag on that specific widget to indicate it's been loaded. Once all widgets get a flag, the Page triggers a request to fetch the data.
I have a Vue 3 project where I have a parent component that loads other component that then loads another one that loads another one.
To give you an idea, I have a button component. The button is added multiple times in a set of numbers, the set goes into a card and the card goes into the main view component.
So 4 levels:
Parent Main view component with X cards (in this case I only have one)
Numbers Card component with X groups
Numbers Group component with X numbers
Number component used in Group
I have a binded var on the Number component that sets it active or not, by being true or false.
I want a way to set a function in my <script setup> tag in the parent top Main component (or one component inside of it) to turn one or multiple of the buttons components one and off (active true or false).
Does that makes sense?
I'm thinking if I need to use emit or just state data from my vuex data store. But I'm a bit confused on how to identify the specific button component (used multiple times) from the main parent 4 levels up component...
(In reality I want to add another type of button component there to activate that, toggle all other buttons on or off - But I think that if I understand how can from one function in one component I can access a variable in another set of components that will solve my issue...)
Let me know if this makes sense and if you can help me.
Feel free to point me to the Vue 3 Docs, I have been trying to find a solution but I'm not sure exactly where to look and because I'm using <script setup> new tag I think a lot of the examples there don't work for me.
Any little help is appreciated. Thanks so much!
Thanks #Matt. I ended up using the Vuex variable as an array.
Then in my Button component I have this in my <script setup> for the isPicked variable, for example:
const isPicked = computed(() => {
return store.state.session.btnsPicked.find(
(i) => i === props.btn
)
})
So my component variable now reacts to the Vuex states values.
I'm making a wrapper component and have to clear the vuex when I close the component.
I've registered this component in a menu, and parent component is default main page(init page when page is loaded).
So basically, I have to get the data from main component(which is Map(geo json) and displays markers) to pass down(i'm using vuex to share data) the data to grand-child component which is a wrapper component(leaflet-draw) to display maker(main component, grand parent component) information at grand-child component(menu)
But whenever I reopen the grand-child component, it keeps add up existing data. so let say there's 10 data in main component, and then whenever I open it, it just keep adds data cuz it's a wrapper component, and I have to use mounted() hook to get all data info.
so mounted() hook is called everytime I open it, but when beforeDestroy() is called, mounted() hook doesn't work anymore...
Could you please tell me how to use beforeDestory() or destroyed() hook correctly for the wrapper component...?
Many thanks.
It's over 8000 lines so let me know if you need a test case. I will add the github link.
I would add it as a comment but it says that i need 50 rep anyway; as far as i understood you are fetching the data when your wrapper component mounts so; maybe you should write sth like: if state is populated empty the state then fetch the new data in the function where you fetch the data which should be in your actions.
Sorry if this is a repeat - I have had a look around and spent most of today on this issue without getting result I am after.
https://github.com/vahidhedayati/micronaut-vuejs-cqrs/blob/master/frontend/src/components/sample/dynamicForm/PropertySplit.vue#L29
https://github.com/vahidhedayati/micronaut-vuejs-cqrs/blob/master/frontend/src/components/sample/dynamicForm2/Property2.vue#L31
I have two similar vue parent pages that load up dynamic form content, what I am trying to do is to get the actual form content which resides on a totally different .vue file in each of the sub folders above.
If I use Vue.component('form-content' this does work but I end up with 1 instance of the form for both different calls rather than a dynamic form per call. So one of the two forms gets loaded in and reused on both calls.
I have tried to use a const variable instead of Vue.component which works correctly locally on the main vue page that loads in the const variable but I haven't been able to get child page to load up the const value
//import Vue from 'vue'
//Vue.component('form-content', {
// components: { ActualForm },
// template: `<actual-form></actual-form>`
//});
//const content = {
// components: { ActualForm },
// template: `<actual-form></actual-form>`
//}
ActualForm.vue is another page and ideally I want to be able to pass this dyanmically per call on different page to the underlying DynamicForm.vue which will load title etc and then the form as per vue page that is passed in.
It seems trivial not quite sure why I am struggling with this.
You cannot pass a component to another component through a prop. For that you need to use a slot. So your code would look like:
<dynamic-form
:headingText="currentHeadingText"
:sectionHeadingText="currentSectionHeadingText"
>
<actual-form/>
</dynamic-form>
And in the <dynamic-form> component you would write:
<template>
[…]
<slot></slot>
[…]
</template
Then the <actual-form> component would be included at the position of the <slot>-tag. You can also use multiple slots and pass in multiple components / template segments. (Read the docs on vuejs.org for detailed information)
I am not completely sure what you are trying to do though. Maybe you need to structure your application in a different way. E.g. use the <dynamic-form> in the template of the <actual-form>. You could then still pass props to <dynamic-form> and fill slots therein from the <actual-form>.
I have webpack setup to bundle all of the source. I have a Vue object that is the page and multiple Vue components of my own creation. This works great!
I am now getting reference data from the database to fill in certain default options for some of these components. In my pages Mounted or Created events (there is no difference for my question) I am calling a method that will check to see if the data exists in localStorage and if not, it will extract the data from the database.
Once Extracted, I have it in localStorage so it is not an issue. However, the first time I need to gather the data (or when I need to refresh it because I have another trigger that lets me know when it has changed) the page and components have rendered (with errors because of lack of data) before the data comes back. The fetch method is in a promise, but mounted events don't seem to care if a promise exists within in before it continues to the next component.
So what is the best practice for loading/refreshing reference data in Vue? I am currently not using VueX because this is not a SPA. Sure, it is a single page that is doing things (there are many single pages that do their own thing in this site) but I have no need to make it a full SPA here. But If VueX and its store will give me some sort of guarantee that it will occur first or page/components will run AFTER VueX things, I will learn it.
Have you tried doing so:
<component v-if="page.isDataLoaded">...</component>
in your Vue-component:
data() {
return {
page: {
isDataLoaded: false,
}
}
},
mounted() {
this.fetchPageData().then(() => this.page.isDataLoaded = true);
}
You can use v-if and v-else to show, for example page loader element like so:
<PageLoader v-if="!page.isDataLoaded"></PageLoader>
<component v-else>...</component>