After calling add method from vuex second argument doesn't apply - javascript

I create vuex with addNews method Inside of it and pass two arguments, title and body of item. But after calling that method inside my component it only prints my first argument title in HTML. Also i try to console.log body and i get data
This is my method in vuex
async addNews({ commit }, title, body) {
const response = await axios.post(
`https://jsonplaceholder.typicode.com/posts`,
{ title: title, body: body }
);
commit("addNews", response.data);
},
And here is component where I call it
<template>
<div class="text-center">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn class="mb-5" fab dark color="primary" v-bind="attrs" v-on="on">
<v-icon dark> mdi-plus </v-icon>
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 blue lighten-2">
ADD NEW POST
</v-card-title>
<v-text-field v-model="title" required></v-text-field>
<v-text-field v-model="body" required></v-text-field>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
class="mr-4"
#click="
onSubmit();
dialog = false;
"
>
Do It
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "AddNews",
data() {
return {
title: "",
body: "",
dialog: false,
};
},
methods: {
...mapActions(["addNews"]),
onSubmit() {
this.addNews(this.title, this.body);
this.title = "";
this.body = "";
},
},
};
</script>

Try passing an object with arguments to your action:
async addNews({ commit }, { title, body }) {
const response = await axios.post(
`https://jsonplaceholder.typicode.com/posts`,
{ title, body }
);
commit("addNews", response.data);
},

Related

Vue.js & vuex handling SMS by server-side-events

I have a app, which is basically a Call centrum. You can receive calls, call to someone, receive sms and send sms etc.. I have problem with showing my SMS on screen, when I receive event from backend, I am showing that data on screen using vuex and V-for for specific component. Problem is that when I receive another event from backend with different number, I would like to show it under that first sms, but it will overwrite first sms and show only that new sms. I was trying multiple approaches, but nothing worked for me so I hope someone will be able to show me my mistake.
Here is photo of screen with one sms (red box is where second sms should be with own informations like number...)..
Here is code where I receive events.
export default function setupStream(){
let evtSource = new EventSource('/hzs/events.sse');
evtSource.addEventListener('receive_sms', event => {
let sms_data = JSON.parse(event.data);
store.dispatch('receiveSMS', sms_data);
}, false)
}
Here is my vuex code
const state = {
sms: [],
};
const getters = {
getSMS: (state) => state.sms,
};
const actions = {
receiveSMS({ commit }, sms_data) {
commit('setSMS', sms_data);
},
};
const mutations = {
setSMS: (state, sms) => (state.sms = sms),
};
export default {
state,
getters,
actions,
mutations
}
And here is component.
<template>
<v-card>
<v-card-title class="primary white--text">
{{ $t("Communication") }}
</v-card-title>
<v-card d-flex flex-column height="100%" class="card-outter scroll">
<v-col>
<div v-for="sms in getSMS" :key="sms.id">
<v-card-actions>
<v-row>
<v-btn #click="openChat" icon class="mt-4"
><v-img
max-width="30px"
max-height="30px"
class="mt-2"
src="#/assets/icons/icon-sms.svg"
alt="icon-sms"
/></v-btn>
<v-col>
<span>{{sms.date_time}}</span> <br />
<h4>{{sms.sender}}</h4>
<!-- Dialog for Adding new Note -->
<v-dialog
v-model="showEditor"
max-width="400px"
persistent
scrollable
>
<template v-slot:activator="{ on, attrs }">
<v-btn
#click="showEditor = true"
depressed
small
v-bind="attrs"
v-on="on"
>{{$t("Add Note")}}</v-btn
>
</template>
<AddNoteDialog v-on:close-card="showEditor = false"
/></v-dialog>
</v-col>
<v-spacer></v-spacer>
<v-btn class="mt-5" icon #click="deleteCommunication"
><v-img
max-width="20px"
src="#/assets/icons/icon-delete.svg"
alt="icon-delete"
/></v-btn>
</v-row>
</v-card-actions>
<v-divider></v-divider>
</div>
<v-spacer></v-spacer>
<v-divider></v-divider>
<v-card-actions class="card-actions">
<v-row>
<v-text-field
class="ml-4"
color="primary white--text"
required
:label="$t('Mobile number')"
clearable
></v-text-field>
<v-dialog
v-model="showEditor1"
max-width="450px"
persistent
scrollable
>
<template v-slot:activator="{ on, attrs }">
<v-btn
#click="showEditor1 = true"
class="mt-5 mr-4"
depressed
icon
v-bind="attrs"
v-on="on"
><v-icon>mdi-plus-circle</v-icon></v-btn
>
</template>
<AddNummberDialog v-on:close-card="showEditor1 = false"
/></v-dialog>
</v-row>
</v-card-actions>
</v-col>
</v-card>
</v-card>
</template>
<script>
import AddNoteDialog from "#/components/UI/AddNoteDialog";
import AddNummberDialog from "#/components/UI/AddNummberDialog";
import { mapGetters, mapActions } from 'vuex';
export default {
name: "Communication",
data() {
return {
dialog: false,
showEditor: false,
showEditor1: false,
note: '',
chat: this.switchChat,
};
},
computed: {
...mapGetters(['getSMS']),
},
components: { AddNoteDialog, AddNummberDialog },
props: ["switchChat"],
methods: {
...mapActions(['setupEvents']),
openChat() {
this.$emit('openChat')
},
async deleteCommunication() {
alert("Deleted");
},
},
};
</script>
<style>
.scroll {
overflow-y: scroll;
}
.card-outter {
padding-bottom: 50px;
}
.card-actions {
position: absolute;
bottom: 0;
width: 100%;
}
</style>
I think that solution is creating new array, where I will store every single SMS that I receive. Problem is that I don't know how and where to do it.
You already have your vue state array called sms which is a good start. You'll need to update your Vuex to have an additional mutation called "addNewSMS" or something:
const mutations = {
setSMS: (state, sms) => (state.sms = sms),
addNewSMS: (state, newSMS) => state.sms.push(newSMS),
};
This will update your state.sms array to include more than one element, which you should be able to loop through using a v-for loop in your template.
Of course, you'll also need to update your actions like this:
const actions = {
receiveSMS({ commit }, sms_data) {
commit('addNewSMS', sms_data);
},
};
As a sidenote, I'd personally change the sms variable name to messages so its clearer to you and other coders that it contains multiple objects.

