How do you prepopulate a Vue text field from Vuex state? - vue.js

I am trying to get a user's name to be prepopulated in the below text field.
<v-text-field
v-model.trim="name"
>{{ currentName }}</v-text-field>
data: () => ({
name: '',
}),
computed: {
currentName() {
return this.$store.state.name
},
}
I feel like I am missing something very simple, but I have tried so many variations that I am completely stumped. Please and Thanks for your help!

#Terry you got it! Thanks!
<v-text-field
v-model.trim="getName">{{ getName }}</v-text-field>
data: () => ({ name: '' })
computed: {
getName: {
get: function() {
return this.$store.state.name
},
set: function(newValue) {
this.name = newValue
}
}
methods: {
submit() {
if(!this.name) {
this.name = this.$store.state.name
}
this.$store.dispatch('updateMe', {
name: this.name,
});
}
export default new Vuex.Store({
state: {
name: null,
}

Related

vue.js accessing this.$route.params in mounted functions

I'm trying to filter table of movies by director ID. The structure of a movie is:
{
id: 1,
title: "They All Lie",
releaseYear: 1989,
director: {
id: 18,
firstName: "Darci",
lastName: "Overill",
nationality: "China",
birthdate: "07/13/1973",
},
},
I want to filter the table using the $route.params.id. I have the following code:
<script>
import axios from "axios";
export default {
data: function () {
return {
directorId: this.$route.params.id,
director: {},
movies: [],
};
},
mounted: function () {
this.getDirector();
this.getMovies();
},
methods: {
getMovies: function () {
let url = "http://localhost:8080/movies/movies";
axios.get(url).then((response) => {
this.movies = response.data;
});
},
getDirector: function () {
let url = "http://localhost:8080/movies/directors/" + this.directorId;
axios.get(url).then((response) => {
this.director = response.data;
});
},
},
computed: {
filteredMovies: function () {
var v = this.$route.params.id;
alert(v);
return this.movies.filter(movie => movie.director.id === v);
},
}
};
</script>
I'm trying to access this.$route.params.id in the filteredMovies function. It works in the .alert function but I can't get the return this.movies.filter(movie => movie.director.id === v); to work. The filtering doesn't work. Any ideas please?
If you want a more elegant solution for parsing the router param id check tis out:
index.js(router file)
{
path: '/directors/:id',
name: 'Directors',
component: myComponentName,
props: (route) => {
const id = Number.parseInt(route.params.id);
return { id }
}
}
Component.vue
props: {
id: {
required: true,
type: Number,
}
With the above implementation you can remove the parsing in the component and also instead of doing this:
this.$route.params.id;
Now you can do:
this.id
And you have the parsed id with best practises ;)
Cheers

vuetify vee-validate scroll to the first validation error

If the submit button is clicked in a form, it should automatically scroll to the first validation error if an error exists.
I've read that I can use "scrolltoview" for this, but I don't know exactly how.
I have already tried it with a simple ScrollTo (0.0) to simply scroll up in the event of errors and it works perfectly.
However, this is not the solution I would like to have.
< script >
...
let name = 'm-form-user';
export default {
name: name,
mixins: [baseForm],
props: {
name: {
type: String,
default: name
},
title: {
type: String,
default: ''
},
type: {
type: String,
default: 'create',
validator: function(value) {
return ['edit', 'create'].indexOf(value) !== -1
}
},
},
data: () => ({
form: {
firstName: '',
lastName: '',
position: '',
email: '',
mobile: '',
roles: []
}
}),
async created() {
if (!this.isCreationForm && this.$route.params.id) {
if (!this.editingUser.length) {
await this.requestUser({
id: this.$route.params.id
});
}
Object.assign(this.form, this.editingUser);
this.form.roles.pop()
}
},
computed: {
...mapGetters({
getUser: "users/read"
}),
text() {
return {
cancel: this.$t('modules.forms.m-form-user.buttons.cancel'),
submit: this.$t('modules.forms.m-form-user.buttons.submit')
}
},
editingUser() {
return this.getUser(this.$route.params.id)
},
isCreationForm() {
return this.type === 'create'
}
},
methods: {
...mapActions({
requestCreateUser: 'users/create',
requestUpdateUser: 'users/update',
requestUser: 'users/read'
}),
async submit() {
const validAll = await this.$validator.validateAll();
const validIdentify = this.validateIdentify();
if (!validAll || !validIdentify) {
// ScrolltoView
return;
}
try {
this.setOrganizationRelation();
let user = this.isCreationForm ? await this.createUser() : await this.updateUser();
this.notify.success(this.$t(`notifications.account.userManagement.${ this.isCreationForm ? 'created':'edited'}`, {
firstName: user.firstName,
lastName: user.lastName
}))
this.redirect(this.nav.dashboard.account.users.view.name, {
id: user._id
})
} catch (e) {
if (e.response && e.response.status === 400) {
e.response.data.violations.forEach(violation => {
if (violation.propertyPath === 'username') return; //TODO temporary workaround, remove it when we get correct response from server
this.$validator.errors.add({
id: violation.propertyPath,
field: violation.propertyPath,
msg: violation.message
});
const field = this.$validator.fields.find({
name: violation.propertyPath
});
if (!field) {
throw `Field "${violation.propertyPath}" in "${this.$options._componentTag}" component don't have validation on client side!`;
}
field.setFlags({
invalid: true,
valid: false,
validated: true
});
});
} else {
this.notify.processUnhandledError(e);
}
}
},
async createUser() {
return await this.requestCreateUser({ ...this.form,
password: passwordGenerator.generate()
});
},
async updateUser() {
return await this.requestUpdateUser(this.form)
},
cancel() {
this.goBack();
},
validateIdentify() {
if (!this.form.email && !this.form.mobile) {
const fields = (({
email,
mobile
}) => ({
email,
mobile
}))(this.$refs);
Object.keys(fields).forEach((key) => {
let field = this.$validator.fields.find({
name: fields[key].name
});
this.$validator.errors.add({
id: field.id,
field: field.name,
msg: this.$t('modules.forms.m-form-user.sections.contacts.emptyContacts')
});
field.setFlags({
invalid: true,
valid: false,
validated: true
});
this.$refs.emailBlock.open();
this.$refs.mobileBlock.open();
});
return false;
}
return true;
},
setOrganizationRelation() {
const rel = {
organization: this.$user.relationships.organization
};
setRelations(this.form, rel)
}
}
} <
/script>
<m-block-form-fields :required="false">
<template #title>
{{$t('modules.forms.m-form-user.sections.personal.title')}}
</template>
<template>
<v-layout wrap>
<v-flex xs12>
<e-input-user-name v-model="form.firstName" rules="required" required-style/>
</v-flex>
<v-flex xs12>
<e-input-user-surname v-model="form.lastName" rules="required" required-style/>
</v-flex>
<v-flex xs12>
<e-input-user-position-function v-model="form.position"/>
</v-flex>
</v-layout>
</template>
</m-block-form-fields>
Try using document.querySelector to locate the first error message like below.
if (!validAll || !validIdentify) {
const el = document.querySelector(".v-messages.error--text:first-of-type");
el.scrollIntoView();
return;
}
This is based on #Eldar's answer.
Because you're changing the DOM you only want to look for the new element after the DOM has been updated.
I was able to get this to work with nextTick.
if(!validAll || !validIdentify) {
// Run after the next update cycle
this.$nextTick(() => {
const el = this.$el.querySelector(".v-messages.error--text:first-of-type");
this.$vuetify.goTo(el);
return;
});
}
First, put all your fields inside "v-form" tag
Second, give it a ref="form" as in:
<v-form
ref="form"
v-model="valid"
lazy-validation
#submit.prevent="() => {}"
>
... all your fields ...
</v-form>
Finally, for the handler of your submit method, do this as a guard clause:
async submit() {
// if a field is invalid => scroll to it
if (!this.$refs.form.validate()) {
const invalidField = this.$refs.form.$children.find((e) => !e.valid)
if (invalidField)
invalidField.$el.scrollIntoView({
behavior: 'smooth',
block: 'center',
})
return
}
// here, your form is valid and you can continue
}

how to get nested getters in vuex nuxt

i have store/index.js like this
new Vuex.Store({
modules: {
nav: {
namespaced: true,
modules: {
message: {
namespaced: true,
state: {
count: 0,
conversations: [],
},
getters: {
getCount: state => {
return state.count;
},
},
mutations: {
updateCount(state) {
state.count++;
},
},
actions: {},
},
requests: {
namespaced: true,
state: {
friends: [],
},
getters: {
getFriends: state => {
return state.friends;
},
},
mutations: {
pushFriends(state, data) {
state.friends.push(data);
},
},
actions: {
pushFriends(commit, data) {
commit('pushFriends', data);
},
},
},
},
},
},
});
i want to use getters in computed property i have tested like this
computed: {
...mapGetters({
count: 'nav/message/getCount',
}),
},
butt getting error
[vuex] unknown getter: nav/message/getCount
what is am missing here
i also want to make separate folder for every modules like my nav have 3 modules message, requests & notifications
i did try but nuxt blow up my codes
I think your index is wrong, the correct thing is to separate the modules independently, something like this:
in your store/index.js
export const state = () => ({
config: {
apiURL: 'https://meuapp.com'
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is an messagem from index without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.config
}
else
// return value
return {
message: 'this is an message from index test with payload.',
result: state.config, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
here is your store/navi.js
export const state = () => ({
navi: {
options: ['aaa', 'bbb', 'ccc']
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is a messagem from nav store without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.navi
}
else
// return value
return {
message: 'this is an messagem from navi test with payload.',
result: state.navi, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
then in your component you can use as a computed properties:
<template>
<div>
without a paylod from index<br>
<pre v-text="indexTest()" />
with a paylod from index<br>
<pre v-text="indexTest( {name: 'name', other: 'other'})" />
without a paylod from navi<br>
<pre v-text="naviTest()" />
with a paylod from navi<br>
<pre v-text="naviTest( {name: 'name', other: 'other'})" />
access getters from methods<br>
<pre>{{ accessGetters('index') }}</pre>
<pre v-text="accessGetters('navi')" />
<br><br>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters({
indexTest: 'test',
naviTest: 'navi/test'
})
},
methods: {
accessGetters (test) {
if (test && test === 'index' ) {
console.log('test is', test) // eslint-disable-line no-console
return this.indexTest()
}
else if (test && test === 'navi') {
console.log('test is:', test) // eslint-disable-line no-console
return this.naviTest()
}
else {
return 'test is false'
}
}
}
}
</script>
Whenever possible separate your code into smaller parts, one part for each thing. This makes it easier for you to update and keep everything in order.
Hope this helps.
I came here to find a way to access the getters of a module that was nested inside another module in Vue.js and the following solution worked for me:
this.$store.getters['outerModuleName/innerModuleName/nameOfTheGetter']
Maybe this helps someone with a similar problem.

Nuxt asyncData result is undefined if using global mixin head() method

I'm would like to get titles for my pages dynamically in Nuxt.js in one place.
For that I've created a plugin, which creates global mixin which requests title from server for every page. I'm using asyncData for that and put the response into storage, because SSR is important here.
To show the title on the page I'm using Nuxt head() method and store getter, but it always returns undefined.
If I place this getter on every page it works well, but I would like to define it only once in the plugin.
Is that a Nuxt bug or I'm doing something wrong?
Here's the plugin I wrote:
import Vue from 'vue'
import { mapGetters } from "vuex";
Vue.mixin({
async asyncData({ context, route, store, error }) {
const meta = await store.dispatch('pageMeta/setMetaFromServer', { path: route.path })
return {
pageMetaTitle: meta
}
},
...mapGetters('pageMeta', ['getTitle']),
head() {
return {
title: this.getTitle, // undefined
// title: this.pageMetaTitle - still undefined
};
},
})
I would like to set title in plugin correctly, now it's undefined
Update:
Kinda solved it by using getter and head() in global layout:
computed: {
...mapGetters('pageMeta', ['getTitle']),
}
head() {
return {
title: this.getTitle,
};
},
But still is there an option to use it only in the plugin?
Update 2
Here's the code of setMetaFromServer action
import SeoPagesConnector from '../../connectors/seoPages/v1/seoPagesConnector';
const routesMeta = [
{
path: '/private/kredity',
dynamic: true,
data: {
title: 'TEST 1',
}
},
{
path: '/private/kreditnye-karty',
dynamic: false,
data: {
title: 'TEST'
}
}
];
const initialState = () => ({
title: 'Юником 24',
description: '',
h1: '',
h2: '',
h3: '',
h4: '',
h5: '',
h6: '',
content: '',
meta_robots_content: '',
og_description: '',
og_image: '',
og_title: '',
url: '',
url_canonical: '',
});
export default {
state: initialState,
namespaced: true,
getters: {
getTitle: state => state.title,
getDescription: state => state.description,
getH1: state => state.h1,
},
mutations: {
SET_META_FIELDS(state, { data }) {
if (data) {
Object.entries(data).forEach(([key, value]) => {
state[key] = value;
})
}
},
},
actions: {
async setMetaFromServer(info, { path }) {
const routeMeta = routesMeta.find(route => route.path === path);
let dynamicMeta;
if (routeMeta) {
if (!routeMeta.dynamic) {
info.commit('SET_META_FIELDS', routeMeta);
} else {
try {
dynamicMeta = await new SeoPagesConnector(this.$axios).getSeoPage({ path })
info.commit('SET_META_FIELDS', dynamicMeta);
return dynamicMeta && dynamicMeta.data;
} catch (e) {
info.commit('SET_META_FIELDS', routeMeta);
return routeMeta && routeMeta.data;
}
}
} else {
info.commit('SET_META_FIELDS', { data: initialState() });
return { data: initialState() };
}
return false;
},
}
}

returning article by id -- vue router, vuex

I am trying to return article ID and go the detail page for that id. I did something like below. But in the end it's not working... in the console there is an error popping up and pointing that:
api/v1/article[object%20Object]:1 Failed to load resource: the server
responded with a status of 404 (Not Found)
I need some help because I am a bit lost here... What I am missing here? what I do wrong?
Vuex
export const articles = {
state: {
article: {},
},
mutations: {
setArticle(state, article){
state.article = article;
},
},
getters: {
loadArticle(state){
return state.article;
},
},
actions: {
getArticle(id){
axios.get("api/v1/article" + id)
.then(response => {
this.commit('setArticles', response.data);
})
.catch(error => {
console.log(error);
})
},
}
}
Routes
{
path: "detail/:id",
name: "detail",
component: Vue.component("Detail", require("./pages/Detail.vue").default),
meta: {
requiresAuth: true
}
},
Article Component
export default {
components: {
maps,
},
data(){
return {
};
},
created(){
this.$store.dispatch( 'getArticle', {
id: this.$route.params.id
});
},
computed: {
article(){
return this.$store.getters.loadArticle;
}
}
}
Link to the article id
<router-link :to="{ name: 'detail', params: { id: item.id } }">詳細を見る</router-link>
Update
First parameter to store action is the store properties itself. This is the reason you get the store object. You need to receive id or any payload as second parameter.
actions: {
getArticle({ commit }, id){
axios.get("api/v1/article" + id)
.then(response => {
commit('setArticles', response.data);
})
.catch(error => {
console.log(error);
})
},
}
Here you see this
created(){
this.$store.dispatch( 'getArticle', {
id: this.$route.params.id
});
},
You are passing an object as parameter-
{
id: this.$route.params.id
}
You should be doing this instead -
created(){
this.$store.dispatch( 'getArticle', this.$route.params.id);
},