My component does not update the loaded property when Store.loaded changes:
Component
import { Vue } from 'vue-property-decorator'
import Component from 'nuxt-class-component'
import { Store } from '../repositories'
#Component
export default class Layout extends Vue {
loaded = Store.loaded
}
Store
class Root {
loaded = false
}
export let Store = new Root()
export default Store
In your example Store is just plain function (class), without any reactivity (no Vue watchers attached for Store.loaded field).
Only properties inside component's data are reactive. If you want reactive single store outside of vue components (better for big frontend applications), you should use Vuex
Simple example will be:
App.vue:
<script>
import { mapGetters, mapMutations } from 'vuex';
import store from './store';
import ChildComponent from './components/ChildComponent.vue';
export default {
store,
components: { ChildComponent },
methods: {
...mapMutations(['toggleLoaded']),
},
computed: {
...mapGetters({
isLoaded: 'isLoaded',
}),
}
}
</script>
<template>
<div id="app">
Toggle loaded
<h3>Root component: </h3>
<div>The loaded flag is: {{ isLoaded }}</div>
<ChildComponent />
</div>
</template>
components/ChildComponent.vue:
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters({
isLoaded: 'isLoaded', //accessing to same data, as root through single Vuex state
}),
}
}
</script>
<template>
<div class="hello">
<h3>Child component</h3>
<div>The loaded flag is: {{ isLoaded }}</div>
</div>
</template>
And reactive Vuex store:
store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
loaded: false
};
const getters = {
isLoaded: state => state.loaded,
};
const mutations = {
toggleLoaded: (state) => {
state.loaded = !state.loaded;
}
};
export default new Vuex.Store({
state,
mutations,
// actions,
getters,
strict: true
});
You can find full source of this example on GitHub.
Related
For a legacy project we need to use vue 2.
However we want to implement state by using #vue/composition-api for vue 2
But my only question is, how to use it with options api?
I have a proof of concept with a .js file
import { reactive } from '#vue/composition-api';
const state = reactive({
counter : 0
})
export default{ state }
For setup it is easy:
<template>
<h1>hi
<div>We still in it: {{ counter }}</div>
<button #click="increment">+</button>
</h1>
</template>
<script>
import { defineComponent, computed } from '#vue/composition-api'
export default defineComponent({
name: "TestStateHello",
setup() {
const store = require("./useState").default;
return {
counter: computed(() => store.state.counter),
increment: () => store.state.counter++,
};
},
})
</script>
But when i want to use regular options api to have access to reactive state of counter i don't seem to know how.
your help will be very grateful!
Just import it globally (outside of the returned options object):
<template>
<h1>hi
<div>We still in it: {{ counter }}</div>
<button #click="increment">+</button>
</h1>
</template>
<script>
import { defineComponent, computed } from '#vue/composition-api'
// Alternative (after fixing export): import {store} from './useState';
// You can use this in setup, too - no need to the require inside the setup()
const store = require("./useState").default;
export default defineComponent({
name: "TestStateHello",
computed: {
counter: () => store.state.counter,
},
methods: {
increment: () => store.state.counter++,
}
})
</script>
I suggest you change the export to:
import { reactive } from '#vue/composition-api';
const state = reactive({
counter : 0
})
export state; // < then import works as above
When dealing with options api you can make use of provide-inject-concept
How it works.
You can provide file of store in main.js like below
import GStore from "./useState"
app.provide('GStore',GStore)
Then in component you can inject that store
export default {
inject:["GStore"]
methods:{
//Code just to show how to access store counter
testingState(){
return this.GStore.state.counter;
}
}
}
I am developing a single-page-application using vue-cli3 and npm.
The problem: Populating a basic integer value (stored in a vuex state) named counter which was incremented/decremented in the backend to the frontend, which displays the new value.
The increment/decrement mutations are working fine on both components (Frontend/Backend), but it seems like the mutations don't work on the same route instance: When incrementing/ decrementing the counter in backend, the value is not updated in the frontend and otherwise.
store.js:
Contains the state which needs to be synced between Backend/Frontend.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter: 10
},
mutations: {
increment (state) {
state.counter++
},
decrement (state) {
state.counter--
}
}
})
index.js:
Defines the routes that the vue-router has to provide.
import Vue from 'vue'
import Router from 'vue-router'
import Frontend from '#/components/Frontend'
import Backend from '#/components/Backend'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Frontend',
component: Frontend
},
{
path: '/backend',
name: 'Backend',
component: Backend
}
],
mode: 'history'
})
main.js:
Inits the Vue instance and provides the global store and router instances.
import Vue from 'vue'
import App from './App'
import router from './router'
import { sync } from 'vuex-router-sync'
import store from './store/store'
Vue.config.productionTip = false
sync(store, router)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Frontend.vue/Backend.vue:
Both (Frontend/Backend) use the same code here.
They use the state counter in order to display and modify it.
<template>
<div> Counter: {{ getCounter }}
<br>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script>
export default {
name: 'Frontend',
methods: {
increment () {
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
},
computed: {
getCounter () {
return this.$store.state.counter
}
}
}
</script>
It would be awesome if someone sould tell me what I am missing or if I have misunderstood the concept of vuex and vue-router.
Just get the counter from the store for both components. You don't need data as store is already reactive.
<template>
<div> Counter: {{ counter }}
<br>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'Frontend',
methods: {
...mapMutations([
'increment',
'decrement',
])
},
computed: {
...mapState({
counter: state => state.counter,
})
}
}
</script>
For reference:
mapState: https://vuex.vuejs.org/guide/state.html#the-mapstate-helper
mapMutations: https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components
#sebikolon component properties that are defined in data () => {} are reactive, methods are not, they are called once. Instead of {{ getCounter }}, just use {{ $store.state.counter }}. OR initiate property in each component that gets the value of your state.
data: function () {
return {
counter: $store.state.counter,
}
}
I am trying to create a small app in which i can click on the button and hide the paragraph but i am trying to implement that using vuex.I have a paragraph in my Home.vue file and a button in my About.vue File. I want the paragraph hide conditionally in the click of the button but i want to accomplish that using vuex. How would i do that? My store.js, home.vue and About.vue are as follows.
This is how my store looks like.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
show:true,
},
mutations: {
toggle : state => {
state.show = !state.show
}
},
actions: {
}
})
This is the Home.vue file
<template>
<p>This needs to disappear</p>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
This is the About.vue file
<template>
<div>
<button #click="toggle">Click Me</button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
the mapMutations should be used in methods not in computed property :
methods:{
...mapMutations ([
"toggle"
])
}
like you see in the official docs :
You can commit mutations in components with this.$store.commit('xxx'), or use the mapMutations helper which maps component methods to store.commit calls (requires root store injection):
I've been playing around with Vue.js for the first time and so far, very cool framework.
One scenario I encountered was trying to pass an object from the parent component to a child, then iterate using v-for directive. I went through the documentation and thought I could pass the object through props but I have a feeling since favoriteObj is a computed data object, it has to do with the rendering sequence as I am getting this in the console:
vue.esm.js?efeb:571 [Vue warn]: Property or method "favoriteObj" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <FavItem> at src/components/FavItem.vue
<FavoriteStuff> at src/components/FavoriteStuff.vue
<App> at src/App.vue
<Root>
Thanks for any info.
Parent:
<template>
<div>
<fav-item :favoriteObj="favoriteObj" ></fav-item>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
import FavItem from '#/components/FavItem'
export default{
name: 'FavoriteStuff',
data (){
return{
uid: localStorage.getItem('_uid'),
}
},
computed: {
...mapGetters([
'favoriteObj'
])
},
created (){
this.$store.dispatch('getFavoriteStuff', {uid: this.uid})
},
components: {FavItem}
}
</script>
Child:
<template>
<div>
<ul class="list-group rpe-list">
<li v-for="item in favoriteObj" :key="item.id" class="list-group-item rpe-item">{{item.fav.name}}</li>
</ul>
</div>
</template>
<script>
export default{
name: 'FavItem',
props: ['favoriteObj']
}
</script>
VUEX Code
actions
export const getFavoriteStuff = ({commit}, {uid}) => {
let favoriteStuff = db.ref( 'stuff-favorites/' + uid );
return favoriteStuff.on('value', function(snapshot){
console.log(snapshot.val)
let favStuff = [];
let idx_counter = 0;
snapshot.forEach(child =>{
idx_counter++;
console.log(child.key)
db.ref('stuff-detail/' + child.key).once('value', function(snapshot){
console.log(snapshot.val())
let obj = {
id: child.key,
recipe: snapshot.val()
}
favStuff.push(obj)
})
})
if (idx_counter === snapshot.numChildren()){
commit('favoriteObj', favStuff)
}
})
}
mutations
export const favoriteObj = (state, payload) => {
state.favoriteObj = payload
}
getter
export const favoriteObj = state => state.favoriteObj;
index
import Vue from 'vue'
import Vuex from 'vuex'
import 'vue-awesome/icons'
import Icon from 'vue-awesome/components/Icon'
import * as getters from './getters'
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)
Vue.component('icon', Icon)
// Define values which will be stored as client-side state
const state = {
favoriteObj: ""
};
// Initialize a store with our getters, actions, mutations, state.
const store = new Vuex.Store({
state,
getters,
actions,
mutations
});
export default store
EDIT:
This was due to a type at the end of my Child component. I simply forgot to close a </script
I am new to Vuex and I am trying a simple test using a getter to return a data property from the state. I'm attempting to display the returned data on the page.
COMPONENT.VUE
<template>
<div class="section" >
<div class="section-content">
<p>{{test.title}}</p>
</div>
</div>
</template>
<script>
export default {
name: 'startNewSearch',
computed: {
getTest() { return this.$store.getters.getTest }
}
}
</script>
GETTERS.JS
export default {
getTest(state) {
return state.test
}
}
STORE.JS
import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
newSearch: {},
test: {
title: 'Test',
msg: 'This is a test message'
}
},
getters,
mutations,
actions
})
The error that I get is that test is- Property or method "test" is not defined on the instance but referenced during render. Is there another step to saving the data to the component that I missed?