vue, vuex, vue-i18n change language button event - vue.js

Why changing mutation do not update page in new language?
main.js : here I implemented vue-i18n with vue:
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import VueI18n from 'vue-i18n'
import locales from './locales'
import router from './router'
import store from './store'
import App from './App'
Vue.use(VueRouter)
Vue.use(VueResource)
Vue.use(VueI18n, store)
Vue.http.interceptors.push((request, next) => {
console.log('sending request: ', request)
next(response => {
console.log('response: ', response)
})
})
Vue.config.debug = true
Vue.config.lang = 'fa'
Object.keys(locales).forEach(lang => {
Vue.locale(lang, locales[lang])
})
const app = new Vue({
el: '#app',
router,
VueI18n,
store,
render: h => h(App)
})
app.$mount('#app')
App.vue: Then used two buttons to change language:
<template>
<div id="app">
<h2>{{ $t('example', '#store.state.culture') }}</h2>
<p>{{ count }}</p>
<p>
<button #click="increment()">+</button>
<button #click="decrement()">-</button>
</p>
<p>culture: {{ culture }}</p>
<p>
<button #click=' changeCulture("en") '>English</button>
<button #click=' changeCulture("fa") '>پارسی</button>
</p>
<input type="text" v-model="newUserName">
<button #click="handleAddUserButton()">add</button>
<div>
<router-link to="/page1">Go to page1</router-link>
<router-link to="/page2">Go to page2</router-link>
</div>
<transition name="fade" mode="out-in">
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
<img src="./assets/logo.png">
<hello></hello>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
// import App from './main.js'
import Hello from 'components/Hello'
export default {
name: 'app',
components: {
Hello
},
data () {
return {
newUserName: ''
}
},
computed: {
...mapGetters([
'count',
'culture'
])
},
methods: {
...mapActions([
'increment',
'decrement',
'exampleGetFirebaseData',
'examplePostFirebaseData',
'changeCulture'
]),
handleAddUserButton () {
const user = {
name: this.newUserName
}
this.examplePostFirebaseData(user)
.then(resp => {
// console.log('resp: ', resp)
})
.catch(error => {
console.log('catch error: ', error)
})
},
handleError () {
}
},
beforeMount () {
this.exampleGetFirebaseData()
.then(resp => {
// console.log('resp: ', resp)
})
.catch(error => {
this.handleError(error)
// console.log('catch error: ', error)
})
}
}
</script>
sotre > culture.js: Then using store, getters, actions and mutation to change langauge,
const state = {
locales: ['en', 'fa'],
culture: 'en'
}
const getters = {
culture: state => state.culture
}
const actions = {
async changeCulture ({ commit }, playload) {
commit('CHANGE', playload)
}
}
import App from '../../main.js'
const mutations = {
CHANGE (state, payload) {
if (state.locales.indexOf(payload) !== -1) {
state.culture = payload
} else state.culture = 'en'
console.log(App.i18n)
}
}
export default {
state,
getters,
actions,
mutations
}
I checked using vue development tools in chrome and culture is changed but the problem is that title of {{ $t("example")}} do not change as mutation change.
I know doubt something basic is wrong in my code, may you please help.
Many Thanks in advance.

Becasue this is not a valid expression for i18n
<h2>{{ $t('example', '#store.state.culture') }}</h2>
If you want to translate the text en and fa do this
<h2>{{ $t(culture) }}</h2>
OR
<h2>{{ $t(`namespace:${culture}`) }}</h2>
If you want to use namespace

Related

Vuex toggle array item boolean value

