Vuejs : v-model with multiple checkbox in v-for - javascript

I have some questions about using v-model with "complex" object. Here's my code:
<v-list v-for="(facet, facetName) in getFacets" :key="facetName">
<v-list-tile-content v-for='valueFacet in facet" :key="valueFacet.key">
<v-checkbox v-model="selectedFacets[facetName]" :value="valueFacet.key"></v-checkbox>
</v-list-tile-content>
</v-list>
And here's my data / init:
data() {
return {
selectedFacets: {}
}
},
created: function() {
// I initialize my object with all my facets here
config.facets.forEarch(facet => {
this.selectedFacets[facet] = [];
});
}
Some explanation: each facet has multiple checkbox representing values. We can select multiple values. My object has to be like this :
selectedFacets: { facet1 : [value1, value2], facet2: [value12, value 13]
With this code, each time I select one value, it will remove the previous one.
I tried to initalize my object in my data like this without using created function:
data() {
return {
selectedFacets: { "facetName" : [], "facetName2" : []}
}
}
and it works fine. All my values for each facet are added in the right array. But I need to initialize my object and facet names with my conf and if I don't initialize my object in my data, it does not work. I tried with computed / created, by getting my object from my store, but it keeps adding value by removing the previous one.
Any idea about what I do wrong ? Thanks.

Just initialize your object with Vue.set:
created: function() {
config.facets.forEach(facet => {
Vue.set(this.selectedFacets, facet, []);
});
}
When component initializes template it doesn't know about selectedFacets[facetName] so to make it reactive and correct working with v-model you should use Vue.set mutator.

Related

How to fetch data in vueJS?

a column data is showing in array : ["1k","2k","3k"]
I used foreach to filter and the data type is showing string and it shows like this again: ["1k","2k","3k"]
If I convert this into an array then v-for loop does not show data.
photographers data is the API action state.
Output: I want to print each value in dropdown from this array.
I am trying to do v-for on print.
I tried to do print as a computed function but no result.
If you want all code then I can print its too much logic that's why I have trimmed it which is required.
Thanks
code:
data() {
return {
print: [],
printPrice: [],
},
computed: {
getImageDetails(){
this.photographersdata.forEach((details) => {
if(details.id === parseInt(this.imageId)){
this.print = details.print;
this.printPrice = details.print_price;
}
})
},```

Selecting specific object inside array of objects throwing undefined in Vuejs /Java app

In this app im building i have a little problem referring to adress the SPA to the selected item by its id.
Fetching my data to my end point(action) i got a array of objects , then through a getter i just grab all that data, and using the function find() i filter all the array throwing the object which contains the id referrig to the selected one.Lets say:
First- Exposing on the router the link and the condition of the id which demark the object to select
{
path: '/game/selected/:gameCrabId',
name: 'GameSelectedView',
props:true,
component: GameSelectedView,
},
Second- Eexposing the component where in i access through router link the supposed object which contains the requested id
<v-btn router-link :to="`/game/selected/${game.game_id}`" >Access</v-btn>
Third-Initializing the view in general of the supposed selected object
<template>
<v-container v-if="allGames">
<!-- <div>{{gameSelected}}</div> -->
</v-container>
</template>
<script>
import { mapActions, mapGetters, mapState } from "vuex";
export default {
name: "GameSelectedview",
props: ["gameCrabId"],
data() {
return {};
},
// props:{SelectedGameComponent:Object},
methods: {
...mapActions(["allGames"])
},
computed: {
...mapGetters(["getSelectedGame"]),
gameSelected() {
return this.getSelectedGame(this.gameCrabId);
}
},
async created() {
await this.allGames;
await this.gameSelected;
await console.log(this.gameSelected);
},
</script>
then on my state managment component (Vuex) , i trigger the method getter which eventually brings me once i click that button of step 2 , the object which has the same id than the required one, but first im exposing the state where in that array of objects is
state: {
allGamesData: {all_games:[
{"game_id": 1,"game_player": "Romi","game_score": 0},
{"game_id": 2,"game_player": "Messi","game_score": 0},
{"game_id": 3,"game_player": "CR7","game_score": 0},
]
},
},
getters:{
getSelectedGame: (state) => (gameCrabId) => {
console.log(gameCrabId);
return state.allGamesData.all_games.find((gameID) => {
gameID.game_id === gameCrabId;
});
},
}
And this is the getter which eventuallly accesses the state and that array of objects , and using a double arrow function first aims to the state ,and second through the parameter (gameCrabId)which is the once that expose the id so neccesary to complete the link to that selected item;then returning the access to that array of objects in my state , i just use the function find() to establish a comparison between that id carried by the parameter (gameCrabId) and the array of objects brought from the state , breaking the cycle once find the first comparable item in the objects which match with that id gameID.game_id === gameCrabId
In order to see if ,my id carrier works i set a log , and works , the number is brought , but for any reason the filter find() throws me undefined in the object selected despite of already have the id once the comparison get settled, being imposible retrieve any information of that object
Missing return from find()
The callback to Array.prototype.find() must return a Boolean, but currently it returns nothing, so the result of find() would always be null:
return state.allGamesData.all_games.find((gameID) => {
gameID.game_id === gameCrabId; // FIXME: No return statement
});
Mismatched types
game_id is a Number, but gameCrabId is a string, and getSelectedGame() uses === to compare the two, which requires the operands to have the same type. One solution is to perform explicit type conversion to ensure both operands are Number:
getters: {
getSelectedGame: state => gameCrabId => {
return state.allGamesData.all_games.find((gameID) => {
// return gameID.game_id === gameCrabId; // DON'T DO THIS
return gameID.game_id === Number(gameCrabId);
});
}
}
demo

Watch elements of simple array

Vue dont see changes in simple array items.
I am learning Vue.js and have problem with watcher.
Namely i am trying to watch changes in array, and change one data value.
Every time i add a new item and change or delete an existing item, I want to change the value.
data() {
return {
change: false,
array: ['one','two','three','four']
}
},
watch:{
array:{
deep:true,
handler(){
this.change = true;
}
}
}
Vue just see when array length is changed but not particular element.
You can watch nested values in an object as shown in the docs use a dot-like-string notation.
var vm = new Vue({
data: {
e: {
f: {
g: 5
}
}
},
watch: {
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
To my knowledge you CANNOT do this with arrays (i.e. array[0]) because the reference my shift or be removed. I think the best way to do what you want is to compare the newValue and oldValue in the watcher handler function if the whole array changes.
// from vuejs docs
watch: {
// whenever question changes, this function will run
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},

Vue.Js how to get value of prop attribute with id

I've been stuck on this for a while now and i'm not sure what to Google to find a solution and through the vuejs docs but can't find anything usefull.
Basicly I have a app with a few props. One of them is 'soundpacks'. This prop is setup this way in my app.js:
soundpacks: [
{
title: 'Techno soundpack',
tempo: 130,
genre_id: 1,
teacher_id: 1
},
{
title: 'Deep House soundpack',
tempo: 123,
genre_id: 2,
teacher_id: 2
}
]
As you can see I have declared a genre_id in here. I have another prop declared like this:
genres: [
{
id: 1,
label: 'Techno'
},
{
id: 2,
label: 'Deep House'
}
]
My problem is as follows. On my front-page I want to display all the soundpacks in a div which I do with a v-for. This is working but in this div I want to display the name of the genre that is linked to the soundpack through the genre_id. I have no clue on how to get this label value to display in this v-for and i'm hoping anybody can give me a push in the good direction. I'm new to Vue.Js thats why I'm asking what looks to be a very simple problem.
Any help is appreciated.
Thank you!
I just wrote a computed property that you can add to your component.
Will compute a new list that you can directly use to display in your app.
computed: {
computeListData(){
let newList = []
this.soundpacks.forEach((item) => {
let data = this.genres.filter((genre) => {
return (genre.id === item.genre_id)
})
let listData = item
listData.label = data[0].label
newList.push(listData)
})
return newList
}
}
Here's a gist link that you can refer to:
https://gist.github.com/ayushgupta11/7eeb1a4fdfeadab1a94a1e592ecd53dd
Please tell if this worked for you.
Lots of options here.
Just using a template you can achieve this using nested loops:
<div v-for="pack in soundpacks">
{{ pack.title }} -
<template v-for="genre in genres">
<template v-if="genre.id === pack.genre_id">
{{ genre.label }}
</template>
</template>
</div>
Alternatively, you can use a computed property to add the genre label to your soundpack data:
computed: {
extendedSoundpacks () {
return this.soundpacks.map(pack => {
const genre = this.genres.find(genre => genre.id === pack.genre_id)
return {
// Copy the original object to avoid mutating it.
// Alternatively you could just nest the object by
// removing the three dots, though this would be at
// the expense of a more complicated template.
...pack,
label: genre.label
}
})
}
}
While this is slightly better than the loop in the template it still uses nested looping. Better would be to prepare a map to do a quick lookup of the label from the id:
computed: {
genreMap () {
const genreMap = {}
this.genres.forEach(genre => {
// If genres have more than just a label you could store
// the whole object instead of just the label
genreMap[genre.id] = genre.label
})
return genreMap
}
}
Now we've got genreMap, the find in my earlier extendedSoundpacks example becomes just:
const genre = this.genreMap[pack.genre_id]
Whilst the code isn't much shorter it does avoid the nested looping, which would be much faster if the arrays are large enough.
However, once you have genreMap it becomes pretty trivial to do this in the template without creating a new array of soundpacks:
{{ genreMap[pack.genre_id] }}
In this case that would be sufficient but if the logic were more complicated than a simple id/label lookup you could use a method...
{{ getGenreLabel(pack.genre_id) }}
... or a filter:
{{ pack.genre_id | genreLabel }}
These would need to be defined as functions in the methods or filters sections of your component respectively. If you needed to use this logic across multiple components you might consider registering the filter globally.
<li v-for="soundpack in soundpacks" :key="soundpack.teacher_id">
{{genres[soundpack.genre_id - 1].label}}
</li>
Note that the -1 is because you started your indexing at 1 while the index of an array starts at 0, also note that my code only works if you continue a constant numbering of your genres.

Vue JavaScript create variable from an other variable value

I can't find how to create a variable from another variable value.
I get this variable from AJAX call (using vue-resources):
langs: {1:'fr', 2:'en', 3:'ar'},
But, I don't know how many properties langs will have.
How can I get this variable dynamically:
newVar: {
has_lang: '1',
values: [{ is: 'fr', val:''}, { is: 'en', val:''}, { is: 'ar', val:''}]
},
So in newVar, the fr, en, and ar properties have been generated automatically using the values in langs.
I am planning to use the values array to bind to the values of my md-input inputs using v-model:
<md-input-container v-for="value in newVar.values">
<label>#{{ attribute.attribute }} #{{ value.is }}</label>
<md-input v-model="value.val"></md-input>
</md-input-container>
Is doing this manipulation the best practice? Because I can't find a solution which uses v-model for inputs that are generated from a v-for loop.
Use a watcher to set the newVar property whenever the langs property changes. You'll need to also do this during the mounted lifecycle event if you are passing in the langs variable as a prop or defining it within data().
In your case, I'd add a method to call at both points:
data() {
return {
langs: {1:'fr', 2:'en', 3:'ar'},
newVar: {},
};
},
methods: {
updateValues() {
let newVar = {
has_lang: (this.langs !== {}) ? '1' : '0',
values: [],
};
for (let lang in this.langs) {
newVar.values.push({ is: this.langs[lang], val: '' });
}
this.newVar = newVar;
}
},
watch: {
langs() {
this.updateValues();
}
},
mounted() {
this.updateValues();
}
Here's a CodePen.

Categories

Resources