Vue Rendering of component based on VUEX getter - vue.js

I have this code to render my main screen. I want to render the components based on the value I get from vuex store.
All is good. The problem the component does not reload when the mutation happens. Any tips, please?
<template>
<component v-bind:is = "showDashboard" > </component>
</template>
<script>
import AllOrganizationDashboard from "../components/AllOrganizationDashboard"
import OrganizationDashboard from "../components/OrganizationDashboard"
import ProjectDashboard from "../components/ProjectDashboard"
export default {
components: {
AllOrganizationDashboard,OrganizationDashboard,ProjectDashboard,
},
data(){
return {
showDashboard: this.$store.getters.getcurrentDashboardToShow,
}
}
}
I hope there is an easy way and that I do not need to re-structure my componenets.

You have to use computed instead of data, because data is executed only once when the component is created. This should work
computed: {
showDashboard() {
return this.$store.getters.getcurrentDashboardToShow
}
}
More about computed

Related

Isolated stores for each root component

I have a project where i need to use a store to manage a root component with many child components,
now i need to make copies of that root component with different props values, but the state is shared across the entire app, is there a way to scope the store it to root component?
example to illustrate the problem:
App.vue
<script>
import ComponentA from './components/ComponentA.vue'
</script>
<template>
<ComponentA name="comp1"/>
<ComponentA name="comp2"/>
<ComponentA name="comp3"/>
</template>
ComponentA.vue
<script>
import { store } from './store.js'
export default{
props: ["name"],
data(){
return { store }
}
}
</script>
<template>
{{name}}:{{store.count}}
<button #click="store.increment()">increment</button>
</template>
store.js
import { reactive } from 'vue'
export const store = reactive({
count: 0,
increment(){
this.count++
}
})
I don't think that's possible without some sort of refactor.
The global store is helpful if you don't want to deal with prop drilling (and event emitting), but in order to scope it, the component ant its children will need to know which store to use. I think the easiest way to implement that would be to make use of provide/inject. You can use provide/inject to share a store instance
import { reactive } from 'vue'
export function createStoreInstance() {
return reactive({
count: 0,
increment(){
this.count++
}
})
}
then, in your component you would generate the instance during creation
<script>
import { createStoreInstance } from './store.js'
export default {
components: {
GrandChild
},
provide() {
return {
store: this.store
}
},
beforeCreate(){
this.store = createStoreInstance()
},
methods:{
increment(){
this.store.count ++
}
}
}
</script>
and in the child component just get the store
<script>
export default {
inject: [ 'store']
}
</script>
<template>
<p>
{{store}}
</p>
</template>
here is a SFC playground link
You can also use a global store that would accommodate keyed instances, and then use provide inject to pass the key to the children, but I think that would make the store more complicated, but there might be use cases where that may be a better approach (like if you want to be able to save the store state)

Access component parameters inside caller component

I have a component which I call inside another component as:
COMPONENT 1
<template>
<FiltersComponent />
</template>
export default Vue.extend({
components: { FiltersComponent }
)}
So, this FiltersComponents have some parameters I want to access into my component one
COMPONENT 2 DATA
data() {
return {
TestList: [] as string[],
Test2List: null as string[] | null,
}
},
How can I access that TestList and Test2List inside COMPONENT 1?
There are multiple possibilities: If one component is a child component or sibling of the other, you might want to take a loop at props (passing data down) and events (passing data up). Otherwise, if they are not siblings or children, you can use a store like vuex.
To use the docs example:
vue entry point: (e.g., app.js):
import { createApp } from 'vue'
import { createStore } from 'vuex'
// Create a new store instance.
const store = createStore({
state () {
return {
someProperty: 'someValue'
}
},
mutations: {
update (state, value) {
state.someProperty = value
}
}
})
const app = createApp({ /* your root component */ })
// Install the store instance as a plugin
app.use(store)
In your component's script-section:
this.$store.commit('update', 'YOUR_VALUE')
Other component:
const val = this.$store.state.someProperty
However, this is only a very basic example. You should definitely check out the docs, especially the sections about state, getters and mutations.
Always store the state as high in the component tree as you need, meaning that if you need these lists inside component 1, store them there. Then, you can use props and events to access and update the data inside component 2.
Alternatively, you can use a centralized store like Vuex.
You can achieve the same using ref,
Check the below example
<template>
<FiltersComponent ref="childComponent" /> <!-- Adding ref over here -->
</template>
export default Vue.extend({
components: { FiltersComponent }
)}
then in any of your methods or mounted section in Component 1. You can access Component2 data like
this.$refs.childComponent.TestList
or
this.$refs.childComponent.Test2List
So simple isn't it? ;)