For some reason I can not find a working solution to toggle checkbox of an item from Vuex state array. I got it working to a point where I am able to display todo items, but whenever I try to commit and call a toggle mutation, it doesn't work as expected, nothing changes.
If I set done = true, it works, but not toggling it. Any idea?
View:
<template>
<div class="todos">
<ul>
<li v-for="todo in $store.state.todos" :key="todo.id">
<input
type="checkbox"
v-model="todo.done"
#change="$store.commit('toggle', todo.id)"
/>
<h3>{{ todo.title }}</h3>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>
<style></style>
State:
import { createStore } from 'vuex';
import Todo from '#/types/Todo';
import { State } from 'vue';
const store = createStore({
state() {
return {
todos: [] as Todo[],
};
},
mutations: {
add(state: State, todo: Todo) {
state.todos.push(todo);
},
toggle(state: State, todoId: number) {
const index = state.todos.findIndex((todo) => todo.id === todoId);
state.todos[index].done = !state.todos[index].done;
},
},
});
export default store;
Try updating the memory reference of the todos array by cloning the state.todos array and deconstructing the todo object.
state.todos = state.todos.map((todo) => ({...todo, done: todoId === todo.id ? !todo.done : todo.done }));

Cannot read properties of undefined (reading ...) Apollo + GraphQL + Vue 3

I'm having some trouble accessing data from my GraphQL query.
This is the situation.
In my MenuList.vue when I try to access menu.menuItems.nodes I receive the error
Cannot read properties of undefined (reading 'nodes')
But if I try to access menu.menuItems it works...
Have you any idea why this is happening?
main.js
import { createApp, provide, h } from "vue";
import { ApolloClient, HttpLink, InMemoryCache } from "#apollo/client/core";
import { createApolloProvider } from "#vue/apollo-option";
import App from "./App.vue";
const httpLink = new HttpLink({
uri: "http://XXX.XXX.XXX.XXX/graphql",
});
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
connectToDevTools: true,
});
// Create a provider
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
});
const app = createApp({
render: () => h(App),
});
app.use(apolloProvider);
app.mount("#app");
graphql.js
import gql from "graphql-tag";
export const MENU_QUERY = gql`
query MENU_QUERY {
menu(id: "MainMenu", idType: NAME) {
count
id
databaseId
name
slug
menuItems {
nodes {
id
databaseId
title
url
uri
cssClasses
description
label
linkRelationship
target
parentId
}
}
}
}
`;
App.vue
<template>
<div id="app">
<nav class="navbar is-primary" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<menu-list></menu-list>
</div>
</div>
</nav>
<router-view/>
</div>
</template>
<script>
import MenuList from './components/MenuList'
export default {
name: 'App',
components: {
MenuList
}
}
</script>
MenuList.vue
<template>
<div>
MENU LIST
<h4 v-if="loading">Loading...</h4>
{{menu.menuItems.nodes}}
</div>
</template>
<script>
import { MENU_QUERY } from "#/graphql";
export default {
name: "MenuList",
data() {
return {
menu: [],
loading: 0,
};
},
apollo: {
menu: {
query: MENU_QUERY,
},
},
};
</script>
Data returned by GraphQL
{
"__typename":"Menu",
"count":2,
"id":"dGVybToz",
"databaseId":3,
"name":"MainMenu",
"slug":"mainmenu",
"menuItems":{
"__typename":"MenuToMenuItemConnection",
"nodes":[
{
"__typename":"MenuItem",
"id":"cG9zdDo2",
"databaseId":6,
"title":null,
"url":"http://XXX/page-3/",
"uri":"/page-3/",
"cssClasses":[
],
"description":null,
"label":"Page 3",
"linkRelationship":null,
"target":null,
"parentId":null
},
{
"__typename":"MenuItem",
"id":"cG9zdDoxMA==",
"databaseId":10,
"title":null,
"url":"http://XXX/page-2/",
"uri":"/page-2/",
"cssClasses":[
],
"description":null,
"label":"Page 2",
"linkRelationship":null,
"target":null,
"parentId":null
}
]
}
}
try:
//add code
<p v-if="menu && menu.menuItems">
{{menu.menuItems.nodes}}

Vue components not loading

