Vuex state inserts [object Object] into input when binded - vuejs2

The issue is that when I bind :value to an input to Vuex and say #input for a method, Vuex causes the input to automatically become an object. When I state it should be the data of the input object via += that doesn't account for deletes and gets messy.
I'm using a module in my Vuex. Below, first, is my template for registration.
<template>
<div class="register-container container">
<div class="auth-form">
<div class="form container">
<div class="title">Sign up to Site</div>
<div class="form">
<div class="input el-input">
<input type="text" placeholder="Email" :value="registrationEmail" #input="setRegistrationEmail" class="el-input__inner">
</div>
<div class="input el-input">
<input type="text" placeholder="Username" :value="registrationUsername" #input="setRegistrationUsername" class="el-input__inner">
</div>
<div class="input el-input">
<input type="password" placeholder="Password" :value="registrationPassword" #input="setRegistrationPassword" class="el-input__inner">
</div>
<div class="input el-input">
<input type="password" placeholder="Confirm Password" v-model="confirm_password" class="el-input__inner">
</div>
<div class="submit btn-pill"><span class="content" #click="register">Sign Up</span></div>
</div>
Have an account? <span class="blue">Log in!</span>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
data() {
return {
confirm_password: '',
};
},
methods: {
...mapMutations('authentication', [
'setRegistrationEmail',
'setRegistrationPassword',
'setRegistrationUsername',
]),
...mapActions('authentication', [
'register',
]),
},
computed: {
...mapState('authentication', [
'registrationEmail',
'registrationPassword',
'registrationUsername',
]),
},
};
</script>
Here is the mdoule:
import HTTP from '../http';
export default {
namespaced: true,
state: {
registrationEmail: null,
registrationPassword: null,
registrationUsername: null,
},
actions: {
register({ state }) {
return HTTP().post('/api/auth/register', {
email: state.registrationEmail,
username: state.registrationUsername,
password: state.registrationPassword,
});
},
},
mutations: {
setRegistrationEmail(state, email) {
console.log(email);
state.registrationEmail = email;
},
setRegistrationPassword(state, password) {
state.registrationPassword = password;
},
setRegistrationUsername(state, username) {
state.registrationUsername = username;
},
},
};

You need to do something like this, link
Call the setRegistrationEmail from a method instead of directly calling it.
<input type="text" :value="registrationEmail" #change="setEmail" class="el-input__inner">
and inside methods
methods: {
setEmail(e) {
this.setRegistrationEmail(e.target.value)
},
...mapMutations('authentication', [
'setRegistrationEmail',
'setRegistrationPassword',
'setRegistrationUsername',
]),
...mapActions('authentication', [
'register',
]),
},

Related

Vuex-ORM two-way-data binding cannot watch a nested object

