How to put selected list values inside array in vue - javascript

I have a vue application where I have to select two elements from an list component and then put them inside an array.Right now I have my list and I can select them thanks to vuetify I binded it to an array with v-model I can console log it inside of an array but what I want to achieve is something like this:
{
"meetingName":"",
"meetingUrl":"",
"participants":{
participant1: "Hasan",
participant2: "Turan"
}
}
instead I am getting right now this:
{
"meetingName":"",
"meetingUrl":"",
"participants":[
"Hasan",
"Turan"
]
}
Could someone look at my code and tell me what is wrong with it?
html:
<template>
<v-container>
<v-row>
<v-col cols="4">
<v-card>
<v-list>
<v-list-item-group
v-model="model"
multiple
color="indigo"
v-model="model.participants"
>
<v-list-item
v-for="(item, i) in voterArrayFilteredByTime"
:key="item.voterUniqueName"
:value="item.voterUniqueName"
v-on:click= "generateGroup"
>
<v-list-item-content>
<v-list-item-title v-text="item.voterUniqueName"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-col>
</v-container>
</template>
an here is the method with which I want to console log it but it does not work like I said I am just getting numbers.
<script>
import axios from "axios";
export default {
name: "AddGroupsModal",
data : ()=>({
singleSelect: false,
selection: "",
model:{ meetingName: "", meetingUrl: "", participants: [] },
methods: {
generateGroup(){
console.log(this.model)
}
}
</script>

One of the problems in your markup is it has two bindings to v-model when there should be just one (the last one in this case):
<v-list-item-group
v-model="model"❌
multiple
color="indigo"
v-model="model.participants"❌
>
The v-list components can't create the expected value format, but you can use a computed prop along with Array.prototype.reduce to create an object from the array entries:
export default {
computed: {
computedParticipants() {
return this.model.participants.reduce((obj, name, i) => {
obj[`participant${i + 1}`] = name
return obj
}, {})
// OR:
const obj = {}
this.model.participants.forEach((name, i) => {
obj[`participant${i + 1}`] = name
})
return obj
},
},
}
demo

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.

Pasting text including delimiters into Vuetify combobox does not separate the chips accordingly the delimiters?

I am trying to make a Vuetify combobox with chips splitting what I'm pasting into it according to the delimiters that are defined for that component, for instance ,. Meaning that if I'm pasting the text a,b,c, the component should turn them into 3 different chips: a, b and c but it does not work as such.
Full Vue Source code: https://codesandbox.io/s/chips-so-0gp7g?file=/src/domains/experimental/Experimental.vue
Preview: https://0gp7g.csb.app/experimental
Relevant piece of Vue Source Code:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#update:search-input="meowInput"
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
console.log(e);
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>
Any idea about how can I achieve that behaviour?
You could bind and sync with the change of search-input, the rest is to split the search value and concat to the chips
search-input: Search value. Can be used with .sync modifier.
<v-combobox
// ...
:search-input.sync="search"
// ...
>
// ...
meowInput(e1) {
if (this.search && this.search.split(",").length > 1) {
this.chips = this.chips.concat(
this.search.split(",").filter((term) => !this.chips.includes(term))
);
this.search = "";
}
}
Forked demo

How to filter with a regex what's the user is typing in a Vuetify combobox to create a chip?

I am trying to allow only a certain regex pattern when the user is typing in a comboox to create or add a new chip (basically for example if you want the user to only be able to add phone number chips).
Full Vue Source code: https://codesandbox.io/s/chips-so-0gp7g?file=/src/domains/experimental/Experimental.vue
Preview: https://0gp7g.csb.app/experimental
Relevant piece of Vue Source Code:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#input="meowInput"
#change="meowInput"
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
console.log(e);
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>
How can I achieve that behaviour?
The only way I can see this working is to evaluate the input against a regex (I used US numbers here, but you can use whatever you need) and, if it does not pass the regex test, pop the value out of the chips array.
You can see what I did below. Hopefully this gives you something to go off of:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#change="meowInput" // I changed this to #change so it executes when the user hits enter
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
mask="###"
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
// Test if the input string matches the specified regex pattern
if (!/^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/.test(e)) {
console.log("invalid!");
// Remove from the chips array
console.log(this.chips);
this.chips.pop();
} else {
console.log("phone number");
// Automatically added to the chips array
}
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>

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
})

passing a callback function via props to child component is undefined in child vue

I have a Vue component that passes a callback function to another child component via props. However, it is the only piece that is undefined in the child.
I have created a repo for this so the files can be looked at. In the file brDialog.vue, I am passing button to the function click(), which should have access to the buttons callback that was passed within props from App.vue, however it is undefined within brDialog while the other two things passed with it are present(label and data).
I'll post the brDialog file, and will post the others if needed, but figured it would be easier to link a repo than post all the different files. I'm a bit new to Vue, so possibly something I'm missing in the documentation.
If you run the repo and click the Form Test button in the header, this is where the issue is.
brDialog.vue
<template>
<v-container>
<v-layout row wrap>
<v-flex xs12>
<v-dialog
v-model="show"
width="500"
persistent
>
<v-card>
<v-card-title> {{ title }} </v-card-title>
<slot name="content"></slot>
<v-card-actions>
<v-btn
v-for="button in buttons"
:key="button.label"
small
#click.native="click(button)"
>
{{ button.label }}
</v-btn>
<v-btn
v-if="showCloseButton"
small
#click.native="closeDialog()"
>
{{ closeButtonLabel }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import { props } from './props.js'
export default {
name: 'brForm',
components: {
brTextField: () => import('#/controls/brTextField/brTextField.vue'),
brTextArea: () => import('#/controls/brTextArea/brTextArea.vue'),
brSelectList: () => import('#/controls/brSelectList/brSelectList.vue')
},
props: props,
data () {
return {
}
},
methods: {
async click (button) {
const response = await button.callback(button.data)
if (response.close) {
this.closeDialog()
}
},
closeDialog () {
this.$emit('close')
}
},
computed: {
}
}
</script>
<style>
</style>
Maybe this is something I'm missing with an $emit in Vue or something, but it seems it should be working. Can someone point out why the callback is undefined after being passed to brDialog?
callback is undefined because you define your data property (App.vue from your repo) with an arrow function and loose the Vue context on this:
data: () => {
return {
testingForm: {
//...
dialog: {
props: {
buttonCallback: this.testingFormSave, //<-- here
buttons: [
{
label: 'Save',
data: {},
callback: this.testingFormSave //<-- and here
}
]
}
}
}
}
},
To fix your issue, change data: () => {...} to data () {...}

Categories

Resources