I was given this problematic codebase, where the Vue components aren't loading in.
Vue is mounting, but without any components.
This is a Laravel 5.7 app, using blade templates with some Vue added in.
This is the initial code:
import 'babel-polyfill'
import loadClientScripts from './load-client-scripts'
import 'bootstrap-material-design/js/'
// Vue & axios
import Vue from 'vue'
import { axios } from '../axios-config'
import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm'
import { createLocales } from '../vue-i18n-config'
import Noty from 'noty'
//Components
import signInForm from './components/SignInForm'
import signUpForm from './components/SignUpForm'
import forgotPassForm from './components/ForgotPassForm'
// import RegisterToAgency from './components/RegisterToAgency'
import SendEmailForm from './components/SendEmailForm'
import AgencyServiceCategories from './components/AgencyServiceCategories'
import DropdownWithCheckboxes from './components/DropdownWithCheckboxes'
import LasiCoalitionAgencies from './components/LasiCoalitionAgencies'
import ServiceProviders from "./components/ServiceProviders";
import ServiceProvider from "./components/ServiceProvider";
import vSelect from "vue-select";
window.axios = axios
Vue.component('v-select', vSelect)
// Bootstrap Vue
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n
}).$mount('#app')
}
sign in form component for example:
<template>
<div>
<b-form
id="sign-in-form"
#submit="onSubmit"
>
<div class="form-group">
<b-form-input
id="sgi-email"
v-model="model.email"
required
name="email"
:state="state('email')"
type="email"
:placeholder="$t('validation.attributes.email_address')"
/>
<b-form-feedback>{{ feedback('email') }}</b-form-feedback>
</div>
<div class="form-group mb-3">
<b-form-input
id="sgi-password"
v-model="model.password"
required="required"
name="password"
:state="state('password')"
type="password"
:placeholder="$t('validation.attributes.password')"
/>
<b-form-feedback>{{ feedback('password') }}</b-form-feedback>
</div>
<div class="form-group my-0">
<a
class="text-opacity forgot-pass-link"
href="#"
>
{{ $t('labels.user.password_forgot') }}
</a>
</div>
</b-form>
</div>
</template>
<script>
console.log('IM HIT')
export default {
name: 'SignInForm',
data() {
return {
model: {
email: '',
password: ''
},
validation: {}
}
},
mounted() {
this.test()
},
methods: {
test() {
console.log("test")
},
feedback(name) {
if (this.state(name)) {
return this.validation.errors[name][0]
}
},
state(name) {
return this.validation.errors !== undefined &&
this.validation.errors.hasOwnProperty(name)
? 'invalid'
: null
},
onSubmit(evt) {
evt.preventDefault()
window.axios
.post('/login', this.model)
.then(response => {
location.href = '/app'
})
.catch(e => {
if (e.response.status === 422) {
this.validation = e.response.data
return
}
})
}
}
}
</script>
Any ideas help!
in the example sign in form, the console does output the "Im hit" that I had placed to ensure that things were loaded.
Thanks
Are you at any point rendering anything with that Vue instance?
Try passing a component to its render function like so:
// lets pretend you've imported a component from some file App.vue up here and called the component simply 'App'
// e.g.: const App = require('./App.vue') or import App from './App.vue';
Vue.use(BootstrapVue)
export function createApp() {
const i18n = createLocales(window.locale)
// Components
Vue.component('sign-in-form', signInForm)
Vue.component('sign-up-form', signUpForm)
Vue.component('forgot-pass-form', forgotPassForm)
// Vue.component('register-to-agency', RegisterToAgency)
Vue.component('send-email-form', SendEmailForm)
Vue.component('agency-service-categories', AgencyServiceCategories)
Vue.component('dropdown-with-checkboxes', DropdownWithCheckboxes)
Vue.component('lasi-coalition-agencies', LasiCoalitionAgencies)
Vue.component('service-providers', ServiceProviders)
Vue.component('service-provider', ServiceProvider)
new Vue({
i18n,
render: createElement => createElement(App) // needs a render function
}).$mount('#app')
}

vuex module mode in nuxtjs