this question is related to Two way data binding with Vuex-ORM
i tried using a watch with deep to handle a user form like this.
<template>
<div id="app">
<div style="display: inline-grid">
<label for="text-1">Text-1: </label>
<input name="text-1" type="text" v-model="user.name" />
<label for="text-2">Text-2: </label>
<input name="text-2" type="text" v-model="user.lastName" />
<label for="text-3">Text-3: </label>
<input name="text-3" type="text" v-model="user.birth" />
<label for="text-4">Text-4: </label>
<input name="text-4" type="text" v-model="user.hobby" />
</div>
<div>
<h5>Result</h5>
{{ userFromStore }}
</div>
</div>
</template>
<script>
import { mapGetters, mapMutations, mapActions } from "vuex";
export default {
name: "App",
computed: {
...mapGetters({
userFromStore: "getUserFromStore",
messageFromStore: "getMessage",
}),
user: function () {
return this.userFromStore ?? {}; // basically "User.find(this.userId)" inside store getters
},
},
watch: {
user: {
handler(value) {
console.log('called')
// this.updateUser(value);
},
deep: true,
},
},
methods: {
...mapActions({
fetchUser: "fetchUser",
}),
...mapMutations({
updateUser: "updateUser",
}),
},
created() {
this.fetchUser();
},
};
</script>
problem is my watcher is not watching, no matter what i try. as soon as the data came from Vuex-ORM my component is not able to watch on the getters user
Anyone idea why?
User.find(...) returns a model. The properties of that model are not reactive i.e. you cannot perform two-way data binding on items that are not being tracked. Hence your watcher will not trigger.
My advice would be to push your user data as props to a component that can handle the data programmatically.
Or, by way of example, you can simply handle two-way binding manually:
Vue.use(Vuex)
class User extends VuexORM.Model {
static entity = 'users'
static fields() {
return {
id: this.number(null),
name: this.string(''),
lastName: this.string(''),
birth: this.string(''),
hobby: this.string('')
}
}
}
const db = new VuexORM.Database()
db.register(User)
const store = new Vuex.Store({
plugins: [VuexORM.install(db)]
})
User.insert({
data: {
id: 1,
name: 'John',
lastName: 'Doe',
birth: '12/12/2012',
hobby: 'This, that, the other'
}
})
Vue.component('user-input', {
props: {
value: { type: String, required: true }
},
template: `<input type="text" :value="value" #input="$emit('input', $event.target.value)" placeholder="Enter text here...">`
})
new Vue({
el: '#app',
computed: {
user() {
return User.find(1)
}
},
methods: {
update(prop, value) {
this.user.$update({
[prop]: value
})
}
}
})
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex#3.6.2/dist/vuex.min.js"></script>
<script src="https://unpkg.com/#vuex-orm/core#0.36.4/dist/vuex-orm.global.prod.js"></script>
<div id="app">
<div v-if="user" style="display: inline-grid">
<label for="text-1">Name: </label>
<user-input
id="text-1"
:value="user.name"
#input="update('name', $event)"
></user-input>
<label for="text-2">Last name: </label>
<user-input
id="text-2"
:value="user.lastName"
#input="update('lastName', $event)"
></user-input>
<label for="text-3">D.O.B: </label>
<user-input
id="text-3"
:value="user.birth"
#input="update('birth', $event)"
></user-input>
<label for="text-4">Hobby: </label>
<user-input
id="text-4"
:value="user.hobby"
#input="update('hobby', $event)"
></user-input>
</div>
<pre>User in store: {{ user }}</pre>
</div>

Form data should remain after displaying error