TextArea Avoid mutating a prop directly since the value will be overwritten

I know that a similar question has already been dealt with on stackoverflow. But I could not put together a solution from the proposed one. I am very ashamed.
The essence is this: I have a component and another one inside it.
The child component-VClipboardTextField is a ready-made configured text-area. I couldn't get the input out of there and I don't get an error when I try to enter it.
Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or
computed property based on the prop's value. Prop being mutated:
"message"
code tabs-item.vue
<template>
<v-container fluid>
<v-row align="center">
<v-col cols="9">
<v-card flat>
<v-card-text>
<h1>Request</h1>
<v-container>
<v-textarea v-model="message"
placeholder="Placeholder"
label="Request"
auto-grow
clear-icon="mdi-close-circle-outline"
clearable
rows="10"
row-height="5"
#click:clear="clearMessage"
></v-textarea>
<v-textarea v-model="response"
placeholder="Placeholder"
label="Request2"
auto-grow
counter
rows="10"
row-height="5"
color="success"
></v-textarea>
<VClipboardTextField ></VClipboardTextField>
<VClipboardTextField isReadOnly></VClipboardTextField>
</v-container>
<v-row>
<v-btn
dark
color="primary"
elevation="12"
justify="end"
float-right
#click="sendRequest"
>
Send Request
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="3">
<schema-selector #changeSchema="onChangeSchema"></schema-selector>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'tabs-item',
props: ['response'],
data() {
return {
schema: String,
message: '',
}
},
methods: {
sendRequest() {
const message = {
message: this.message,
method: this.schema.state
}
this.$emit('sendRequest', message)
},
clearMessage() {
this.message = ''
},
onChangeSchema(selectS) {
console.log("get schema: ", selectS.state)
this.schema = selectS
}
},
}
</script>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
}
},
methods: {
copyToBuffer() {
console.log("this: ", this)
navigator.clipboard.writeText(this.message);
this.toolTipChange()
setTimeout(() => this.toolTipChange(), 1000)
},
clearMessage() {
this.message = ''
},
toolTipChange() {
if (this.show)
this.show = false
}
}
}
</script>
I will be glad to see an example of the code that will explain how to correctly solve this problem without crutches!
Thanks.
you cannot modify props in components, if the initial value of message is needed as placeholder (meaning the input might not be empty at the begining), you can store the data in message prop to another data variable and use that as v-model to the textarea.
if there is no initial value for message, just use another data variable for textarea and emit an update for message in a watcher.
in code tabs-item.vue
<VClipboardTextField :message.sync="message"></VClipboardTextField>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message_slug"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
message_slug: '',
}
},
watch: {
message_slug(x) {
this.$emit('update:message', x)
}
},
}
</script>
It will bind the value to message_slug and update the message on parent component when it's value changes.
Instead of watching for change every time, you can only emit when there are changes.
In your tabs-item.vue
<VClipboardTextField v-model="message"></VClipboardTextField>
In your VClipboardTextField.vue component, you can receive the v-model input prop as a "VALUE" prop and assign it to a local data property. That way u can manipulate the local property and emit only when in it is changed!
<template>
<v-container>
<v-tooltip bottom v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
v-on="on"
v-bind="attrs"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
#change="$emit('input', v)"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: "VClipboardTextField",
props: {
isReadOnly: { type: Boolean },
value: {
type: String,
},
},
data() {
return {
show: false,
message: this.value,
};
},
};
</script>

