Add new properties to an object used in v-for - javascript

I want to add data (from two v-text-fields) in my product component to my data that I pass to the server. So that if the user adds the values '444' and '230' to the v-text-fields, the entry would be:
{
"444": 230
}
So far, I have hard coded the '444' and managed to get the value '230' passed. But how do i pass both '444' and '230' according to user inputs from a v-text-field?
product.vue
<v-content>
<v-container>
<code>formData:</code> {{ formData }}<br />
<v-btn color="primary" #click="create">Save</v-btn>
(Check console)<br />
<v-row>
<v-col>
<v-text-field v-for="(value, key) in formData" :key="key"
label="Card Type" v-model="formData[key]"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-content>
data() {
return {
dialog: false,
product: null,
error: null,
formData: {
444: null,
}
}
},
methods: {
async create() {
try {
await ProductService.create(this.formData);
} catch (error) {
this.error = error.response.data.message
}
}
}
What changes would i need to make in my component so that formData is based on user input from another v-text-field?

I remember your previous question. To add a new item to that basic formData hash, you would do something like this:
<div>
<v-text-field label="Product #" v-model="newKey"></v-text-field>
<v-text-field label="Card Type" v-model="newValue"></v-text-field>
<v-btn #click="$set(formData, newKey, newValue)">+</v-btn>
</div>
This uses $set to add new properties to formData.

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>

update google chart with selected period parameter from v-select

I am looking for a way to update the google bar type chart display
by selecting the period value from v-select.
so far, Initially, my script loaded, call 'async created()' and
get Data from REST API to MongoDB call module and store returned data in posts[].
and then finally save it to chart data array.
The data format is like this
Key
Type
Date (YYYY-MM-DD)
String
success_count
int
failure_count
int
what I wanna do is, each v-select values consist with {name, id} pair.
Is there any way to pass selected 'selectedItem.id' to
script's created(parameter) method or PostService.get_dashboard_Posts(parameter)'s parameter position?
without refreshing the whole page?
below is my CSS code
<template>
<v-app
:style="{ background: $vuetify.theme.themes.light.background }"
style="max-height:100vw;"
>
<v-container style="min-height:100%;">
<v-layout row class="ma-4">
<v-app style="background-color:grey lighten-1:" class="rounded">
<v-row>
<v-col cols="12" md="12" class="ligne">
<v-container>
<v-row>
<v-col cols="12" md="12">
<h1 class="heading mb-2 grey--text">Dashboard </h1>
<v-card>
<template>
<p class="error" v-if="error">{{error}}</p>
<div class = "posts-container">
<div class="post"
v-for="(post,index) in posts"
v-bind:item="post"
v-bind:index="index"
v-bind:key="post._id"
>
</div>
</div>
<div id="app">
<v-col cols="12" md="12">
<v-row>
<v-col cols="12" md="9"><h3 class="subTitle">Number Of Transactions</h3></v-col>
<v-col cols="12" md="3">
<v-select
:items="comboItem"
v-model="selectedItem"
item-text="name"
item-value="id"
label="Period"
single-line
dense
v-on:change=""
return-object>
</v-select>
</v-col>
</v-row>
<GChart
type="ColumnChart"
:data="chartData"
:options="chartOptions"
/>
</v-col>
</div>
</template>
</v-card>
</v-col>
</v-row>
</v-container>
</v-col>
</v-row>
</v-app>
</v-layout>
</v-container>
below is my script code
<script>
// # is an alias to /src
import PostService from "../PostService";
export default {
name: "PostComponent",
data: () => ({
posts:[],
comboItem:[
{
name: 'today',
id: 1
},
{
name: 'last week',
id: 2
},
{
name: 'last month',
id: 3
},
{
name: 'last year',
id: 4
},
],
chartData: [
["date", "success", "failure"]
],
chartOptions: {
chart: {
title: "Company performance",
subtitle: "Sales,Expences,and Profit:2016-2019"
},
hAxis: {
title: "date",
textStyle: {
fontSize:9
}
},
vAxis: {
title: "frequency",
textStyle: {
fontSize:15
}
},
legend: {
position : 'top',
alignment:'center'
}
}
}),
async created() {
try {
this.posts = await PostService.get_dashboard_Posts();
for(var i=0;i<Object.keys(this.posts[0]).length;i++){
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count])
}
} catch (err) {
this.error = err.message;
}
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods:{
}
};
</script>
Yes that is possible,
Basically we need to be able to respond to changes to selectedItem. All the loading currently happens inside the created function, which we cannot run again, so step 1 would be to move or copy the body of the created function to a new method, which we may call load or something. Now we can respond to changed to selectedItem by calling load.
async created() {
await this.load();
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods: {
async load() {
try {
this.posts = await PostService.get_dashboard_Posts();
for (var i = 0; i < Object.keys(this.posts[0]).length; i++) {
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count
])
}
} catch (err) {
this.error = err.message;
}
}
}
There are a few ways we can respond to changes, i'll show you a few:
We can define this behavior on the input element directly in the template:
<v-select
v-model="selectedItem"
...
v-on:change="load(selectedItem.id)"
...
>
</v-select>
Another way to tell vue to respond to changes is by using watchers:
...
async created() {
await this.load();
},
watch: {
selectedItem(newValue) {
this.load(newValue.id);
// At the moment the watcher runs this.selectedItem is already
// the newValue (this.selectedItem === newValue) so we could also
// do this:
this.load(this.selectedItem.id);
}
},
components: {},
...
The last thing that I haven't covered and leave up to you is to supply the selectedItem.id to PostService.get_dashboard_Posts