I am new to inertia js and creating a project using Laravel and inertia js, I am facing an issue that form data is erased after any error display. How to prevent that in inertia.
I have already tried preserveState.
Vue code:
<template>
<template #breadcrumb>
<div>
<div>
<button #click="submit">Save</button>
</div>
</div>
</template>
<div>
<div>
<label>Name</label>
<t-input type="text" v-model="form.name" />
<div v-if="errors['name']">{{ errors["name"] }}</div>
</div>
<div>
<label>Comment</label>
<t-input type="text" v-model="form.comment" />
<div v-if="errors['comment']">{{ errors["comment"] }}</div>
</div>
</div>
</template>
<script>
import { Inertia } from '#inertiajs/inertia'
export default {
components: {
Inertia
},
props: {
form: Object,
errors: Object,
},
methods: {
submit() {
Inertia.post(this.route("post.store"), this.form, { preserveState: true });
},
},
};
</script>
Controller code
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string'],
'comment' => ['required', 'string'],
]);
$data = [
'name' => $request->get('name'),
'comment' => $request->get('comment'),
];
Post::create($data);
return redirect(route('post'))->with('success', 'Post created.');
}
Try using the form helper (read up on it here https://inertiajs.com/forms) something like this in your Vue component binding on the form props, not the prop you pass into the vue:
<template>
<template #breadcrumb>
<div>
<div>
<button #click="submit">Save</button>
</div>
</div>
</template>
<div>
<div>
<label>Name</label>
<t-input type="text" v-model="myForm.name" />
<div v-if="myForm.error('name')">{{ myForm.error('name') }}</div>
</div>
<div>
<label>Comment</label>
<t-input type="text" v-model="myForm.comment" />
<div v-if="myForm.error('comment')">{{ myForm.error('comment') }}</div>
</div>
</div>
</template>
<script>
import { Inertia } from '#inertiajs/inertia'
export default {
components: {
Inertia
},
props: {
form: Object,
errors: Object,
},
data() {
return {
myForm: Inertia.form(
{
name: this.form.name,
comment: this.form.comment,
},
}
},
methods: {
submit() {
this.myForm.post(this.route("post.store"));
},
},
};
</script>

vform multiselected dropdown default selected and expected an array error

Can anyone help me I used Vform When I clicked edit button then it's show this error that expect array but I used already array in categories[] and also how to selected default category ?
I used vform and axios. I want to make a post with multiple category it's store successfully but when I click edit button then I get error that it's expected an array but I bind it an array and also how I make default select when it's edit mode ?
<form #submit.prevent="editMode ? updateVal() : createVal()">
<div class="modal-body">
<div class="form-group">
<label>Category</label>
<select class="form-control"
multiple
v-model="form.categories"
name="categories[]"
:class="{ 'is-invalid': form.errors.has('categories') }">
<option v-for="catetoryValue in getCategory" :key="catetoryValue.id" :value="catetoryValue.id">{{ catetoryValue.name }}</option>
</select>
<has-error :form="form" field="categories"></has-error>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button v-show="editMode" type="submit" class="btn btn-primary">Update</button>
<button v-show="!editMode" type="submit" class="btn btn-primary">Create</button>
</div>
</form>
<script>
export default {
data() {
return {
editMode: false,
categories:[],
selected: null,
form: new Form({
id: "",
title: "",
categories:[],
})
};
},
methods: {
getCategory(){
if (this.$gate.isAdminOrAuthor()) {
axios
.get("api/getcategory")
.then((response) => {
this.getCategory = response.data
})
.catch(() => {
console.log("Error...");
});
}
},
newModal() {
this.editMode = false;
this.form.reset();
$("#addnewmodal").modal("show");
},
edidModal(value) {
this.editMode = true;
this.form.reset();
$("#addnewmodal").modal("show");
this.form.fill(value);
},
},
created() {
this.getCategory();
},
};
</script>

problems with Vuex data rendering

I'm studying reactivity of vuex using nuxt and module mode of store. The problem is, that despite all data in store is changed by actions => mutations successfully, they do not appear on the page, and shows only empty new element of store array. here are my files:
store>contacts>index.js:
let initialData = [
{
id: 1,
name: 'Michael',
email: 'michael.s#mail.com',
message: 'message from Michael'
},
{
id: 2,
name: 'Mark',
email: 'mark.sh#email.com',
message: 'message from Mark'
},
{
id: 3,
name: 'Valery',
email: 'valery.sh#mail.com',
message: 'message from Valery'
}
]
const state = () =>{
return {
contacts: []
}
}
const getters = {
allContacts (state) {
return state.contacts
}
}
const actions = {
async initializeData({ commit }) {
commit('setData', initialData)
},
addNewContact({ commit, state }, newContact) {
commit('addContact', newContact)
}
}
const mutations = {
setData: (state, contacts) => (state.contacts = contacts),
addContact: (state, newContact) => state.contacts.push(newContact)
}
export default { state, getters, mutations, actions}
component itself:
<template>
<div class="contact-form">
<div class="links">
<nuxt-link to="/">home</nuxt-link>
<nuxt-link to="/contact-form">contact form</nuxt-link>
</div>
<h1>leave your contacts and message here:</h1>
<div class="input-wrapper">
<form class="feedback-form" action="">
<div class="name">
<label for="recipient-name" class="col-form-label">Ваше имя:</label>
<input type="text" id="recipient-name" v-model="obj.userName" name="name" class="form-control" placeholder="Представьтесь, пожалуйста">
</div>
<div class="form-group">
<label for="recipient-mail" class="col-form-label">Ваш email:</label>
<input type="email" v-model="obj.userEmail" name="email" id="recipient-mail" class="form-control" placeholder="example#mail.ru">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Сообщение:</label>
<textarea name="message" v-model="obj.userMessage" id="message-text" class="form-control"></textarea>
</div>
<button #click.prevent="addToStore()" type="submit">submit</button>
</form>
</div>
<h3>list of contacts</h3>
<div class="contacts-list">
<div class="list-element" v-for="contact in allContacts" :key="contact.id">
id: {{contact.id}} <br> name: {{contact.name}}<br/> email: {{contact.email}}<br/> message: {{contact.message}}
</div>
</div>
</div>
</template>
<script>
import { mapMutations, mapGetters, mapActions } from 'vuex'
export default {
data() {
return {
obj: {
userName: '',
userEmail: '',
userMessage: ''
}
}
},
mounted() {
console.log(this.showGetters)
},
created() {
this.initializeData()
},
methods: {
...mapActions({
initializeData: 'contacts/initializeData',
addNewContact: 'contacts/addNewContact'
}),
addToStore() {
this.addNewContact(this.obj)
},
},
computed: {
...mapGetters({
allContacts: 'contacts/allContacts',
}),
showGetters () {
return this.allContacts
}
},
}
</script>
so, could anybody help to understand, what is wrong?
You've got mismatched field names.
Inside obj you've called them userName, userEmail and userMessage. For all the other contacts you've called them name, email and message.
You can use different names if you want but somewhere you're going to have to map one onto the other so that they're all the same within the array.
You should be able to confirm this via the Vue Devtools. The first 3 contacts will have different fields from the newly added contact.

Click-to-Edit text field with Vue

I am working on Vue js and having an issue editing a field. When I click on a field to edit it, all the editable fields become active. Here is my code.
export default {
props: ['profileHeight'],
data() {
return {
User: User,
isEditing: false,
form:{
name:'',
email: '',
},
};
},
mounted() {
},
methods: {
activateInEditMode() {
this.isEditing = true
},
deActivateInEditMode() {
this.isEditing = false
}
}
}
<span>Profile settings</span>
<p>Full name<span v-on:click="activateInEditMode" v-show="!isEditing">{{User.state.auth.name}}</span>
<span v-show="isEditing" >
<input v-model="form.name" type="text" class="form-control" >
</span>
</p>
<p>E-mail<span>{{User.state.auth.email}}</span>
<span v-show="isEditing" >
<input v-model="form.email" type="text" class="form-control" >
</span>
</p>
Try using focus and blur methods to show/hide form elements!
Hope this helps!
new Vue({
el: '#app',
data(){
return {
user : {
name: '',
email: ''
},
editField : ''
}
},
methods : {
focusField(name){
this.editField = name;
},
blurField(){
this.editField = '';
},
showField(name){
return (this.user[name] == '' || this.editField == name)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<h1>Profile settings</h1>
<label for="user-name">Full name</label>
<div class="field">
<span class="field-value" v-show="!showField('name')" #click="focusField('name')">{{user.name}}</span>
<input v-model="user.name" v-show="showField('name')" id="user-name" type="text" class="field-value form-control" #focus="focusField('name')" #blur="blurField">
</div>
<label for="user-email">Email address</label>
<div class="field">
<span class="field-value" v-show="!showField('email')" #click="focusField('email')">{{user.email}}</span>
<input v-model="user.email" v-show="showField('email')" type="email" class="field-value form-control" #focus="focusField('email')" #blur="blurField">
</div>
</div>
There are are dozens of ways to do this. I might recommend a component.
console.clear()
Vue.component("editable",{
props:["label", "value"],
template:`
<p>
{{label}}
<span #click="editing=true" v-show="!editing">
{{value}}
</span>
<span v-show="editing" >
<input :value="value"
#input="$emit('input', $event.target.value)"
#keydown.enter="editing=false"
type="text"
class="form-control" >
</span>
</p>
`,
data(){
return {
editing: false,
}
}
})
const User = {
name: 'bob',
email: 'bob#example.com'
}
new Vue({
el:"#app",
data() {
return {
form: User
};
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<span>Profile settings</span>
<editable label="Full name" v-model="form.name"></editable>
<editable label="E-mail" v-model="form.email"></editable>
<br>
{{form}}
</div>
I have written a component for this, I call it Click-to-Edit.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue:
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>