Data transfer problem between two siblings components (Vue js)

I have three components and one of those is the parent of the others I'm trying to pass an object called talk between siblings emiting it inside an event from FollowedBrowser to LeftBar and then passing it via prop from LeftBar to TalksList component, after that another event is emited by TalksList and listened one more time for LeftBar and finally this component redefine the talk object as an empty object.
This is my parent component LeftBar.
<template>
<v-navigation-drawer width="25%" permanent clipped app light>
<talks-list v-if="inRoute('messages')" :talk="talk" #talkAdded="talkAdded()"/>
<template v-if="inRoute('messages')" v-slot:prepend>
<followed-browser #newTalk="addTalk($event)"/>
</template>
</v-navigation-drawer>
</template>
<script>
import FollowedBrowser from "./FollowedBrowser";
import TalksList from "./TalksList";
import { mapGetters } from "vuex";
export default {
data(){
return {
talk: {}
}
},
components: {
FollowedBrowser,
TalksList
},
methods: {
addTalk(talk){
this.talk = talk;
},
talkAdded(){
this.talk = {};
}
}
}
</script>
And this is my two children:
TalksList.vue
<template>
<v-container class="my-0 px-5">
<v-list flat>
<v-list-item-group class="my-0">
<div class="ma-0 pa-0" v-for="(talk, index) in talks" :key="index">
<v-divider v-if="talk.divider"></v-divider>
<v-list-item v-else class="px-2" style="cursor: pointer">
<template>
<v-list-item-avatar>
<v-img :src="correctedImageUrl(talk.recipient)"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(talk.recipient) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ talk.recipient.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</template>
</v-list-item>
</div>
</v-list-item-group>
</v-list>
</v-container>
</template>
<script>
import axios from "axios";
export default {
data(){
return {
talks: []
}
},
props: {
talk: {
type: Object,
default: null,
required: true
}
},
watch: {
talk(val){
if(val){
this.talks.splice(0, 1, val);
this.$emit("talkAdded");
}
}
}
}
</script>
FollowedBrowsed.vue
<template>
<div style="display: inline">
<v-dialog scrollable v-model="dialog" max-width="400px" max-height="500px">
<v-card :loading="loading">
<v-text-field dense outlined color="blue lighten-1" label="Nombre de usuario" class="px-5" append-icon="mdi-magnify" v-model="browsedUsername"/>
<v-divider></v-divider>
<v-card-text style="height: 300px;" class="px-2">
<v-list>
<v-list-item class="px-2" style="cursor: pointer" v-for="listUser in filteredFollowed" :key="listUser.id" #click.prevent="newTalk(listUser)">
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(listUser) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ listUser.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import axios from "axios";
export default {
data(){
return {
browsedUsername: "",
loading: false,
dialog: false,
skeleton: true,
followed: []
}
},
watch: {
dialog(dialog){
if(!dialog){
this.browsedUsername = "";
this.item = null;
}
}
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
user: "auth/user"
}),
filteredFollowed(){
return this.followed.filter((user) => {
return user.username.toLowerCase().indexOf(this.browsedUsername.toLowerCase()) !== -1;
})
}
},
mounted(){
axios.get("all_followers_followed/followed")
.then((response) => {
if(response.data){
this.followed = response.data;
this.skeleton = false;
}
})
.catch((error) => {
console.log(error)
});
},
methods: {
async newTalk(user){
this.loading = "blue lighten-1";
await axios.post("messages/new_talk", {recipient_id: user.id})
.then((response) => {
if(response.data){
this.dialog = false;
this.$emit("newTalk", {
messages_number: 0,
recipient: user,
sender: this.user
});
}
})
.catch((error) => {
console.log(error);
});
}
}
}
When the newTalk method is called inside FollowedBrowser component newTalk event is emited but after that my screen freezes like the app was inside infinite loop and I don't know why. I omitted some code that I thought was irrelevant.
Can anybody help me.
Thanks in advance.
I solved... So simple, I just had to get a copy of talk prop inside TalksList, inside watch just put this:
watch: {
talk(val){
if(val){
if(this.talks.length){
this.talks.unshift({ divider: true });
}
let buffer = new Object();
let talk = new Object();
buffer.data = val;
talk = buffer.data;
this.talks.unshift(talk);
}
}
},