I'm trying to implement a todo list using modules mode in the vuex store in nuxtjs but get the error this.$store.todo is undefined and cant find much about this relating to nuxt
Can anyone assist please I have
store index.js
export const state = () => ({
})
export const mutations = {
}
store todo.js
export const state = () => ({
todos: [],
})
export const mutations = {
mutations ...
}
export const actions = {
actions ...
}
export const getters = {
getters ...
}
index.vue page
<template>
<div>
<h2>Todos:</h2>
<p> Count: {{ doneTodosCount }} </p>
<ul v-if="todos.length > 0">
<li v-for="(todo, i) in todos" :key="i">
...
</li>
</ul>
<p v-else>Done!</p>
<div class="add-todo">
<input type="text" v-model="newTodoText">
<button #click="add">Add todo</button>
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'app',
data () {
return {
newTodoText: ""
}
},
created () {
this.$store.todo.dispatch('loadData')
},
computed: {
...mapState(['todos', ]),
...mapGetters([ 'doneTodosCount', 'doneTodos'])
},
methods: {
toggle (todo) {
this.$store.todo.dispatch('toggleTodo', todo)
},
}
}
</script>
From what i read I thought this should work but doesn't
I should add it all works fine if i don't use modules mode and just have a single index.js setup
Many Thanks
You need to call it differently
this.$store.dispatch('todo/toggleTodo', todo)
Also better to call it in fetch method, not created

Error in Unit Test VueJS component with Vuex

Error: undefined is not a constructor (evaluating 'vm.$el.querySelector('h3')')
Follow my code and full code here
// main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import 'babel-polyfill'
require('es6-promise').polyfill()
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
// Home.vue
<template>
<div>
<h3 class="ui center aligned header">
An simple contact list
</h3>
<div class="ui fluid input">
<input type="text" placeholder="Search..." v-model="searchTerm" autofocus autocomplete="false">
</div>
<div class="ui inverted dimmer" :class="{ 'active': isFetchingContacts }">
<div class="ui text loader">Loading contacts</div>
</div>
<transition-group name="custom" mode="out-in"
enter-active-class="animated flipInX"
class="ui middle aligned selection celled relaxed list">
<component class="item"
v-for="contact in contacts" :key="contact.id"
:is="openedId === contact.id ? 'contact-card' : 'contact-item'" :contact=contact>
</component>
</transition-group>
<div class="ui warning message" v-if="contacts.length <= 0">
<div class="header">
No contacts found!
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex'
import _ from 'lodash'
import contactItem from './ContactItem'
import contactCard from './ContactCard'
export default {
name: 'home',
data () {
return { searchTerm: '' }
},
created () { this.fetchContacts() },
watch: {
'$route': 'fetchContacts',
'searchTerm': 'search'
},
computed: {
...mapGetters([ 'contacts' ]),
...mapState({
isFetchingContacts: state => state.isFetchingContacts,
openedId: state => state.openedId
})
},
methods: {
...mapActions([ 'fetchContacts' ]),
search: _.debounce(function () {
this.fetchContacts(this.searchTerm)
}, 900)
},
components: { contactItem, contactCard }
}
</script>
// Home.spec.js
import Vue from 'vue'
import Home from '#/components/Home'
describe('Home.vue', () => {
it('should render title', () => {
const vm = new Vue(Home).$mount()
expect(vm.$el.querySelector('h3'))
.toBe('An simple contact list')
})
})
Console error
Home.vue
✗ should render title
undefined is not a constructor (evaluating 'vm.$el.querySelector('h3')')
webpack:///test/unit/specs/Home.spec.js:7:32 <- index.js:30622:32
ERROR LOG: '[Vue warn]: Error in created hook:
(found in <Root>)'
ERROR LOG: TypeError{stack: 'mappedAction#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:10798:25
boundFn#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:690:16
created#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:31008:23
callHook#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:2786:25
_init#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:4209:13
VueComponent#http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:4373:17
http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644:30627:29
callFnAsync#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4470:25
run#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4420:18
runTest#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4936:13
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:5042:19
next#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4853:16
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4863:11
next#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4787:16
http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:4831:9
timeslice#http://localhost:9876/absolute/Users/ridermansb/Projects/frontend-recruitment-orion/node_modules/mocha/mocha.js?0491afff0b566ea45cd04c9164a355dba705689e:82:27', line: 10798, sourceURL: 'http://localhost:9876/base/index.js?fcbe9e188a70bce472b9278dcad3e9e00645b644'}
ERROR LOG: '[Vue warn]: Error in render function:
you need to include a store, in order to get your HTML rendered.
import Vuex from 'vuex'
const store = new Vuex.Store()
const Component = Vue.extend(Home)
const vm = new Component({store}).$mount()