Dynamically update props

As simplified below, my app has a template with a custom component.
The data is passed from Template A to custom component as props (":list")
Template A:
<template>
...
<custom-component
v-for="list in listGroup"
:key="list.id_list"
:list="list"
/>
</template>
<script>
export default {
data() {
return {
listGroup: []
};
},
components: {
'custom-component':require("...").default
}
</script>
The custom component
<template>
...
</template>
<script>
export default {
props:["list];
...
}
</script>
Problem to solve:
A new item is added to the list sent as props.
I need the list (:list="list") to be dynamically updated so that the props in the custom component automatically reflect that update.
Thanks.
There are two ways to achieve that one way is to use a state management library(Vuex is recommended) the other is to use events.
Here is an example of using events:
create a file event-bus.js with the following content
import Vue from "vue";
export const EventBus = new Vue();
then in your component where you want to update list use this EventBus.$emit('eventName', data);
remember to import event-bus file
the listen to the event in the other component
EventBus.$on('eventName', function (details) {
//update list here
});

How to use vuex action in function

I'm new to Vue, so it's likely I misunderstand something. I want to call a vuex action inside a local function in App.vue like so:
<template>
<div id="app">
<button #click="runFunction(1)">Test</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default{
data() { return { } },
methods: {
...mapActions(['doAction']),
buttonClicked: (input) => { runFunction(input) }
}
}
function runFunction(input){
doAction({ ID: input });
}
</script>
The action calls a mutation in store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
IDs: []
},
mutations: {
doAction: (state, id) => { state.IDs.push(id) }
},
actions: {
doAction: ({ commit }, id) => { commit('doAction', id) }
}
})
I also have a main.js that sets up the vue:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(App)
})
The error I'm getting is:
ReferenceError: doAction is not defined
at runFunction
How can I call the mapped action inside a function? Version is Vue 2.6.10
There are several problems with defining runFunction as a 'local function':
function runFunction(input){
doAction({ ID: input });
}
Firstly, this is just a normal JavaScript function and the usual scoping rules apply. doAction would need to be defined somewhere that this function can see it. There is no magic link between this function and the component defined in App.vue. The function will be accessible to code in the component, such as in buttonClicked, but not the other way around.
The next problem is that it won't be available within your template. When you write runTemplate(1) in your template that's going to be looking for this.runTemplate(1), trying to resolve it on the current instance. Your function isn't on the current instance. Given your template includes #click="runFunction(1)" I'm a little surprised you aren't seeing a console error warning that the click handler is undefined.
mapActions accesses the store by using the reference held in this.$store. That reference is created when you add the store to your new Vue({store}). The store may appear to be available by magic but it's really just this.$store, where this is the current component.
It isn't really clear why you're trying to write this function outside of the component. The simplest solution is to add it to the methods. It'll then be available to the template and you can access doAction as this.doAction.
To keep it as a separate function you'd need to give it some sort of access to the store. Without knowing why you want it to be separate in the first place it's unclear how best to achieve that.
Of course it is not defined outside your instance .... you have to import the exported store from store.js on your function component :
<script>
import { mapActions } from 'vuex'
import store from 'store.js'
export default{
data() { return { } },
methods: {
...mapActions(['doAction']),
buttonClicked: (input) => { runFunction(input) }
}
}
function runFunction(input){
store.commit({ ID: input });
}
</script>

Is there a thing in Vuex that work the same as mapDispatchToProps in Redux?

I was trying to do things like this.updateData() instead of this.$store.dispatch() in a child component, where the updateData is a function inherits from its parent component. Anyone have any idea how to achieve that?
So to update data of parent comp. from child component in Vue we can utilize event bus:
Parent:
<template>
Hi Blake...
<child-component #trigger="updateData()"></child-component>
</template>
<script>
export default {
mounted() {},
methods: {
updateData: function() {
// this will be triggered from child
}
}
}
</script>
Child ( child-component ):
<template>
<button #click="$emit('trigger')">Trigger Parent Function</button>
</template>
<script>
export default {
mounted() {}
}
</script>
Now this only triggers parent function but you can also send data with event that will be received by parent. This is just Vue without Vuex. If I'm wrong and you're not looking for this maybe you want to use Vuex mapActions which can be used to import all Vuex actions you need in your component and use them as this.vuexAction instead of this.$store.dispatch('vuexAction').
I hope I helped. Good luck.