Add new reply in list after submit without reload in vue

I am new to Vue and trying to make a blog project.
In above image when i click on reply button a dialog box opens and i submit a reply to the comment.
But the problem here is that After submitting the form and on dialog=false the new submitted reply doesn't show in the list.
my code is
<v-btn
#click="handleReply(comment.id)"
>Reply</v-btn>
<v-dialog v-model="dialog" max-width="400">
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-form ref="replyForm" v-model="isFormValid" lazy-validation>
<v-text-field v-model="editedReply.reply" label="reply" required></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" text #click="close">Cancel</v-btn>
<v-btn
color="primary"
#click.prevent="handleSubmit"
:disabled="!isFormValid || isProcessing"
:loading="isProcessing"
>Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
and
handleReply(id) {
this.dialog = true;
this.editedReply.comment_id = id;
},
handleSubmit() {
this.isProcessing = true;
axios
.post(`/replies`, { reply: this.editedReply })
.then(res => {
this.isProcessing = false;
const newEntry = res.data.reply;
this.replies.push(newEntry);
this.dialog = false;
},
Here this.editedReply presents the state of all params which are
editedReply: {
reply: null,
comment_id: null,
name: JSON.parse(localStorage.getItem("user")).name,
email: JSON.parse(localStorage.getItem("user")).email
}
we are getting comment id from handleReply method.
thanks:)

Vue.js footer component acting on other components?

I'm new to Vue.js and building an app with header / footer (showing on certain pages) and in the footer I have a shopping cart.
Basically on some pages of the app I can add items to the cart, and with the icon in the footer I can acess to the list of items, and delete them if I want to.
My cart showing as a dialog full screen, above my app.
The problem is, if I empty my cart, I want to reload the component behind my shopping cart before closing it (because the step is not the same...), and I don't know how to do it, actually i'm reloading the page if I close the cart while he's empty....
This is my footer part (with the cart) :
var footer = Vue.component("iteck-footer", {
template: `<div>
<v-footer fixed height="auto" color="teal" dark>
<v-layout class="pl-2 pr-2" justify-center row wrap>
<v-dialog fullscreen v-model="dialog" transition="dialog-bottom-transition">
<v-toolbar fixed dense dark color="teal">
<v-spacer></v-spacer>
<v-toolbar-title>Panier</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn icon dark #click="dialog = false">
<v-icon>close</v-icon>
</v-btn>
</v-toolbar-items>
</v-toolbar>
<div class="content-with-header-footer">
<div v-if="articles.length > 0">
<div v-for="article in articles" :key="article.LIG">
<v-checkbox class="inputs-panier pa-0 ma-0 " v-model="article.selected">
<template slot="label">
<div>{{article.DESIG}} | <b>Qté.</b> : {{quantite(article.QTESAI)}}</div>
<div>{{article.CLE}} {{article.CODBAR}}</div>
</template>
</v-checkbox>
</div>
</div>
<div class="text-xs-center mt-4" v-else>
<h2 class="red--text">Panier vide</h2>
</div>
</div>
<v-footer fixed height="auto" color="teal" dark>
<v-btn #click="deleteLignesPanier()" class="mx-0" icon>
<v-icon>delete</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-btn #click="validerPanier()" class="mx-0" icon>
<i class="material-icons">check</i>
</v-btn>
</v-footer>
</v-dialog>
<v-btn style="position:relative;" #click="openMenu()" class="mx-0" icon>
<i class="material-icons">shopping_basket</i>
<span id="nb_articles">
{{articles.length}}
</span>
</v-btn>
<v-spacer></v-spacer>
<v-btn #click="validerPanier()" class="mx-0" icon>
<i class="material-icons">check</i>
</v-btn>
<v-dialog v-model="modal" max-width="290">
<v-card>
<v-card-text>
{{modalMessage}}
</v-card-text>
<v-card-actions>
<v-btn color="green darken-1" flat="flat" #click="modal = false; modalMessage = ''">
OK
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</v-footer>
</div>`,
props: ["menu"],
data() {
return {
modal : false,
modalMessage : "",
entete: null,
articles: [],
dialog: false
}
},
mounted() {
this.getPanier()
},
watch: {
// whenever question changes, this function will run
dialog: function (newValue, oldValue) {
if(newValue === false && this.articles.length === 0)
location.reload();
}
},
methods: {
async openMenu() {
this.articles = [];
this.entete = null;
if(this.dialog === false) {
//Récuperation du panier en cours.
this.getPanier();
}
this.dialog = true;
},
async deleteLignesPanier() {
let lignes = "";
for (let i=0; i < this.articles.length; i++ ) {
if(this.articles[i].selected === true) {
if(lignes === "")
lignes = lignes + this.articles[i].LIGNE;
else
lignes = lignes + "," + this.articles[i].LIGNE;
}
}
if(lignes !== "") {
//On envoie la requête au serveur.
let data = await wspda.deleteLignesPanier(this.menu,lignes);
if(data == 'true')
{
console.log('OUI');
console.log('length',this.articles.length);
console.log('articles',this.articles);
for (let i = this.articles.length -1; i >= 0; i-- ) {
if(this.articles[i].selected === true) {
this.articles.splice(i,1);
}
}
}
else
{
console.log(data);
}
}
},
async validerPanier() {
if(this.articles.length > 0) {
let data = await wspda.validerPanier(this.menu,"","False");
this.modal = true;
this.modalMessage = data;
this.articles = [];
}
},
async getPanier() {
let data = await wspda.getPanier(this.menu);
if(data.ProDataSet && data.ProDataSet.PANIERENT && data.ProDataSet.PANIERLIG) {
if(Array.isArray(data.ProDataSet.PANIERLIG)) {
this.articles = data.ProDataSet.PANIERLIG;
} else {
this.articles.push(data.ProDataSet.PANIERLIG);
}
this.entete = data.ProDataSet.PANIERENT;
for (let i=0; i<this.articles; i++ ) {
this.articles[i].selected = false;
}
}
},
quantite(qtesai) {
return (qtesai % 1) === 0 ? Math.round(qtesai) : qtesai;
}
}
});
As you can see, in my watcher, on the closing of the cart, i may reload the page... But there is a way, when the cart is empty (deleting items), reload the current component behind my dialog ?? can't find a proper way to do it.
And my current component running behind (just for an example) :
var spaHome = Vue.component("Home", {
template: `<div>
<v-container fill-height>
<v-layout>
<v-flex>
<v-list>
<v-list-tile class="menu-home"
v-for="(item,index) in menus" :key="index" #click="$router.push(item.path)" v-if="item.value">
<!--<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>-->
<v-list-tile-content class="teal darken-3 white--text">
<v-list-tile-title class="text-xs-center">{{ item.label }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-flex>
</v-layout>
</v-container>
</div>`,
props: ["title"],
$_veeValidate: {
validator: "new"
},
data() {
return {
menus: conf.Menus,
}
},
methods: {
}
});
By the way, I'm not using node.js etc... Because I can't run the app on a node server, so I can't use "import" , "export"...

Categories

Resources