How to create array of strings in VueJS?

I am using VueJS and Vuetify to create a modal that can accept some strings in the text field. Now what i want to do is to push the input string inside an array on click. So let's say if i input something and click create the resultant array is ['inputValue1'] but if i add another value by separating with a comma, the resultant array should be ['inputValue1', 'inputValue2'] instead i am getting it as ['inputValue1', 'inputValue1' 'inputValue2']. So the new value should be pushed to the new index instead of adding it with the last value.
Here is a demo
new Vue({
el: "#app",
data() {
return {
dialog: false,
inputValue: "",
stringArray: []
};
},
methods: {
createArray() {
if (this.inputValue !== "") {
this.stringArray.push(this.inputValue);
console.log(this.stringArray);
}
},
closeDialog() {
this.dialog = false;
this.inputValue = "";
this.stringArray = [];
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-layout justify-center>
<v-flex>
<v-btn color="primary" #click="dialog=!dialog"> Click Me </v-btn>
</v-flex>
</v-layout>
<v-dialog v-model="dialog" width=350>
<v-card>
<v-card-title primary-title>
Create Array
</v-card-title>
<v-card-text>
<span class="title">How to create Array of Strings </span>
<v-layout justify-center>
<v-flex xs11>
<v-text-field v-model="inputValue"></v-text-field>
</v-flex>
</v-layout>
</v-card-text>
<v-card-actions class="mt-5">
<v-spacer></v-spacer>
<v-btn #click="closeDialog">CLOSE</v-btn>
<v-btn #click="createArray" :disabled="this.inputValue === ''" color="primary">CREATE</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
</div>
Also on Cancel how can i set the input value and the array to an empty string and an empty array respectively. Thank You. I asked it yesterday but had to delete since i wasn't able to figure out the exact issue.
Your `createArray' method is not attached to any click event. Other than that the code is correct. :)
You should clear the inputValue after the value is pushed to the array like this:
methods: {
createArray() {
if (this.inputValue !== "") {
this.stringArray.push(this.inputValue);
this.inputValue = '';
console.log(this.stringArray);
} else {
console.log('The inputValue is empty')
}
},
closeDialog() {
this.dialog = false;
this.inputValue = "";
this.stringArray = []
}
}
});

How to filter data using boolean(True or False) in VueJS?

I'm creating a button to filter those data(games) that a user bought or have though in my code it only filters data with no boolean attribute set upon them.
Edited: added some more details on the code with firebase data to ref
<!-- This is the button I'm having trouble with -->
<v-tooltip right>
<v-btn small flat color="grey" #click="toggleHave(true)" slot="activator">
<v-icon left small>title</v-icon>
<span class="caption text-lowercase">by All titles bought</span>
</v-btn>
<span>Sort by Game's Title that I have</span>
</v-tooltip>
</v-flex>
<!-- filterGames is for the search method -->
<v-layout row wrap>
<v-card v-for="game in filterGames" :key="game.id" class="ma-2" width="240px">
<router-link :to="{ name: 'view-game', params: { game_id: game.game_id }}">
<v-img :src="game.cover" :alt="game.name" />
</router-link>
<v-card-title>
<div>
<span class="subheading">{{ game.title }}</span><br>
<span class="caption grey--text">{{ game.platform }}</span><br>
</div>
</v-card-title>
</v-card>
</v-layout>
export default {
data() {
return {
games: [], //this is connected to firebase
search: '',
}
},
toggleHave(bought) {
console.log(bought)
this.games = this.games.filter(game => {
return game.have === bought.have
})
},
computed: {
filterGames() {
return this.games.filter((game) => {
return game.title.toLowerCase().match(this.search)
})
}
}
}
// with true
game_id: "006"
title: "Far Cry 4"
have: true // this is the boolean
// with false
game_id: "051"
title: "Assassin's Creed Unity"
have: false // this is the boolean
You're calling toggleHave(true), but in your toggleHave function you use argument as it was an object:
game.have === bought.have
That means you compare game.have with undefined, which explains current behaviour.
Replace bought.have with bought and it should work.
Your question is more of a JavaScript question then a vue question. The filter function returns an array of the elements that meet your condition see here
If you would like to receive Boolean on filtering try using the includes method like here
see this Filter list with Vue.js
just change
return this.games.filter(game => {
return game.have == bought
})

Categories

Resources